diff --git a/MAINTAINERS b/MAINTAINERS index 63e9ba521bcc5..4057319003185 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1724,6 +1724,15 @@ F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h F: tests/functional/test_riscv64_sifive_u.py +Tenstorrent Machines +M: Joel Stanley +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/test_riscv64_tt_*.py + AMD Microblaze-V Generic Board M: Sai Pavan Boddu S: Maintained @@ -2666,6 +2675,12 @@ S: Orphaned F: hw/gpio/pcf8574.c F: include/gpio/pcf8574.h +DesignWare I2C +M: Chris Rauer +S: Maintained +F: hw/i2c/designware_i2c.c +F: include/hw/i2c/designware_i2c.h + Generic Loader M: Alistair Francis S: Maintained diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst new file mode 100644 index 0000000000000..640cabf7b046b --- /dev/null +++ b/docs/system/riscv/tt_atlantis.rst @@ -0,0 +1,38 @@ +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. + +Features +-------- + +* 8-core Ascalon-X CPU Cluster +* Dual x32 LPDDR5 @ 6400 MT/s +* RISC-V compliant Advanced Interrupt Architecture +* PCIe Gen4 +* RISC-V compliant IOMMU +* GPU and Video subsystem +* 2x USB3.1 & 2x USB2.0 +* 2x 1GbE Ethernet +* 2x eMMC5.1/SDIO3.0 storage +* Extensive connectivity (SPI, I2C, UART, GPIO, CANFD) + +Note: the QEMU tt-atlantis machine does not model the platform +exactly or all devices, but it is undergoing improvement. + +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 89b2cb732c206..b745b63cf1c7a 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -70,6 +70,7 @@ undocumented; you can get a complete list by running riscv/microchip-icicle-kit 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 596a7a3165add..d3f394edeb9cf 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -18,6 +18,10 @@ config ARM_SBCON_I2C bool select BITBANG_I2C +config DESIGNWARE_I2C + bool + 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 0000000000000..2e808f61f0501 --- /dev/null +++ b/hw/i2c/designware_i2c.c @@ -0,0 +1,813 @@ +/* + * 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/bitops.h" +#include "qemu/guest-random.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" + +enum DesignWareI2CRegister { + DW_IC_CON = 0x00, + DW_IC_TAR = 0x04, + DW_IC_SAR = 0x08, + DW_IC_DATA_CMD = 0x10, + DW_IC_SS_SCL_HCNT = 0x14, + DW_IC_SS_SCL_LCNT = 0x18, + DW_IC_FS_SCL_HCNT = 0x1c, + DW_IC_FS_SCL_LCNT = 0x20, + DW_IC_INTR_STAT = 0x2c, + DW_IC_INTR_MASK = 0x30, + DW_IC_RAW_INTR_STAT = 0x34, + DW_IC_RX_TL = 0x38, + DW_IC_TX_TL = 0x3c, + DW_IC_CLR_INTR = 0x40, + DW_IC_CLR_RX_UNDER = 0x44, + DW_IC_CLR_RX_OVER = 0x48, + DW_IC_CLR_TX_OVER = 0x4c, + DW_IC_CLR_RD_REQ = 0x50, + DW_IC_CLR_TX_ABRT = 0x54, + DW_IC_CLR_RX_DONE = 0x58, + DW_IC_CLR_ACTIVITY = 0x5c, + DW_IC_CLR_STOP_DET = 0x60, + DW_IC_CLR_START_DET = 0x64, + DW_IC_CLR_GEN_CALL = 0x68, + DW_IC_ENABLE = 0x6c, + DW_IC_STATUS = 0x70, + DW_IC_TXFLR = 0x74, + DW_IC_RXFLR = 0x78, + DW_IC_SDA_HOLD = 0x7c, + DW_IC_TX_ABRT_SOURCE = 0x80, + DW_IC_SLV_DATA_NACK_ONLY = 0x84, + DW_IC_DMA_CR = 0x88, + DW_IC_DMA_TDLR = 0x8c, + DW_IC_DMA_RDLR = 0x90, + DW_IC_SDA_SETUP = 0x94, + DW_IC_ACK_GENERAL_CALL = 0x98, + DW_IC_ENABLE_STATUS = 0x9c, + DW_IC_FS_SPKLEN = 0xa0, + DW_IC_CLR_RESTART_DET = 0xa8, + DW_IC_COMP_PARAM_1 = 0xf4, + DW_IC_COMP_VERSION = 0xf8, + DW_IC_COMP_TYPE = 0xfc, +}; + +/* DW_IC_CON fields */ +#define DW_IC_CON_STOP_DET_IF_MASTER_ACTIV BIT(10) +#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) +#define DW_IC_CON_TX_EMPTY_CTRL BIT(8) +#define DW_IC_CON_STOP_IF_ADDRESSED BIT(7) +#define DW_IC_CON_SLAVE_DISABLE BIT(6) +#define DW_IC_CON_IC_RESTART_EN BIT(5) +#define DW_IC_CON_10BITADDR_MASTER BIT(4) +#define DW_IC_CON_10BITADDR_SLAVE BIT(3) +#define DW_IC_CON_SPEED(rv) extract32((rv), 1, 2) +#define DW_IC_CON_MASTER_MODE BIT(0) + +/* DW_IC_TAR fields */ +#define DW_IC_TAR_IC_10BITADDR_MASTER BIT(12) +#define DW_IC_TAR_SPECIAL BIT(11) +#define DW_IC_TAR_GC_OR_START BIT(10) +#define DW_IC_TAR_ADDRESS(rv) extract32((rv), 0, 10) + +/* DW_IC_DATA_CMD fields */ +#define DW_IC_DATA_CMD_RESTART BIT(10) +#define DW_IC_DATA_CMD_STOP BIT(9) +#define DW_IC_DATA_CMD_CMD BIT(8) +#define DW_IC_DATA_CMD_DAT(rv) extract32((rv), 0, 8) + +/* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */ +#define DW_IC_INTR_RESTART_DET BIT(12) +#define DW_IC_INTR_GEN_CALL BIT(11) +#define DW_IC_INTR_START_DET BIT(10) +#define DW_IC_INTR_STOP_DET BIT(9) +#define DW_IC_INTR_ACTIVITY BIT(8) +#define DW_IC_INTR_RX_DONE BIT(7) +#define DW_IC_INTR_TX_ABRT BIT(6) +#define DW_IC_INTR_RD_REQ BIT(5) +#define DW_IC_INTR_TX_EMPTY BIT(4) /* Hardware clear only. */ +#define DW_IC_INTR_TX_OVER BIT(3) +#define DW_IC_INTR_RX_FULL BIT(2) /* Hardware clear only. */ +#define DW_IC_INTR_RX_OVER BIT(1) +#define DW_IC_INTR_RX_UNDER BIT(0) + +/* DW_IC_ENABLE fields */ +#define DW_IC_ENABLE_TX_CMD_BLOCK BIT(2) +#define DW_IC_ENABLE_ABORT BIT(1) +#define DW_IC_ENABLE_ENABLE BIT(0) + +/* DW_IC_STATUS fields */ +#define DW_IC_STATUS_SLV_ACTIVITY BIT(6) +#define DW_IC_STATUS_MST_ACTIVITY BIT(5) +#define DW_IC_STATUS_RFF BIT(4) +#define DW_IC_STATUS_RFNE BIT(3) +#define DW_IC_STATUS_TFE BIT(2) +#define DW_IC_STATUS_TFNF BIT(1) +#define DW_IC_STATUS_ACTIVITY BIT(0) + +/* DW_IC_TX_ABRT_SOURCE fields */ +#define DW_IC_TX_TX_FLUSH_CNT extract32((rv), 23, 9) +#define DW_IC_TX_ABRT_USER_ABRT BIT(16) +#define DW_IC_TX_ABRT_SLVRD_INTX BIT(15) +#define DW_IC_TX_ABRT_SLV_ARBLOST BIT(14) +#define DW_IC_TX_ABRT_SLVFLUSH_TXFIFO BIT(13) +#define DW_IC_TX_ARB_LOST BIT(12) +#define DW_IC_TX_ABRT_MASTER_DIS BIT(11) +#define DW_IC_TX_ABRT_10B_RD_NORSTRT BIT(10) +#define DW_IC_TX_ABRT_SBYTE_NORSTRT BIT(9) +#define DW_IC_TX_ABRT_HS_NORSTRT BIT(8) +#define DW_IC_TX_ABRT_SBYTE_ACKDET BIT(7) +#define DW_IC_TX_ABRT_HS_ACKDET BIT(6) +#define DW_IC_TX_ABRT_GCALL_READ BIT(5) +#define DW_IC_TX_ABRT_GCALL_NOACK BIT(4) +#define DW_IC_TX_ABRT_TXDATA_NOACK BIT(3) +#define DW_IC_TX_ABRT_10ADDR2_NOACK BIT(2) +#define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(1) +#define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(0) + + +/* IC_ENABLE_STATUS fields */ +#define DW_IC_ENABLE_STATUS_SLV_RX_DATA_LOST BIT(2) +#define DW_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY BIT(1) +#define DW_IC_ENABLE_STATUS_IC_EN BIT(0) + +/* Masks for writable registers. */ +#define DW_IC_CON_MASK 0x000003ff +#define DW_IC_TAR_MASK 0x00000fff +#define DW_IC_SAR_MASK 0x000003ff +#define DW_IC_SS_SCL_HCNT_MASK 0x0000ffff +#define DW_IC_SS_SCL_LCNT_MASK 0x0000ffff +#define DW_IC_FS_SCL_HCNT_MASK 0x0000ffff +#define DW_IC_FS_SCL_LCNT_MASK 0x0000ffff +#define DW_IC_INTR_MASK_MASK 0x00001fff +#define DW_IC_ENABLE_MASK 0x00000007 +#define DW_IC_SDA_HOLD_MASK 0x00ffffff +#define DW_IC_SDA_SETUP_MASK 0x000000ff +#define DW_IC_FS_SPKLEN_MASK 0x000000ff + +/* Reset values */ +#define DW_IC_CON_INIT_VAL 0x7d +#define DW_IC_TAR_INIT_VAL 0x1055 +#define DW_IC_SAR_INIT_VAL 0x55 +#define DW_IC_SS_SCL_HCNT_INIT_VAL 0x190 +#define DW_IC_SS_SCL_LCNT_INIT_VAL 0x1d6 +#define DW_IC_FS_SCL_HCNT_INIT_VAL 0x3c +#define DW_IC_FS_SCL_LCNT_INIT_VAL 0x82 +#define DW_IC_INTR_MASK_INIT_VAL 0x8ff +#define DW_IC_STATUS_INIT_VAL 0x6 +#define DW_IC_SDA_HOLD_INIT_VAL 0x1 +#define DW_IC_SDA_SETUP_INIT_VAL 0x64 +#define DW_IC_FS_SPKLEN_INIT_VAL 0x2 + +#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7) +#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */ +#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5) +#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */ +#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3)) +#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */ +#define DW_IC_COMP_PARAM_1_INIT_VAL \ + (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \ + DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \ + DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \ + DW_IC_COMP_PARAM_1_INTR_IO | \ + DW_IC_COMP_PARAM_1_HAS_DMA | \ + DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \ + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \ + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16)) +#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a +#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140 + +static void dw_i2c_update_irq(DesignWareI2CState *s) +{ + int level; + uint32_t intr = s->ic_raw_intr_stat & s->ic_intr_mask; + + level = !!((intr & DW_IC_INTR_RX_UNDER) | + (intr & DW_IC_INTR_RX_OVER) | + (intr & DW_IC_INTR_RX_FULL) | + (intr & DW_IC_INTR_TX_OVER) | + (intr & DW_IC_INTR_TX_EMPTY) | + (intr & DW_IC_INTR_RD_REQ) | + (intr & DW_IC_INTR_TX_ABRT) | + (intr & DW_IC_INTR_RX_DONE) | + (intr & DW_IC_INTR_ACTIVITY) | + (intr & DW_IC_INTR_STOP_DET) | + (intr & DW_IC_INTR_START_DET) | + (intr & DW_IC_INTR_GEN_CALL) | + (intr & DW_IC_INTR_RESTART_DET) + ); + qemu_set_irq(s->irq, level); +} + +static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s) +{ + uint32_t value = s->rx_fifo[s->rx_cur]; + + 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) { + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER; + dw_i2c_update_irq(s); + } + return 0; + } + + s->rx_cur = (s->rx_cur + 1) % DESIGNWARE_I2C_RX_FIFO_SIZE; + + if (s->ic_rxflr > 0) { + s->ic_rxflr--; + } else { + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER; + dw_i2c_update_irq(s); + return 0; + } + + if (s->ic_rxflr <= s->ic_rx_tl) { + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL; + dw_i2c_update_irq(s); + } + + return value; +} + +static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t value = 0; + + DesignWareI2CState *s = opaque; + + switch (offset) { + case DW_IC_CON: + value = s->ic_con; + break; + case DW_IC_TAR: + value = s->ic_tar; + break; + case DW_IC_SAR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_sar\n", + DEVICE(s)->canonical_path); + value = s->ic_sar; + break; + case DW_IC_DATA_CMD: + value = dw_i2c_read_ic_data_cmd(s); + break; + case DW_IC_SS_SCL_HCNT: + value = s->ic_ss_scl_hcnt; + break; + case DW_IC_SS_SCL_LCNT: + value = s->ic_ss_scl_lcnt; + break; + case DW_IC_FS_SCL_HCNT: + value = s->ic_fs_scl_hcnt; + break; + case DW_IC_FS_SCL_LCNT: + value = s->ic_fs_scl_lcnt; + break; + case DW_IC_INTR_STAT: + value = s->ic_raw_intr_stat & s->ic_intr_mask; + break; + case DW_IC_INTR_MASK: + value = s->ic_intr_mask; + break; + case DW_IC_RAW_INTR_STAT: + value = s->ic_raw_intr_stat; + break; + case DW_IC_RX_TL: + value = s->ic_rx_tl; + break; + case DW_IC_TX_TL: + value = s->ic_tx_tl; + break; + case DW_IC_CLR_INTR: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL | + DW_IC_INTR_RESTART_DET | + DW_IC_INTR_START_DET | + DW_IC_INTR_STOP_DET | + DW_IC_INTR_ACTIVITY | + DW_IC_INTR_RX_DONE | + DW_IC_INTR_TX_ABRT | + DW_IC_INTR_RD_REQ | + DW_IC_INTR_TX_OVER | + DW_IC_INTR_RX_OVER | + DW_IC_INTR_RX_UNDER); + s->ic_tx_abrt_source = 0; + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_RX_UNDER: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_UNDER); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_RX_OVER: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_OVER); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_TX_OVER: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_OVER); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_RD_REQ: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RD_REQ); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_TX_ABRT: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_ABRT); + s->ic_tx_abrt_source = 0; + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_RX_DONE: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_DONE); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_ACTIVITY: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_ACTIVITY); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_STOP_DET: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_STOP_DET); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_START_DET: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_START_DET); + dw_i2c_update_irq(s); + break; + case DW_IC_CLR_GEN_CALL: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL); + dw_i2c_update_irq(s); + break; + case DW_IC_ENABLE: + value = s->ic_enable; + break; + case DW_IC_STATUS: + value = s->ic_status; + break; + case DW_IC_TXFLR: + value = s->ic_txflr; + break; + case DW_IC_RXFLR: + value = s->ic_rxflr; + break; + case DW_IC_SDA_HOLD: + value = s->ic_sda_hold; + break; + case DW_IC_TX_ABRT_SOURCE: + value = s->ic_tx_abrt_source; + break; + case DW_IC_SLV_DATA_NACK_ONLY: + qemu_log_mask(LOG_UNIMP, + "%s: unsupported read - ic_slv_data_nack_only\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_DMA_CR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_cr\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_DMA_TDLR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_tdlr\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_DMA_RDLR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_rdlr\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_SDA_SETUP: + value = s->ic_sda_setup; + break; + case DW_IC_ACK_GENERAL_CALL: + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_ack_general_call\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_ENABLE_STATUS: + value = s->ic_enable_status; + break; + case DW_IC_FS_SPKLEN: + value = s->ic_fs_spklen; + break; + case DW_IC_CLR_RESTART_DET: + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RESTART_DET); + break; + case DW_IC_COMP_PARAM_1: + value = s->ic_comp_param_1; + break; + case DW_IC_COMP_VERSION: + value = s->ic_comp_version; + break; + case DW_IC_COMP_TYPE: + value = s->ic_comp_type; + break; + + /* This register is invalid at this point. */ + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, offset); + break; + } + + return value; +} + +static void dw_i2c_write_ic_con(DesignWareI2CState *s, uint32_t value) +{ + if (value & DW_IC_CON_RX_FIFO_FULL_HLD_CTRL) { + qemu_log_mask(LOG_UNIMP, + "%s: unsupported ic_con flag - RX_FIFO_FULL_HLD_CTRL\n", + DEVICE(s)->canonical_path); + } + + if (!(s->ic_enable & DW_IC_ENABLE_ENABLE)) { + s->ic_con = value & DW_IC_CON_MASK; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid setting to ic_con %d when ic_enable[0]==1\n", + DEVICE(s)->canonical_path, value); + } +} + +static void dw_i2c_reset_to_idle(DesignWareI2CState *s) +{ + s->ic_enable_status &= ~DW_IC_ENABLE_STATUS_IC_EN; + s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY; + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL; + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER; + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_OVER; + s->ic_rxflr = 0; + s->ic_status &= ~DW_IC_STATUS_ACTIVITY; + s->status = DW_I2C_STATUS_IDLE; + dw_i2c_update_irq(s); +} + +static void dw_ic_tx_abort(DesignWareI2CState *s, uint32_t src) +{ + s->ic_tx_abrt_source |= src; + s->ic_raw_intr_stat |= DW_IC_INTR_TX_ABRT; + dw_i2c_reset_to_idle(s); + dw_i2c_update_irq(s); +} + +static void dw_i2c_write_ic_data_cmd(DesignWareI2CState *s, uint32_t value) +{ + int recv = !!(value & DW_IC_DATA_CMD_CMD); + + if (s->status == DW_I2C_STATUS_IDLE || + s->ic_raw_intr_stat & DW_IC_INTR_TX_ABRT) { + 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, DW_IC_TAR_ADDRESS(s->ic_tar), recv); + if (rv) { + dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK); + return; + } + s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING; + } + + /* Send data */ + if (!recv) { + int rv = i2c_send(s->bus, DW_IC_DATA_CMD_DAT(value)); + if (rv) { + i2c_end_transfer(s->bus); + dw_ic_tx_abort(s, DW_IC_TX_ABRT_TXDATA_NOACK); + return; + } + dw_i2c_update_irq(s); + } + + /* Restart command */ + if (value & DW_IC_DATA_CMD_RESTART && s->ic_con & DW_IC_CON_IC_RESTART_EN) { + s->ic_raw_intr_stat |= DW_IC_INTR_RESTART_DET | + DW_IC_INTR_START_DET | + DW_IC_INTR_ACTIVITY; + s->ic_status |= DW_IC_STATUS_ACTIVITY; + dw_i2c_update_irq(s); + + if (i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), recv)) { + dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK); + return; + } + + s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING; + } + + /* Receive data */ + if (recv) { + uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE; + + if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) { + s->rx_fifo[pos] = i2c_recv(s->bus); + s->ic_rxflr++; + } else { + s->ic_raw_intr_stat |= DW_IC_INTR_RX_OVER; + dw_i2c_update_irq(s); + } + + if (s->ic_rxflr > s->ic_rx_tl) { + s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL; + dw_i2c_update_irq(s); + } + if (value & DW_IC_DATA_CMD_STOP) { + i2c_nack(s->bus); + } + } + + /* Stop command */ + if (value & DW_IC_DATA_CMD_STOP) { + s->ic_raw_intr_stat |= DW_IC_INTR_STOP_DET; + s->ic_status &= ~DW_IC_STATUS_ACTIVITY; + s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY; + i2c_end_transfer(s->bus); + dw_i2c_update_irq(s); + } +} + +static void dw_i2c_write_ic_enable(DesignWareI2CState *s, uint32_t value) +{ + if (value & DW_IC_ENABLE_ENABLE && !(s->ic_con & DW_IC_CON_SLAVE_DISABLE)) { + qemu_log_mask(LOG_UNIMP, + "%s: Designware I2C slave mode is not supported.\n", + DEVICE(s)->canonical_path); + return; + } + + s->ic_enable = value & DW_IC_ENABLE_MASK; + + if (value & DW_IC_ENABLE_ABORT || value & DW_IC_ENABLE_TX_CMD_BLOCK) { + dw_ic_tx_abort(s, DW_IC_TX_ABRT_USER_ABRT); + return; + } + + if (value & DW_IC_ENABLE_ENABLE) { + s->ic_enable_status |= DW_IC_ENABLE_STATUS_IC_EN; + s->ic_status |= DW_IC_STATUS_ACTIVITY; + s->ic_raw_intr_stat |= DW_IC_INTR_ACTIVITY | + DW_IC_INTR_START_DET | + DW_IC_INTR_TX_EMPTY; + s->status = DW_I2C_STATUS_SENDING_ADDRESS; + dw_i2c_update_irq(s); + } else if ((value & DW_IC_ENABLE_ENABLE) == 0) { + dw_i2c_reset_to_idle(s); + } + +} + +static void dw_i2c_write_ic_rx_tl(DesignWareI2CState *s, uint32_t value) +{ + /* Note that a value of 0 for ic_rx_tl indicates a threashold 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, value); + s->ic_rx_tl = DESIGNWARE_I2C_RX_FIFO_SIZE - 1; + } else { + s->ic_rx_tl = value; + } + + if (s->ic_rxflr > s->ic_rx_tl && s->ic_enable & DW_IC_ENABLE_ENABLE) { + s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL; + } else { + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL; + } + dw_i2c_update_irq(s); +} + +static void dw_i2c_write_ic_tx_tl(DesignWareI2CState *s, uint32_t value) +{ + /* + * Note that a value of 0 for ic_tx_tl indicates a threashold 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, value); + s->ic_tx_tl = DESIGNWARE_I2C_TX_FIFO_SIZE - 1; + } else { + s->ic_tx_tl = value; + } +} + +static void dw_i2c_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + DesignWareI2CState *s = opaque; + + /* The order of the registers are their order in memory. */ + switch (offset) { + case DW_IC_CON: + dw_i2c_write_ic_con(s, value); + break; + case DW_IC_TAR: + s->ic_tar = value & DW_IC_TAR_MASK; + break; + case DW_IC_SAR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n", + DEVICE(s)->canonical_path); + s->ic_sar = value & DW_IC_SAR_MASK; + break; + case DW_IC_DATA_CMD: + dw_i2c_write_ic_data_cmd(s, value); + break; + case DW_IC_SS_SCL_HCNT: + s->ic_ss_scl_hcnt = value & DW_IC_SS_SCL_HCNT_MASK; + break; + case DW_IC_SS_SCL_LCNT: + s->ic_ss_scl_lcnt = value & DW_IC_SS_SCL_LCNT_MASK; + break; + case DW_IC_FS_SCL_HCNT: + s->ic_fs_scl_hcnt = value & DW_IC_FS_SCL_HCNT_MASK; + break; + case DW_IC_FS_SCL_LCNT: + s->ic_fs_scl_lcnt = value & DW_IC_FS_SCL_LCNT_MASK; + break; + case DW_IC_INTR_MASK: + s->ic_intr_mask = value & DW_IC_INTR_MASK_MASK; + dw_i2c_update_irq(s); + break; + case DW_IC_RX_TL: + dw_i2c_write_ic_rx_tl(s, value); + break; + case DW_IC_TX_TL: + dw_i2c_write_ic_tx_tl(s, value); + break; + case DW_IC_ENABLE: + dw_i2c_write_ic_enable(s, value); + break; + case DW_IC_SDA_HOLD: + s->ic_sda_hold = value & DW_IC_SDA_HOLD_MASK; + break; + case DW_IC_SLV_DATA_NACK_ONLY: + qemu_log_mask(LOG_UNIMP, + "%s: unsupported write - ic_slv_data_nack_only\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_DMA_CR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_cr\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_DMA_TDLR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_tdlr\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_DMA_RDLR: + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_rdlr\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_SDA_SETUP: + s->ic_sda_setup = value & DW_IC_SDA_SETUP_MASK; + break; + case DW_IC_ACK_GENERAL_CALL: + qemu_log_mask(LOG_UNIMP, + "%s: unsupported write - ic_ack_general_call\n", + DEVICE(s)->canonical_path); + break; + case DW_IC_FS_SPKLEN: + s->ic_fs_spklen = value & DW_IC_FS_SPKLEN_MASK; + break; + + /* This register is invalid at this point. */ + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset or readonly register 0x%" + HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, offset); + break; + } +} + +static const MemoryRegionOps designware_i2c_ops = { + .read = dw_i2c_read, + .write = dw_i2c_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .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); + + s->ic_con = DW_IC_CON_INIT_VAL; + s->ic_tar = DW_IC_TAR_INIT_VAL; + s->ic_sar = DW_IC_SAR_INIT_VAL; + s->ic_ss_scl_hcnt = DW_IC_SS_SCL_HCNT_INIT_VAL; + s->ic_ss_scl_lcnt = DW_IC_SS_SCL_LCNT_INIT_VAL; + s->ic_fs_scl_hcnt = DW_IC_FS_SCL_HCNT_INIT_VAL; + s->ic_fs_scl_lcnt = DW_IC_FS_SCL_LCNT_INIT_VAL; + s->ic_intr_mask = DW_IC_INTR_MASK_INIT_VAL; + s->ic_raw_intr_stat = 0; + s->ic_rx_tl = 0; + s->ic_tx_tl = 0; + s->ic_enable = 0; + s->ic_status = DW_IC_STATUS_INIT_VAL; + s->ic_txflr = 0; + s->ic_rxflr = 0; + s->ic_sda_hold = DW_IC_SDA_HOLD_INIT_VAL; + s->ic_tx_abrt_source = 0; + s->ic_sda_setup = DW_IC_SDA_SETUP_INIT_VAL; + s->ic_enable_status = 0; + s->ic_fs_spklen = DW_IC_FS_SPKLEN_INIT_VAL; + s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL; + s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL; + s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL; + + s->rx_cur = 0; + 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 = (VMStateField[]) { + VMSTATE_UINT32(ic_con, DesignWareI2CState), + VMSTATE_UINT32(ic_tar, DesignWareI2CState), + VMSTATE_UINT32(ic_sar, DesignWareI2CState), + VMSTATE_UINT32(ic_ss_scl_hcnt, DesignWareI2CState), + VMSTATE_UINT32(ic_ss_scl_lcnt, DesignWareI2CState), + VMSTATE_UINT32(ic_fs_scl_hcnt, DesignWareI2CState), + VMSTATE_UINT32(ic_fs_scl_lcnt, DesignWareI2CState), + VMSTATE_UINT32(ic_intr_mask, DesignWareI2CState), + VMSTATE_UINT32(ic_raw_intr_stat, DesignWareI2CState), + VMSTATE_UINT32(ic_rx_tl, DesignWareI2CState), + VMSTATE_UINT32(ic_tx_tl, DesignWareI2CState), + VMSTATE_UINT32(ic_enable, DesignWareI2CState), + VMSTATE_UINT32(ic_status, DesignWareI2CState), + VMSTATE_UINT32(ic_txflr, DesignWareI2CState), + VMSTATE_UINT32(ic_rxflr, DesignWareI2CState), + VMSTATE_UINT32(ic_sda_hold, DesignWareI2CState), + VMSTATE_UINT32(ic_tx_abrt_source, DesignWareI2CState), + VMSTATE_UINT32(ic_sda_setup, DesignWareI2CState), + VMSTATE_UINT32(ic_enable_status, DesignWareI2CState), + VMSTATE_UINT32(ic_fs_spklen, DesignWareI2CState), + VMSTATE_UINT32(ic_comp_param_1, DesignWareI2CState), + VMSTATE_UINT32(ic_comp_version, DesignWareI2CState), + VMSTATE_UINT32(ic_comp_type, DesignWareI2CState), + VMSTATE_UINT32(status, DesignWareI2CState), + VMSTATE_UINT8_ARRAY(rx_fifo, DesignWareI2CState, + DESIGNWARE_I2C_RX_FIFO_SIZE), + VMSTATE_UINT8(rx_cur, DesignWareI2CState), + VMSTATE_END_OF_LIST(), + }, +}; + +static void designware_i2c_smbus_init(Object *obj) +{ + DesignWareI2CState *s = DESIGNWARE_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s, + "regs", 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); + + s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); +} + +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; +} + +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_smbus_init, + }, +}; +DEFINE_TYPES(designware_i2c_types); diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index c459adcb596cc..88aea35662dd5 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/riscv/Kconfig b/hw/riscv/Kconfig index fc9c35bd981ea..9662c32e4b040 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -128,3 +128,24 @@ config XIANGSHAN_KUNMINGHU select RISCV_APLIC select RISCV_IMSIC select SERIAL_MM + +config TENSTORRENT + bool + default y + depends on RISCV64 + imply PCI_DEVICES + imply TEST_DEVICES + select DEVICE_TREE + select RISCV_NUMA + select PVPANIC_MMIO + select PCI + select PCI_EXPRESS_GENERIC_BRIDGE + select SERIAL_MM + select RISCV_ACLINT + select RISCV_APLIC + select RISCV_IMSIC + select FW_CFG_DMA + select PLATFORM_BUS + select DESIGNWARE_I2C + select DS1338 + select TMP105 diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c new file mode 100644 index 0000000000000..8d45a21f85e2f --- /dev/null +++ b/hw/riscv/aia.c @@ -0,0 +1,89 @@ +/* + * 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, + 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) +{ + int i; + hwaddr addr = 0; + uint32_t guest_bits; + DeviceState *aplic_s_dev = NULL; + DeviceState *aplic_m_dev = NULL; + + if (msimode) { + if (!kvm_enabled()) { + /* Per-socket M-level IMSICs */ + addr = 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 = 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_dev = riscv_aplic_create(aplic_m->base + + socket * aplic_m->size, + aplic_m->size, + (msimode) ? 0 : base_hartid, + (msimode) ? 0 : hart_count, + num_sources, + VIRT_IRQCHIP_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, + VIRT_IRQCHIP_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 0000000000000..a63a1ab293fe9 --- /dev/null +++ b/hw/riscv/aia.h @@ -0,0 +1,59 @@ +/* + * 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" + +/* + * The virt machine physical address space used by some of the devices + * namely ACLINT, PLIC, APLIC, and IMSIC depend on number of Sockets, + * number of CPUs, and number of IMSIC guest files. + * + * Various limits defined by VIRT_SOCKETS_MAX_BITS, VIRT_CPUS_MAX_BITS, + * and VIRT_IRQCHIP_MAX_GUESTS_BITS are tuned for maximum utilization + * of virt machine physical address space. + */ + +#define VIRT_SOCKETS_MAX_BITS 2 +#define VIRT_CPUS_MAX_BITS 9 +#define VIRT_CPUS_MAX (1 << VIRT_CPUS_MAX_BITS) +#define VIRT_SOCKETS_MAX (1 << VIRT_SOCKETS_MAX_BITS) + +#define VIRT_IRQCHIP_NUM_MSIS 255 +#define VIRT_IRQCHIP_NUM_SOURCES 96 +#define VIRT_IRQCHIP_NUM_PRIO_BITS 3 +#define VIRT_IRQCHIP_MAX_GUESTS_BITS 3 +#define VIRT_IRQCHIP_MAX_GUESTS ((1U << VIRT_IRQCHIP_MAX_GUESTS_BITS) - 1U) + + +#define VIRT_IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) +#if VIRT_IMSIC_GROUP_MAX_SIZE < \ + IMSIC_GROUP_SIZE(VIRT_CPUS_MAX_BITS, VIRT_IRQCHIP_MAX_GUESTS_BITS) +#error "Can't accommodate single IMSIC group in address space" +#endif + +#define VIRT_IMSIC_MAX_SIZE (VIRT_SOCKETS_MAX * \ + VIRT_IMSIC_GROUP_MAX_SIZE) +#if 0x4000000 < VIRT_IMSIC_MAX_SIZE +#error "Can't accommodate all IMSIC groups in address space" +#endif + +uint32_t imsic_num_bits(uint32_t count); + +DeviceState *riscv_create_aia(bool msimode, int aia_guests, + 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); + + +#endif diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 75f34287ff1b6..3913bb1183f47 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -69,11 +69,22 @@ 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); } +void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info, + RISCVHartArrayState *harts, + hwaddr start, hwaddr size) +{ + riscv_boot_info_init(info, harts); + info->ram_low_start = start; + info->ram_low_size = size; +} + hwaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info, hwaddr firmware_end_addr) { if (info->is_32bit) { @@ -134,6 +145,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) @@ -146,7 +158,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); } @@ -154,39 +167,57 @@ 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, + 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; g_assert(firmware_filename != NULL); - if (load_elf_ram_sym(firmware_filename, NULL, NULL, NULL, - &firmware_entry, NULL, &firmware_end, NULL, - 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { + firmware_size = load_elf_ram_sym(firmware_filename, NULL, NULL, NULL, + &firmware_entry, NULL, &firmware_end, + NULL, 0, EM_RISCV, 1, 0, NULL, false, + sym_cb); + if (firmware_size > 0) { *firmware_load_addr = firmware_entry; return firmware_end; } + if (firmware_size != ELF_LOAD_NOT_ELF) { + /* + * If the user specified an ELF format firmware that could not be + * loaded as an ELF, it's possible that loading it as a binary is + * not what was intended. + */ + warn_report("could not load ELF format firmware '%s' (%s). " + "Attempting to load as binary.", + firmware_filename, + load_elf_strerror(firmware_size)); + } + firmware_size = load_image_targphys_as(firmware_filename, *firmware_load_addr, - current_machine->ram_size, NULL, + mem_size, NULL, NULL); if (firmware_size > 0) { return *firmware_load_addr + firmware_size; } - error_report("could not load firmware '%s'", firmware_filename); + error_report("could not load firmware '%s': %s", firmware_filename, + load_elf_strerror(firmware_size)); exit(1); } 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; @@ -232,6 +263,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; @@ -263,7 +295,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; @@ -359,7 +391,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); } @@ -486,6 +518,27 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts kernel_entry); } +/* Simple payload so OpenSBI does not hang early with no output */ +void riscv_setup_halting_payload(MachineState *machine, + RISCVBootInfo *info, hwaddr addr) +{ + int i; + uint32_t payload_vec[] = { + 0x10500073, /* 1: wfi */ + 0xffdff06f, /* j 1b */ + }; + /* copy in the payload vector in little_endian byte order */ + for (i = 0; i < ARRAY_SIZE(payload_vec); i++) { + payload_vec[i] = cpu_to_le32(payload_vec[i]); + } + rom_add_blob_fixed_as("mrom.payload", payload_vec, sizeof(payload_vec), + addr, &address_space_memory); + + info->kernel_size = sizeof(payload_vec); + info->image_low_addr = addr; + info->image_high_addr = info->image_low_addr + info->kernel_size; +} + void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr) { CPUState *cs; diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 2a8d5b136cc40..bb6c4910176e5 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -1,5 +1,5 @@ riscv_ss = ss.source_set() -riscv_ss.add(files('boot.c')) +riscv_ss.add(files('boot.c', 'aia.c')) riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) @@ -9,6 +9,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 a17f62cd082dd..3aa9d0e25a1cf 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -614,18 +614,20 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) firmware_load_addr = RESET_VECTOR; } + riscv_boot_info_init(&boot_info, &s->soc.u_cpus); + /* 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 d369a8a7dcd18..968c7dcb2969f 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -99,12 +99,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/shakti_c.c b/hw/riscv/shakti_c.c index 3e7f4411727d6..d0398f4fd0ecb 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -45,6 +45,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 */ @@ -57,8 +58,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 a7492aa27a46c..cc11056658592 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -589,7 +589,8 @@ static void sifive_u_machine_init(MachineState *machine) } 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); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index b0bab3fe00819..9a8bce07d9f6e 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -289,9 +289,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); @@ -301,7 +304,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 0000000000000..5d6944b6411ff --- /dev/null +++ b/hw/riscv/tt_atlantis.c @@ -0,0 +1,917 @@ +/* + * Tenstorrent Atlantis RISC-V System on Chip + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright 2025 Tenstorrent, Joel Stanley + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/units.h" + +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" + +#include "target/riscv/cpu.h" +#include "target/riscv/pmu.h" + +#include "hw/riscv/boot.h" +#include "hw/riscv/numa.h" +#include "hw/riscv/riscv_hart.h" + +#include "hw/char/serial-mm.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/intc/riscv_aplic.h" +#include "hw/misc/pvpanic.h" +#include "hw/pci-host/gpex.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_MAX_GUESTS_BITS 3 +#define TT_IRQCHIP_MAX_GUESTS ((1U << ATL_IRQCHIP_MAX_GUESTS_BITS) - 1U) + +#define IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) + +#define FDT_PCI_ADDR_CELLS 3 +#define FDT_PCI_INT_CELLS 1 +#define FDT_MAX_INT_CELLS 2 +#define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ + 1 + FDT_MAX_INT_CELLS) + +#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_FW_CFG] = { 0x80002000, 0xff }, /* qemu only */ + [TT_ATL_SYSCON] = { 0x80004000, 0x1000 }, /* qemu only */ + [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 }, + [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 }, + [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 }, + [TT_ATL_PRCM] = { 0xa8000000, 0x10000 }, + [TT_ATL_TIMER] = { 0xa8020000, 0x10000 }, + [TT_ATL_WDT0] = { 0xa8030000, 0x10000 }, + [TT_ATL_PCI_MMU_CFG] = { 0xaa000000, 0x100000 }, + [TT_ATL_UART0] = { 0xb0100000, 0x10000 }, + [TT_ATL_I2C0] = { 0xb0400000, 0x10000 }, + [TT_ATL_I2C1] = { 0xb0500000, 0x10000 }, + [TT_ATL_I2C2] = { 0xb0600000, 0x10000 }, + [TT_ATL_I2C3] = { 0xb0700000, 0x10000 }, + [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 }, + [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 }, + [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 }, + [TT_ATL_PCIE_ECAM0] = { 0x01110000000, 0x10000000 }, + [TT_ATL_PCIE_ECAM1] = { 0x01120000000, 0x10000000 }, + [TT_ATL_PCIE_ECAM2] = { 0x01130000000, 0x10000000 }, + [TT_ATL_PCIE_MMIO0] = { 0x10000000000, 0x10000000000 }, + [TT_ATL_PCIE_PIO0] = { 0x10000000000, 0x10000 }, /* qemu only */ + [TT_ATL_PCIE_MMIO0_32] = { 0x10004000000, 0x4000000 }, /* qemu only */ + [TT_ATL_PCIE_MMIO0_64] = { 0x10010000000, 0x0fff0000000 }, /* qemu only */ + [TT_ATL_PCIE_MMIO1] = { 0x20000000000, 0x10000000000 }, + [TT_ATL_PCIE_MMIO2] = { 0x30000000000, 0x10000000000 }, +}; + +static uint32_t next_phandle(void) +{ + static uint32_t phandle = 1; + return phandle++; +} + +static void create_pcie_irq_map(void *fdt, char *nodename, int legacy_irq, + uint32_t irqchip_phandle) +{ + int pin, dev; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS * + FDT_MAX_INT_MAP_WIDTH] = {}; + uint32_t *irq_map = full_irq_map; + + /* + * This code creates a standard swizzle of interrupts such that + * each device's first interrupt is based on it's PCI_SLOT number. + * (See pci_swizzle_map_irq_fn()) + * + * We only need one entry per interrupt in the table (not one per + * possible slot) seeing the interrupt-map-mask will allow the table + * to wrap to any number of devices. + */ + for (dev = 0; dev < PCI_NUM_PINS; dev++) { + int devfn = dev * 0x8; + + for (pin = 0; pin < PCI_NUM_PINS; pin++) { + int irq_nr = legacy_irq + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); + int i = 0; + + /* Fill PCI address cells */ + irq_map[i] = cpu_to_be32(devfn << 8); + i += FDT_PCI_ADDR_CELLS; + + /* Fill PCI Interrupt cells */ + irq_map[i] = cpu_to_be32(pin + 1); + i += FDT_PCI_INT_CELLS; + + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(irqchip_phandle); + irq_map[i++] = cpu_to_be32(irq_nr); + irq_map[i++] = cpu_to_be32(0x4); + + if (!irq_map_stride) { + irq_map_stride = i; + } + irq_map += irq_map_stride; + } + } + + qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map, + PCI_NUM_PINS * PCI_NUM_PINS * + irq_map_stride * sizeof(uint32_t)); + + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles) +{ + uint32_t cpu_phandle; + void *fdt = MACHINE(s)->fdt; + + for (int cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + RISCVCPU *cpu_ptr = &s->soc.harts[cpu]; + g_autofree char *cpu_name = NULL; + g_autofree char *intc_name = NULL; + + cpu_phandle = next_phandle(); + + cpu_name = g_strdup_printf("/cpus/cpu@%d", s->soc.hartid_base + cpu); + qemu_fdt_add_subnode(fdt, cpu_name); + + qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv57"); + + riscv_isa_write_fdt(cpu_ptr, fdt, cpu_name); + + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbom-block-size", + cpu_ptr->cfg.cbom_blocksize); + + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cboz-block-size", + cpu_ptr->cfg.cboz_blocksize); + + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size", + cpu_ptr->cfg.cbop_blocksize); + + qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", s->soc.hartid_base + cpu); + qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); + qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle); + + intc_phandles[cpu] = next_phandle(); + + intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name); + qemu_fdt_add_subnode(fdt, intc_name); + qemu_fdt_setprop_cell(fdt, intc_name, "phandle", + intc_phandles[cpu]); + qemu_fdt_setprop_string(fdt, intc_name, "compatible", + "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1); + } +} + +static void create_fdt_memory_node(TTAtlantisState *s, + hwaddr addr, hwaddr size) +{ + void *fdt = MACHINE(s)->fdt; + g_autofree char *name = g_strdup_printf("/memory@%"HWADDR_PRIX, addr); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, addr, 2, size); + qemu_fdt_setprop_string(fdt, name, "device_type", "memory"); +} + +static void create_fdt_memory(TTAtlantisState *s) +{ + 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_memory_node(s, s->memmap[TT_ATL_DDR_LO].base, size_lo); + 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_memory_node(s, s->memmap[TT_ATL_DDR_HI].base + size_lo, + size_hi); + } +} + +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) +{ + g_autofree char *pmu_name = g_strdup_printf("/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 = NULL; + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + TT_ACLINT_TIMEBASE_FREQ); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + intc_phandles = g_new0(uint32_t, ms->smp.cpus); + + create_fdt_cpus(s, intc_phandles); + + create_fdt_memory(s); + + create_fdt_aclint(s, intc_phandles); + + /* 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, 0); + + /* 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_num_bits(s->aia_guests + 1)); + + 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_pcie(void *fdt, + const MemMapEntry *mem_ecam, + const MemMapEntry *mem_pio, + const MemMapEntry *mem_mmio32, + const MemMapEntry *mem_mmio64, + int legacy_irq, + uint32_t aplic_s_phandle, + uint32_t imsic_s_phandle) +{ + g_autofree char *name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + mem_ecam->base); + + qemu_fdt_setprop_cell(fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS); + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS); + qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0x2); + qemu_fdt_setprop_string(fdt, name, "compatible", "pci-host-ecam-generic"); + qemu_fdt_setprop_string(fdt, name, "device_type", "pci"); + qemu_fdt_setprop_cells(fdt, name, "bus-range", 0, + mem_ecam->size / PCIE_MMCFG_SIZE_MIN - 1); + qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cell(fdt, name, "msi-parent", imsic_s_phandle); + + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, mem_ecam->base, + 2, mem_ecam->size); + if (!(mem_mmio32->base & 0xffffffffUL)) { + /* XXX: this is a silly hack because it would collide with PIO */ + error_report("mmio32 base must not be 0 mod 2^32"); + exit(1); + } + uint32_t flags = FDT_PCI_RANGE_MMIO_64BIT | FDT_PCI_RANGE_PREFETCHABLE; + qemu_fdt_setprop_sized_cells(fdt, name, "ranges", + 1, FDT_PCI_RANGE_IOPORT, + 2, 0x0, + 2, mem_pio->base, + 2, mem_pio->size, + 1, FDT_PCI_RANGE_MMIO, + 2, (mem_mmio32->base & 0xffffffffUL), + 2, mem_mmio32->base, + 2, mem_mmio32->size, + 1, flags, + 2, mem_mmio64->base, + 2, mem_mmio64->base, + 2, mem_mmio64->size); + + create_pcie_irq_map(fdt, name, legacy_irq, aplic_s_phandle); +} + +static void create_fdt_reset(void *fdt, const MemMapEntry *mem) +{ + uint32_t syscon_phandle = next_phandle(); + char *name; + + name = g_strdup_printf("/soc/syscon@%"HWADDR_PRIX, mem->base); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "syscon"); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); + qemu_fdt_setprop_cell(fdt, name, "phandle", syscon_phandle); + g_free(name); + + name = g_strdup_printf("/poweroff"); + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-poweroff"); + qemu_fdt_setprop_cell(fdt, name, "regmap", syscon_phandle); + qemu_fdt_setprop_cell(fdt, name, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, name, "value", PVPANIC_SHUTDOWN); + g_free(name); +} + +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_fw_cfg(void *fdt, const MemMapEntry *mem) +{ + g_autofree char *name = g_strdup_printf("/fw-cfg@%"HWADDR_PRIX, mem->base); + + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "compatible", "qemu,fw-cfg-mmio"); + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); + qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0); +} + +static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq, + int irqchip_phandle) +{ + g_autofree char *name = g_strdup_printf("/soc/i2c@%" PRIx64, 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, "#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@%"PRIx64"/sensor@%d", + 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(); + 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_pcie(fdt, + &s->memmap[TT_ATL_PCIE_ECAM0], + &s->memmap[TT_ATL_PCIE_PIO0], + &s->memmap[TT_ATL_PCIE_MMIO0_32], + &s->memmap[TT_ATL_PCIE_MMIO0_64], + TT_ATL_PCIE0_INTA_IRQ, + aplic_s_phandle, imsic_s_phandle); + + create_fdt_reset(fdt, &s->memmap[TT_ATL_SYSCON]); + + create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ, + aplic_s_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); + } + + create_fdt_i2c_device(s, 0, "national,lm75", 0x48); + create_fdt_i2c_device(s, 0, "dallas,ds1338", 0x6f); +} + +static void create_fdt(TTAtlantisState *s) +{ + MachineState *ms = MACHINE(s); + uint8_t rng_seed[32]; + g_autofree char *name = NULL; + void *fdt; + + fdt = create_device_tree(&s->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + ms->fdt = fdt; + + qemu_fdt_setprop_string(fdt, "/", "model", + "Tenstorrent Atlantis RISC-V Machine"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "tenstorrent,atlantis"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + + /* + * The "/soc/pci@..." node is needed for PCIE hotplugs + * that might happen before finalize_fdt(). + */ + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + s->memmap[TT_ATL_PCIE_ECAM0].base); + qemu_fdt_add_subnode(fdt, name); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + s->memmap[TT_ATL_PCIE_ECAM1].base); + qemu_fdt_add_subnode(fdt, name); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX, + s->memmap[TT_ATL_PCIE_ECAM2].base); + qemu_fdt_add_subnode(fdt, name); + + qemu_fdt_add_subnode(fdt, "/chosen"); + + /* Pass seed to RNG */ + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + + qemu_fdt_add_subnode(fdt, "/aliases"); + + create_fdt_fw_cfg(fdt, &s->memmap[TT_ATL_SYSCON]); + create_fdt_pmu(s); +} + +static void gpex_pcie_init_one(TTAtlantisState *s, GPEXHost *gpex_host, + MemoryRegion *mr, + const MemMapEntry *mem_ecam, + const MemMapEntry *mem_pio, + const MemMapEntry *mem_mmio32, + const MemMapEntry *mem_mmio64, + int legacy_irq) +{ + DeviceState *dev; + Object *obj; + MemoryRegion *ecam_alias, *ecam_reg; + MemoryRegion *mmio32_alias, *mmio64_alias, *mmio_reg; + hwaddr ecam_base = mem_ecam->base; + hwaddr ecam_size = mem_ecam->size; + hwaddr pio_base = mem_pio->base; + hwaddr pio_size = mem_pio->size; + hwaddr mmio32_base = mem_mmio32->base; + hwaddr mmio32_size = mem_mmio32->size; + hwaddr mmio64_base = mem_mmio64->base; + hwaddr mmio64_size = mem_mmio64->size; + qemu_irq irq; + char name[16]; + int i; + + snprintf(name, sizeof(name), "pcie"); + object_initialize_child(OBJECT(s), name, gpex_host, TYPE_GPEX_HOST); + dev = DEVICE(gpex_host); + obj = OBJECT(dev); + + object_property_set_uint(obj, PCI_HOST_ECAM_BASE, ecam_base, &error_abort); + object_property_set_int(obj, PCI_HOST_ECAM_SIZE, ecam_size, &error_abort); + object_property_set_uint(obj, PCI_HOST_BELOW_4G_MMIO_BASE, mmio32_base, + &error_abort); + object_property_set_int(obj, PCI_HOST_BELOW_4G_MMIO_SIZE, mmio32_size, + &error_abort); + object_property_set_uint(obj, PCI_HOST_ABOVE_4G_MMIO_BASE, mmio64_base, + &error_abort); + object_property_set_int(obj, PCI_HOST_ABOVE_4G_MMIO_SIZE, mmio64_size, + &error_abort); + object_property_set_uint(obj, PCI_HOST_PIO_BASE, pio_base, &error_abort); + object_property_set_int(obj, PCI_HOST_PIO_SIZE, pio_size, &error_abort); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + ecam_alias = g_new0(MemoryRegion, 1); + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + snprintf(name, sizeof(name), "pcie.ecam"); + memory_region_init_alias(ecam_alias, obj, name, + ecam_reg, 0, ecam_size); + memory_region_add_subregion(mr, ecam_base, ecam_alias); + + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + + mmio32_alias = g_new0(MemoryRegion, 1); + snprintf(name, sizeof(name), "pcie.mmio32"); + memory_region_init_alias(mmio32_alias, obj, name, + mmio_reg, mmio32_base & 0xffffffffUL, mmio32_size); + memory_region_add_subregion(mr, mmio32_base, mmio32_alias); + + mmio64_alias = g_new0(MemoryRegion, 1); + snprintf(name, sizeof(name), "pcie.mmio64"); + memory_region_init_alias(mmio64_alias, obj, name, + mmio_reg, mmio64_base, mmio64_size); + memory_region_add_subregion(mr, mmio64_base, mmio64_alias); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); + + for (i = 0; i < PCI_NUM_PINS; i++) { + irq = qdev_get_gpio_in(s->irqchip, legacy_irq + i); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); + gpex_set_irq_num(GPEX_HOST(dev), i, legacy_irq + i); + } + + gpex_host->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus; +} + +static void gpex_pcie_init(TTAtlantisState *s, MemoryRegion *mr) +{ + gpex_pcie_init_one(s, &s->gpex_host, mr, + &s->memmap[TT_ATL_PCIE_ECAM0], + &s->memmap[TT_ATL_PCIE_PIO0], + &s->memmap[TT_ATL_PCIE_MMIO0_32], + &s->memmap[TT_ATL_PCIE_MMIO0_64], + TT_ATL_PCIE0_INTA_IRQ); +} + +static DeviceState *create_reboot_device(const MemMapEntry *mem) +{ + DeviceState *dev = qdev_new(TYPE_PVPANIC_MMIO_DEVICE); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + qdev_prop_set_uint32(dev, "events", PVPANIC_SHUTDOWN | PVPANIC_PANICKED); + + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, mem->base); + + return dev; +} + +static FWCfgState *create_fw_cfg(const MemMapEntry *mem, int num_cpus) +{ + FWCfgState *fw_cfg; + hwaddr base = mem->base; + + fw_cfg = fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, + &address_space_memory); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, num_cpus); + + return fw_cfg; +} + +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; + + /* + * An 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); + } else { + riscv_setup_halting_payload(machine, &boot_info, kernel_start_addr); + } + kernel_entry = boot_info.image_low_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, hi_ram_size; + int hart_count = machine->smp.cpus; + int base_hartid = 0; + + 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", base_hartid, + &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, s->aia_guests, 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, base_hartid, hart_count); + + riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base, + TT_ACLINT_MTIME_SIZE, + base_hartid, hart_count, + TT_ACLINT_MTIMECMP, + TT_ACLINT_MTIME, + TT_ACLINT_TIMEBASE_FREQ, true); + + /* DDR */ + + /* The high address covers all of RAM, the low address just the first 2GB */ + lo_ram_size = s->memmap[TT_ATL_DDR_LO].size; + hi_ram_size = s->memmap[TT_ATL_DDR_HI].size; + if (machine->ram_size > hi_ram_size) { + char *sz = size_to_str(hi_ram_size); + error_report("RAM size is too large, maximum is %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + + memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", machine->ram, + 0, lo_ram_size); + memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", machine->ram, + 0, hi_ram_size); + memory_region_add_subregion(system_memory, + s->memmap[TT_ATL_DDR_LO].base, ram_lo); + memory_region_add_subregion(system_memory, + s->memmap[TT_ATL_DDR_HI].base, ram_hi); + + /* 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); + + /* + * Init fw_cfg. Must be done before riscv_load_fdt, otherwise the + * device tree cannot be altered and we get FDT_ERR_NOSPACE. + */ + s->fw_cfg = create_fw_cfg(&s->memmap[TT_ATL_FW_CFG], machine->smp.cpus); + rom_set_fw(s->fw_cfg); + + /* PCIe */ + gpex_pcie_init(s, system_memory); + + /* Reboot and exit */ + create_reboot_device(&s->memmap[TT_ATL_SYSCON]); + + /* UART */ + serial_mm_init(system_memory, s->memmap[TT_ATL_UART0].base, 2, + qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ), + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + /* I2C */ + for (int i = 0; i < TT_ATL_NUM_I2C; i++) { + object_initialize_child(OBJECT(s), "i2c[*]", &s->i2c[i], + TYPE_DESIGNWARE_I2C); + sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), &error_fatal); + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->i2c[i]); + memory_region_add_subregion(system_memory, + s->memmap[TT_ATL_I2C0 + i].base, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i)); + } + i2c_slave_create_simple(s->i2c[0].bus, "ds1338", 0x6f); + i2c_slave_create_simple(s->i2c[0].bus, "tmp105", 0x48); + + /* Load or create device tree */ + if (machine->dtb) { + machine->fdt = load_device_tree(machine->dtb, &s->fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + } 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"; + mc->init = tt_atlantis_machine_init; + mc->max_cpus = 8; + mc->default_cpus = 8; + mc->default_ram_size = 2 * GiB; + mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->pci_allow_0_address = true; + mc->default_ram_id = "tt_atlantis.ram"; +} + +static const TypeInfo tt_atlantis_machine_typeinfo = { + .name = MACHINE_TYPE_NAME("tt-atlantis"), + .parent = TYPE_MACHINE, + .class_init = tt_atlantis_machine_class_init, + .instance_size = sizeof(TTAtlantisState), +}; + +static void tt_atlantis_machine_init_register_types(void) +{ + type_register_static(&tt_atlantis_machine_typeinfo); +} + +type_init(tt_atlantis_machine_init_register_types) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index f1406cb68339b..8da60fe127c4a 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -40,6 +40,8 @@ #include "qemu/error-report.h" #include "system/reset.h" +#include "aia.h" + #define ACPI_BUILD_TABLE_SIZE 0x20000 #define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index)) @@ -142,6 +144,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,7 +154,7 @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count, 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))); @@ -467,10 +470,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"); } @@ -487,15 +493,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); @@ -574,7 +580,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 17909206c7ef8..27e9ffd7bb70f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -59,6 +59,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) { @@ -509,17 +511,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) @@ -1302,68 +1293,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; @@ -1465,7 +1394,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]); @@ -1488,8 +1420,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); @@ -1624,9 +1554,14 @@ 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, + 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); } /* Try to use different IRQCHIP instance based device type */ @@ -1756,6 +1691,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 a95fd6174fdc6..431fe21b762e2 100644 --- a/hw/riscv/xiangshan_kmh.c +++ b/hw/riscv/xiangshan_kmh.c @@ -166,6 +166,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, @@ -177,13 +178,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 0000000000000..b44e6e22d65ce --- /dev/null +++ b/include/hw/i2c/designware_i2c.h @@ -0,0 +1,101 @@ +/* + * DesignWare I2C Module. + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef DESIGNWARE_I2C_H +#define DESIGNWARE_I2C_H + +#include "hw/i2c/i2c.h" +#include "hw/irq.h" +#include "hw/sysbus.h" + +/* Size of the FIFO buffers. */ +#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; + +/* + * struct DesignWareI2CState - DesignWare I2C device state. + * @bus: The underlying I2C Bus + * @irq: GIC interrupt line to fire on events + * @ic_con: : I2C control register + * @ic_tar: I2C target address register + * @ic_sar: I2C slave address register + * @ic_ss_scl_hcnt: Standard speed i2c clock scl high count register + * @ic_ss_scl_lcnt: Standard speed i2c clock scl low count register + * @ic_fs_scl_hcnt: Fast mode or fast mode plus i2c clock scl high count + * register + * @ic_fs_scl_lcnt:Fast mode or fast mode plus i2c clock scl low count + * register + * @ic_intr_mask: I2C Interrupt Mask Register + * @ic_raw_intr_stat: I2C raw interrupt status register + * @ic_rx_tl: I2C receive FIFO threshold register + * @ic_tx_tl: I2C transmit FIFO threshold register + * @ic_enable: I2C enable register + * @ic_status: I2C status register + * @ic_txflr: I2C transmit fifo level register + * @ic_rxflr: I2C receive fifo level register + * @ic_sda_hold: I2C SDA hold time length register + * @ic_tx_abrt_source: The I2C transmit abort source register + * @ic_sda_setup: I2C SDA setup register + * @ic_enable_status: I2C enable status register + * @ic_fs_spklen: I2C SS, FS or FM+ spike suppression limit + * @ic_comp_param_1: Component parameter register + * @ic_comp_version: I2C component version register + * @ic_comp_type: I2C component type register + * @rx_fifo: The FIFO buffer for receiving in FIFO mode. + * @rx_cur: The current position of rx_fifo. + * @status: The current status of the SMBus. + */ +typedef struct DesignWareI2CState { + SysBusDevice parent; + + MemoryRegion iomem; + + I2CBus *bus; + qemu_irq irq; + + uint32_t ic_con; + uint32_t ic_tar; + uint32_t ic_sar; + uint32_t ic_ss_scl_hcnt; + uint32_t ic_ss_scl_lcnt; + uint32_t ic_fs_scl_hcnt; + uint32_t ic_fs_scl_lcnt; + uint32_t ic_intr_mask; + uint32_t ic_raw_intr_stat; + uint32_t ic_rx_tl; + uint32_t ic_tx_tl; + uint32_t ic_enable; + uint32_t ic_status; + uint32_t ic_txflr; + uint32_t ic_rxflr; + uint32_t ic_sda_hold; + uint32_t ic_tx_abrt_source; + uint32_t ic_sda_setup; + uint32_t ic_enable_status; + uint32_t ic_fs_spklen; + uint32_t ic_comp_param_1; + uint32_t ic_comp_version; + uint32_t ic_comp_type; + + uint8_t rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE]; + uint8_t rx_cur; + + DesignWareI2CStatus status; +} DesignWareI2CState; + +#define TYPE_DESIGNWARE_I2C "designware-i2c" +#define DESIGNWARE_I2C(obj) OBJECT_CHECK(DesignWareI2CState, (obj), \ + TYPE_DESIGNWARE_I2C) + +#endif /* DESIGNWARE_I2C_H */ diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index 51b0e13bd3ea1..d26302d3e987b 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 start, hwaddr size); hwaddr 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, + RISCVBootInfo *info, + const char *firmware_filename, hwaddr *firmware_load_addr, symbol_fn_t sym_cb); void riscv_load_kernel(MachineState *machine, @@ -68,6 +78,8 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts hwaddr rom_base, hwaddr rom_size, uint64_t kernel_entry, uint64_t fdt_load_addr); +void riscv_setup_halting_payload(MachineState *machine, + RISCVBootInfo *info, hwaddr addr); void riscv_rom_copy_firmware_info(MachineState *machine, RISCVHartArrayState *harts, hwaddr rom_base, diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h new file mode 100644 index 0000000000000..bbe4cf2b40341 --- /dev/null +++ b/include/hw/riscv/tt_atlantis.h @@ -0,0 +1,95 @@ +/* + * Tenstorrent Atlantis RISC-V System on Chip + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright 2025 Tenstorrent, Joel Stanley + */ + +#ifndef HW_RISCV_TT_ATLANTIS_H +#define HW_RISCV_TT_ATLANTIS_H + +#include "hw/boards.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/sysbus.h" +#include "hw/block/flash.h" +#include "hw/intc/riscv_imsic.h" +#include "hw/i2c/designware_i2c.h" + +#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis") +OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE) + +#define TT_ATL_NUM_I2C 4 + +struct TTAtlantisState { + /*< private >*/ + MachineState parent; + + /*< public >*/ + Notifier machine_done; + DeviceState *platform_bus_dev; + FWCfgState *fw_cfg; + const MemMapEntry *memmap; + + RISCVHartArrayState soc; + DeviceState *irqchip; + GPEXHost gpex_host; + DesignWareI2CState i2c[TT_ATL_NUM_I2C]; + + int fdt_size; + int aia_guests; /* TODO: This should be hard coded once known */ +}; + +enum { + TT_ATL_SYSCON_IRQ = 10, + 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_UART0_IRQ = 38, + TT_ATL_UART1_IRQ = 39, + TT_ATL_UART2_IRQ = 40, + TT_ATL_UART3_IRQ = 41, + TT_ATL_UART4_IRQ = 42, + TT_ATL_PCIE0_INTA_IRQ = 96, +}; + +enum { + TT_ATL_ACLINT, + TT_ATL_BOOTROM, + TT_ATL_DDR_LO, + TT_ATL_DDR_HI, + TT_ATL_FW_CFG, + TT_ATL_I2C0, + TT_ATL_I2C1, + TT_ATL_I2C2, + TT_ATL_I2C3, + TT_ATL_MAPLIC, + TT_ATL_MIMSIC, + TT_ATL_PCIE_ECAM0, + TT_ATL_PCIE_ECAM1, + TT_ATL_PCIE_ECAM2, + TT_ATL_PCIE_MMIO0, + TT_ATL_PCIE_PIO0, + TT_ATL_PCIE_MMIO0_32, + TT_ATL_PCIE_MMIO0_64, + TT_ATL_PCIE_MMIO1, + TT_ATL_PCIE_PIO1, + TT_ATL_PCIE_MMIO1_32, + TT_ATL_PCIE_MMIO1_64, + TT_ATL_PCIE_MMIO2, + TT_ATL_PCIE_PIO2, + TT_ATL_PCIE_MMIO2_32, + TT_ATL_PCIE_MMIO2_64, + TT_ATL_PCI_MMU_CFG, + TT_ATL_PRCM, + TT_ATL_SAPLIC, + TT_ATL_SIMSIC, + TT_ATL_SYSCON, + TT_ATL_TIMER, + TT_ATL_UART0, + TT_ATL_WDT0, +}; + +#endif diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 7b4c2c8b7de34..bf6c768c2d509 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 { @@ -102,12 +103,6 @@ enum { #define VIRT_PLATFORM_BUS_NUM_IRQS 32 -#define VIRT_IRQCHIP_NUM_MSIS 255 -#define VIRT_IRQCHIP_NUM_SOURCES 96 -#define VIRT_IRQCHIP_NUM_PRIO_BITS 3 -#define VIRT_IRQCHIP_MAX_GUESTS_BITS 3 -#define VIRT_IRQCHIP_MAX_GUESTS ((1U << VIRT_IRQCHIP_MAX_GUESTS_BITS) - 1U) - #define VIRT_PLIC_PRIORITY_BASE 0x00 #define VIRT_PLIC_PENDING_BASE 0x1000 #define VIRT_PLIC_ENABLE_BASE 0x2000 @@ -135,28 +130,5 @@ 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 - * namely ACLINT, PLIC, APLIC, and IMSIC depend on number of Sockets, - * number of CPUs, and number of IMSIC guest files. - * - * Various limits defined by VIRT_SOCKETS_MAX_BITS, VIRT_CPUS_MAX_BITS, - * and VIRT_IRQCHIP_MAX_GUESTS_BITS are tuned for maximum utilization - * of virt machine physical address space. - */ - -#define VIRT_IMSIC_GROUP_MAX_SIZE (1U << IMSIC_MMIO_GROUP_MIN_SHIFT) -#if VIRT_IMSIC_GROUP_MAX_SIZE < \ - IMSIC_GROUP_SIZE(VIRT_CPUS_MAX_BITS, VIRT_IRQCHIP_MAX_GUESTS_BITS) -#error "Can't accommodate single IMSIC group in address space" -#endif - -#define VIRT_IMSIC_MAX_SIZE (VIRT_SOCKETS_MAX * \ - VIRT_IMSIC_GROUP_MAX_SIZE) -#if 0x4000000 < VIRT_IMSIC_MAX_SIZE -#error "Can't accommodate all IMSIC groups in address space" -#endif #endif diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 73d4280d7c84a..b8b64284a281a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3169,6 +3169,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, @@ -3182,11 +3183,12 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_ssaia = true, .cfg.ext_sscofpmf = true, .cfg.ext_sstc = true, - .cfg.ext_svade = true, .cfg.ext_svinval = true, .cfg.ext_svnapot = true, .cfg.ext_svpbmt = true, + .cfg.mvendorid = TENSTORRENT_VENDOR_ID, + .cfg.max_satp_mode = VM_1_10_SV57, ), diff --git a/target/riscv/cpu_vendorid.h b/target/riscv/cpu_vendorid.h index 96b6b9c2cb58e..6a5c2491b9231 100644 --- a/target/riscv/cpu_vendorid.h +++ b/target/riscv/cpu_vendorid.h @@ -7,4 +7,6 @@ #define VEYRON_V1_MIMPID 0x111 #define VEYRON_V1_MVENDORID 0x61f +#define TENSTORRENT_VENDOR_ID 0x7a1 + #endif /* TARGET_RISCV_CPU_VENDORID_H */ diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build index c1704d92751be..52d5cb117a39f 100644 --- a/tests/functional/riscv64/meson.build +++ b/tests/functional/riscv64/meson.build @@ -11,5 +11,6 @@ tests_riscv64_system_quick = [ tests_riscv64_system_thorough = [ 'sifive_u', + 'tt_atlantis', 'tuxrun', ] diff --git a/tests/functional/riscv64/test_opensbi.py b/tests/functional/riscv64/test_opensbi.py index d077e40f42780..0f8beb7e7a8c0 100755 --- a/tests/functional/riscv64/test_opensbi.py +++ b/tests/functional/riscv64/test_opensbi.py @@ -28,6 +28,10 @@ def test_riscv_sifive_u(self): 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 0000000000000..fb6943509c284 --- /dev/null +++ b/tests/functional/riscv64/test_tt_atlantis.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Tenstorrent Atlantis machine +# and checks the console +# +# Copyright (c) Linaro Ltd. +# +# Author: +# Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import Asset, LinuxKernelTest +from qemu_test import skipIfMissingCommands + + +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 ' + 'root=/dev/vda ') + + 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) + + os.remove(rootfs_path) + + def test_riscv64_tt_atlantis(self): + self.do_test_riscv64_tt_atlantis(False) + + def test_riscv64_tt_atlantis_disk(self): + self.do_test_riscv64_tt_atlantis(True) + + +if __name__ == '__main__': + LinuxKernelTest.main()