RISC-V PR for 10.0

* Correct the validness check of iova
 * Fix APLIC in_clrip and clripnum write emulation
 * Support riscv-iommu-sys device
 * Add Tenstorrent Ascalon CPU
 * Add AIA userspace irqchip_split support
 * Add Microblaze V generic board
 * Upgrade ACPI SPCR table to support SPCR table revision 4 format
 * Remove tswap64() calls from HTIF
 * Support 64-bit address of initrd
 * Introduce svukte ISA extension
 * Support ssstateen extension
 * Support for RV64 Xiangshan Nanhu CPU
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmdkzjgACgkQr3yVEwxT
 gBOcyA//e0XhAQciQglCZZCfINdOyI8qSh+P2K0qtrXZ4VERHEMp7UoD5CQr2cZv
 h8ij1EkatXCwukVELx0rNckxG33bEFgG1oESnQSrwGE0Iu4csNW24nK5WlUS0/r+
 A5oD2wtzEF+cbhTKrVSDBN/PvlnWTKGEoJRkuXWfz5d4uR9eyQhfED0S2j36lNEC
 X1x/OZoKM89XuXtOFe9g55Z5UNzAatcdTISozL0FydiPh7QeVjTLHh28/tt559MX
 7v5aJFlQuZ78z1mIHkZmPSorSrJ0zqhkP6NWe1ae06oMgzwRQQhYLppDILV4ZgUF
 3mSDRoXmBycQXiYNPcHep3LdXfvxr+PpWHSevx8gH1jwm93On7Y/H7Uol6TDXzfC
 mrFjalfV5tzrD90ZvB+s5bCMF1q5Z8Dlj0pYF9aN9P1ILoWy3dndFAPJB6uKKDP7
 Qd4qOQ3dVyHAX9jLmVkB6QvAV/vTDrYTsAxaF/EaoLOy0IoKhjTvgda3XzE1MFKA
 gVafLluADIfSEdqa2QR2ExL8d1SZVoiObCp5TMLRer0HIpg/vQZwjfdbo4BgQKL3
 7Q6wBxcZUNqrFgspXjm5WFIrdk2rfS/79OmvpNM6SZaK6BnklntdJHJHtAWujGsm
 EM310AUFpHMp2h6Nqnemb3qr5l4d20KSt8DhoPAUq1IE59Kb8XY=
 =0iQW
 -----END PGP SIGNATURE-----

Merge tag 'pull-riscv-to-apply-20241220' of https://github.com/alistair23/qemu into staging

RISC-V PR for 10.0

* Correct the validness check of iova
* Fix APLIC in_clrip and clripnum write emulation
* Support riscv-iommu-sys device
* Add Tenstorrent Ascalon CPU
* Add AIA userspace irqchip_split support
* Add Microblaze V generic board
* Upgrade ACPI SPCR table to support SPCR table revision 4 format
* Remove tswap64() calls from HTIF
* Support 64-bit address of initrd
* Introduce svukte ISA extension
* Support ssstateen extension
* Support for RV64 Xiangshan Nanhu CPU

 # -----BEGIN PGP SIGNATURE-----
 #
 # iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmdkzjgACgkQr3yVEwxT
 # gBOcyA//e0XhAQciQglCZZCfINdOyI8qSh+P2K0qtrXZ4VERHEMp7UoD5CQr2cZv
 # h8ij1EkatXCwukVELx0rNckxG33bEFgG1oESnQSrwGE0Iu4csNW24nK5WlUS0/r+
 # A5oD2wtzEF+cbhTKrVSDBN/PvlnWTKGEoJRkuXWfz5d4uR9eyQhfED0S2j36lNEC
 # X1x/OZoKM89XuXtOFe9g55Z5UNzAatcdTISozL0FydiPh7QeVjTLHh28/tt559MX
 # 7v5aJFlQuZ78z1mIHkZmPSorSrJ0zqhkP6NWe1ae06oMgzwRQQhYLppDILV4ZgUF
 # 3mSDRoXmBycQXiYNPcHep3LdXfvxr+PpWHSevx8gH1jwm93On7Y/H7Uol6TDXzfC
 # mrFjalfV5tzrD90ZvB+s5bCMF1q5Z8Dlj0pYF9aN9P1ILoWy3dndFAPJB6uKKDP7
 # Qd4qOQ3dVyHAX9jLmVkB6QvAV/vTDrYTsAxaF/EaoLOy0IoKhjTvgda3XzE1MFKA
 # gVafLluADIfSEdqa2QR2ExL8d1SZVoiObCp5TMLRer0HIpg/vQZwjfdbo4BgQKL3
 # 7Q6wBxcZUNqrFgspXjm5WFIrdk2rfS/79OmvpNM6SZaK6BnklntdJHJHtAWujGsm
 # EM310AUFpHMp2h6Nqnemb3qr5l4d20KSt8DhoPAUq1IE59Kb8XY=
 # =0iQW
 # -----END PGP SIGNATURE-----
 # gpg: Signature made Thu 19 Dec 2024 20:54:00 EST
 # gpg:                using RSA key 6AE902B6A7CA877D6D659296AF7C95130C538013
 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [unknown]
 # gpg: WARNING: This key is not certified with a trusted signature!
 # gpg:          There is no indication that the signature belongs to the owner.
 # Primary key fingerprint: 6AE9 02B6 A7CA 877D 6D65  9296 AF7C 9513 0C53 8013

* tag 'pull-riscv-to-apply-20241220' of https://github.com/alistair23/qemu: (39 commits)
  target/riscv: add support for RV64 Xiangshan Nanhu CPU
  target/riscv: add ssstateen
  target/riscv/tcg: hide warn for named feats when disabling via priv_ver
  target/riscv: Include missing headers in 'internals.h'
  target/riscv: Include missing headers in 'vector_internals.h'
  target/riscv: Check svukte is not enabled in RV32
  target/riscv: Expose svukte ISA extension
  target/riscv: Check memory access to meet svukte rule
  target/riscv: Support hstatus[HUKTE] bit when svukte extension is enabled
  target/riscv: Support senvcfg[UKTE] bit when svukte extension is enabled
  target/riscv: Add svukte extension capability variable
  hw/riscv: Add the checking if DTB overlaps to kernel or initrd
  hw/riscv: Add a new struct RISCVBootInfo
  hw/riscv: Support to load DTB after 3GB memory on 64-bit system.
  hw/char/riscv_htif: Clarify MemoryRegionOps expect 32-bit accesses
  hw/char/riscv_htif: Explicit little-endian implementation
  MAINTAINERS: Cover RISC-V HTIF interface
  tests/qtest/bios-tables-test: Update virt SPCR golden reference for RISC-V
  hw/acpi: Upgrade ACPI SPCR table to support SPCR table revision 4 format
  qtest: allow SPCR acpi table changes
  ...

Conflicts:
  target/riscv/cpu.c

  Merge conflict with DEFINE_PROP_END_OF_LIST() removal. No Property
  array terminator is needed anymore.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2024-12-21 08:13:16 -05:00
commit 60a07d4a6e
46 changed files with 1381 additions and 178 deletions

View file

@ -324,8 +324,10 @@ S: Supported
F: configs/targets/riscv*
F: docs/system/target-riscv.rst
F: target/riscv/
F: hw/char/riscv_htif.c
F: hw/riscv/
F: hw/intc/riscv*
F: include/hw/char/riscv_htif.h
F: include/hw/riscv/
F: linux-user/host/riscv32/
F: linux-user/host/riscv64/
@ -1626,6 +1628,12 @@ F: docs/system/riscv/sifive_u.rst
F: hw/*/*sifive*.c
F: include/hw/*/*sifive*.h
AMD Microblaze-V Generic Board
M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
S: Maintained
F: hw/riscv/microblaze-v-generic.c
F: docs/system/riscv/microblaze-v-generic.rst
RX Machines
-----------
rx-gdbsim

View file

@ -37,3 +37,4 @@ guest hardware that is specific to QEMU.
rapl-msr
rocker
riscv-iommu
riscv-aia

83
docs/specs/riscv-aia.rst Normal file
View file

@ -0,0 +1,83 @@
.. _riscv-aia:
RISC-V AIA support for RISC-V machines
======================================
AIA (Advanced Interrupt Architecture) support is implemented in the ``virt``
RISC-V machine for TCG and KVM accelerators.
The support consists of two main modes:
- "aia=aplic": adds one or more APLIC (Advanced Platform Level Interrupt Controller)
devices
- "aia=aplic-imsic": adds one or more APLIC device and an IMSIC (Incoming MSI
Controller) device for each CPU
From an user standpoint, these modes will behave the same regardless of the accelerator
used. From a developer standpoint the accelerator settings will change what it being
emulated in userspace versus what is being emulated by an in-kernel irqchip.
When running TCG, all controllers are emulated in userspace, including machine mode
(m-mode) APLIC and IMSIC (when applicable).
When running KVM:
- no m-mode is provided, so there is no m-mode APLIC or IMSIC emulation regardless of
the AIA mode chosen
- with "aia=aplic", s-mode APLIC will be emulated by userspace
- with "aia=aplic-imsic" there are two possibilities. If no additional KVM option
is provided there will be no APLIC or IMSIC emulation in userspace, and the virtual
machine will use the provided in-kernel APLIC and IMSIC controllers. If the user
chooses to use the irqchip in split mode via "-accel kvm,kernel-irqchip=split",
s-mode APLIC will be emulated while using the s-mode IMSIC from the irqchip
The following table summarizes how the AIA and accelerator options defines what
we will emulate in userspace:
.. list-table:: How AIA and accel options changes controller emulation
:widths: 25 25 25 25 25 25 25
:header-rows: 1
* - Accel
- Accel props
- AIA type
- APLIC m-mode
- IMSIC m-mode
- APLIC s-mode
- IMSIC s-mode
* - tcg
- ---
- aplic
- emul
- n/a
- emul
- n/a
* - tcg
- ---
- aplic-imsic
- emul
- emul
- emul
- emul
* - kvm
- ---
- aplic
- n/a
- n/a
- emul
- n/a
* - kvm
- none
- aplic-imsic
- n/a
- n/a
- in-kernel
- in-kernel
* - kvm
- irqchip=split
- aplic-imsic
- n/a
- n/a
- emul
- in-kernel

View file

@ -6,9 +6,9 @@ RISC-V IOMMU support for RISC-V machines
QEMU implements a RISC-V IOMMU emulation based on the RISC-V IOMMU spec
version 1.0 `iommu1.0`_.
The emulation includes a PCI reference device, riscv-iommu-pci, that QEMU
RISC-V boards can use. The 'virt' RISC-V machine is compatible with this
device.
The emulation includes a PCI reference device (riscv-iommu-pci) and a platform
bus device (riscv-iommu-sys) that QEMU RISC-V boards can use. The 'virt'
RISC-V machine is compatible with both devices.
riscv-iommu-pci reference device
--------------------------------
@ -83,6 +83,30 @@ Several options are available to control the capabilities of the device, namely:
- "s-stage": enable s-stage support
- "g-stage": enable g-stage support
riscv-iommu-sys device
----------------------
This device implements the RISC-V IOMMU emulation as a platform bus device that
RISC-V boards can use.
For the 'virt' board the device is disabled by default. To enable it use the
'iommu-sys' machine option:
.. code-block:: bash
$ qemu-system-riscv64 -M virt,iommu-sys=on (...)
There is no options to configure the capabilities of this device in the 'virt'
board using the QEMU command line. The device is configured with the following
riscv-iommu options:
- "ioatc-limit": default value (2Mb)
- "intremap": enabled
- "ats": enabled
- "off": on (DMA disabled)
- "s-stage": enabled
- "g-stage": enabled
.. _iommu1.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0/riscv-iommu.pdf
.. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/

View file

@ -0,0 +1,42 @@
Microblaze-V generic board (``amd-microblaze-v-generic``)
=========================================================
The AMD MicroBlaze™ V processor is a soft-core RISC-V processor IP for AMD
adaptive SoCs and FPGAs. The MicroBlaze™ V processor is based on the 32-bit (or
64-bit) RISC-V instruction set architecture (ISA) and contains interfaces
compatible with the classic MicroBlaze™ V processor (i.e it is a drop in
replacement for the classic MicroBlaze™ processor in existing RTL designs).
More information can be found in below document.
https://docs.amd.com/r/en-US/ug1629-microblaze-v-user-guide/MicroBlaze-V-Architecture
The MicroBlaze™ V generic board in QEMU has following supported devices:
- timer
- uartlite
- uart16550
- emaclite
- timer2
- axi emac
- axi dma
The MicroBlaze™ V core in QEMU has the following configuration:
- RV32I base integer instruction set
- "Zicsr" Control and Status register instructions
- "Zifencei" instruction-fetch
- Extensions: m, a, f, c
Running
"""""""
Below is an example command line for launching mainline U-boot
(xilinx_mbv32_defconfig) on the Microblaze-V generic board.
.. code-block:: bash
$ qemu-system-riscv32 -M amd-microblaze-v-generic \
-display none \
-device loader,addr=0x80000000,file=u-boot-spl.bin,cpu-num=0 \
-device loader,addr=0x80200000,file=u-boot.img \
-serial mon:stdio \
-device loader,addr=0x83000000,file=system.dtb \
-m 2g

View file

@ -94,6 +94,12 @@ command line:
$ qemu-system-riscv64 -M virt -device riscv-iommu-pci (...)
It also has support for the riscv-iommu-sys platform device:
.. code-block:: bash
$ qemu-system-riscv64 -M virt,iommu-sys=on (...)
Refer to :ref:`riscv-iommu` for more information on how the RISC-V IOMMU support
works.
@ -123,12 +129,23 @@ The following machine-specific options are supported:
MSIs. When not specified, this option is assumed to be "none" which selects
SiFive PLIC to handle wired interrupts.
This option also interacts with '-accel kvm'. When using "aia=aplic-imsic"
with KVM, it is possible to set the use of the kernel irqchip in split mode
by using "-accel kvm,kernel-irqchip=split". In this case the ``virt`` machine
will emulate the APLIC controller instead of using the APLIC controller from
the irqchip. See :ref:`riscv-aia` for more details on all available AIA
modes.
- aia-guests=nnn
The number of per-HART VS-level AIA IMSIC pages to be emulated for a guest
having AIA IMSIC (i.e. "aia=aplic-imsic" selected). When not specified,
the default number of per-HART VS-level AIA IMSIC pages is 0.
- iommu-sys=[on|off]
Enables the riscv-iommu-sys platform device. Defaults to 'off'.
Running Linux kernel
--------------------

View file

@ -66,6 +66,7 @@ undocumented; you can get a complete list by running
.. toctree::
:maxdepth: 1
riscv/microblaze-v-generic
riscv/microchip-icicle-kit
riscv/shakti-c
riscv/sifive_u

View file

@ -2078,7 +2078,7 @@ static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags,
void build_spcr(GArray *table_data, BIOSLinker *linker,
const AcpiSpcrData *f, const uint8_t rev,
const char *oem_id, const char *oem_table_id)
const char *oem_id, const char *oem_table_id, const char *name)
{
AcpiTable table = { .sig = "SPCR", .rev = rev, .oem_id = oem_id,
.oem_table_id = oem_table_id };
@ -2124,9 +2124,21 @@ void build_spcr(GArray *table_data, BIOSLinker *linker,
build_append_int_noprefix(table_data, f->pci_flags, 4);
/* PCI Segment */
build_append_int_noprefix(table_data, f->pci_segment, 1);
/* Reserved */
build_append_int_noprefix(table_data, 0, 4);
if (rev < 4) {
/* Reserved */
build_append_int_noprefix(table_data, 0, 4);
} else {
/* UartClkFreq */
build_append_int_noprefix(table_data, f->uart_clk_freq, 4);
/* PreciseBaudrate */
build_append_int_noprefix(table_data, f->precise_baudrate, 4);
/* NameSpaceStringLength */
build_append_int_noprefix(table_data, f->namespace_string_length, 2);
/* NameSpaceStringOffset */
build_append_int_noprefix(table_data, f->namespace_string_offset, 2);
/* NamespaceString[] */
g_array_append_vals(table_data, name, f->namespace_string_length);
}
acpi_table_end(linker, &table);
}
/*

View file

@ -463,8 +463,12 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
.pci_flags = 0,
.pci_segment = 0,
};
build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id);
/*
* Passing NULL as the SPCR Table for Revision 2 doesn't support
* NameSpaceString.
*/
build_spcr(table_data, linker, &serial, 2, vms->oem_id, vms->oem_table_id,
NULL);
}
/*

View file

@ -29,7 +29,7 @@
#include "qemu/timer.h"
#include "qemu/error-report.h"
#include "exec/address-spaces.h"
#include "exec/tswap.h"
#include "qemu/bswap.h"
#include "sysemu/dma.h"
#include "sysemu/runstate.h"
@ -212,11 +212,11 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written)
} else {
uint64_t syscall[8];
cpu_physical_memory_read(payload, syscall, sizeof(syscall));
if (tswap64(syscall[0]) == PK_SYS_WRITE &&
tswap64(syscall[1]) == HTIF_DEV_CONSOLE &&
tswap64(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) {
if (le64_to_cpu(syscall[0]) == PK_SYS_WRITE &&
le64_to_cpu(syscall[1]) == HTIF_DEV_CONSOLE &&
le64_to_cpu(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) {
uint8_t ch;
cpu_physical_memory_read(tswap64(syscall[2]), &ch, 1);
cpu_physical_memory_read(le64_to_cpu(syscall[2]), &ch, 1);
/*
* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks
@ -324,6 +324,11 @@ static void htif_mm_write(void *opaque, hwaddr addr,
static const MemoryRegionOps htif_mm_ops = {
.read = htif_mm_read,
.write = htif_mm_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
},
};
HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr,

View file

@ -32,6 +32,7 @@
#include "target/riscv/cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/tcg.h"
#include "kvm/kvm_riscv.h"
#include "migration/vmstate.h"
@ -154,11 +155,38 @@
* KVM AIA only supports APLIC MSI, fallback to QEMU emulation if we want to use
* APLIC Wired.
*/
static bool is_kvm_aia(bool msimode)
bool riscv_is_kvm_aia_aplic_imsic(bool msimode)
{
return kvm_irqchip_in_kernel() && msimode;
}
bool riscv_use_emulated_aplic(bool msimode)
{
#ifdef CONFIG_KVM
if (tcg_enabled()) {
return true;
}
if (!riscv_is_kvm_aia_aplic_imsic(msimode)) {
return true;
}
return kvm_kernel_irqchip_split();
#else
return true;
#endif
}
void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr)
{
#ifdef CONFIG_KVM
if (riscv_use_emulated_aplic(aplic->msimode)) {
aplic->kvm_msicfgaddr = extract64(addr, 0, 32);
aplic->kvm_msicfgaddrH = extract64(addr, 32, 32);
}
#endif
}
static bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic,
uint32_t irq)
{
@ -248,9 +276,12 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic,
if ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) ||
(sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) {
if (!aplic->msimode || (aplic->msimode && !pending)) {
if (!aplic->msimode) {
return;
}
if (aplic->msimode && !pending) {
goto noskip_write_pending;
}
if ((aplic->state[irq] & APLIC_ISTATE_INPUT) &&
(sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) {
return;
@ -261,6 +292,7 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic,
}
}
noskip_write_pending:
riscv_aplic_set_pending_raw(aplic, irq, pending);
}
@ -359,13 +391,16 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic,
uint32_t lhxs, lhxw, hhxs, hhxw, group_idx, msicfgaddr, msicfgaddrH;
aplic_m = aplic;
while (aplic_m && !aplic_m->mmode) {
aplic_m = aplic_m->parent;
}
if (!aplic_m) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n",
__func__);
return;
if (!aplic->kvm_splitmode) {
while (aplic_m && !aplic_m->mmode) {
aplic_m = aplic_m->parent;
}
if (!aplic_m) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n",
__func__);
return;
}
}
if (aplic->mmode) {
@ -397,6 +432,11 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic,
addr |= (uint64_t)(guest_idx & APLIC_xMSICFGADDR_PPN_HART(lhxs));
addr <<= APLIC_xMSICFGADDR_PPN_SHIFT;
if (aplic->kvm_splitmode) {
addr |= aplic->kvm_msicfgaddr;
addr |= ((uint64_t)aplic->kvm_msicfgaddrH << 32);
}
address_space_stl_le(&address_space_memory, addr,
eiid, MEMTXATTRS_UNSPECIFIED, &result);
if (result != MEMTX_OK) {
@ -853,7 +893,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
uint32_t i;
RISCVAPLICState *aplic = RISCV_APLIC(dev);
if (!is_kvm_aia(aplic->msimode)) {
if (riscv_use_emulated_aplic(aplic->msimode)) {
aplic->bitfield_words = (aplic->num_irqs + 31) >> 5;
aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs);
aplic->state = g_new0(uint32_t, aplic->num_irqs);
@ -870,6 +910,10 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&aplic->mmio, OBJECT(dev), &riscv_aplic_ops,
aplic, TYPE_RISCV_APLIC, aplic->aperture_size);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &aplic->mmio);
if (kvm_enabled()) {
aplic->kvm_splitmode = true;
}
}
/*
@ -877,7 +921,7 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
* have IRQ lines delegated by their parent APLIC.
*/
if (!aplic->parent) {
if (kvm_enabled() && is_kvm_aia(aplic->msimode)) {
if (kvm_enabled() && !riscv_use_emulated_aplic(aplic->msimode)) {
qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs);
} else {
qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs);
@ -916,8 +960,8 @@ static const Property riscv_aplic_properties[] = {
static const VMStateDescription vmstate_riscv_aplic = {
.name = "riscv_aplic",
.version_id = 1,
.minimum_version_id = 1,
.version_id = 2,
.minimum_version_id = 2,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(domaincfg, RISCVAPLICState),
VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState),
@ -925,6 +969,8 @@ static const VMStateDescription vmstate_riscv_aplic = {
VMSTATE_UINT32(smsicfgaddr, RISCVAPLICState),
VMSTATE_UINT32(smsicfgaddrH, RISCVAPLICState),
VMSTATE_UINT32(genmsi, RISCVAPLICState),
VMSTATE_UINT32(kvm_msicfgaddr, RISCVAPLICState),
VMSTATE_UINT32(kvm_msicfgaddrH, RISCVAPLICState),
VMSTATE_VARRAY_UINT32(sourcecfg, RISCVAPLICState,
num_irqs, 0,
vmstate_info_uint32, uint32_t),
@ -1020,7 +1066,7 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size,
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
if (!is_kvm_aia(msimode)) {
if (riscv_use_emulated_aplic(msimode)) {
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
}

View file

@ -276,8 +276,12 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine)
};
lvms = LOONGARCH_VIRT_MACHINE(machine);
/*
* Passing NULL as the SPCR Table for Revision 2 doesn't support
* NameSpaceString.
*/
build_spcr(table_data, linker, &serial, 2, lvms->oem_id,
lvms->oem_table_id);
lvms->oem_table_id, NULL);
}
typedef

View file

@ -25,6 +25,14 @@ config MICROCHIP_PFSOC
select SIFIVE_PLIC
select UNIMP
config MICROBLAZE_V
bool
default y
depends on RISCV32 || RISCV64
select XILINX
select XILINX_AXI
select XILINX_ETHLITE
config OPENTITAN
bool
default y

View file

@ -67,9 +67,16 @@ char *riscv_plic_hart_config_string(int hart_count)
return g_strjoinv(",", (char **)vals);
}
target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts,
void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts)
{
info->kernel_size = 0;
info->initrd_size = 0;
info->is_32bit = riscv_is_32bit(harts);
}
target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info,
target_ulong firmware_end_addr) {
if (riscv_is_32bit(harts)) {
if (info->is_32bit) {
return QEMU_ALIGN_UP(firmware_end_addr, 4 * MiB);
} else {
return QEMU_ALIGN_UP(firmware_end_addr, 2 * MiB);
@ -175,7 +182,7 @@ target_ulong riscv_load_firmware(const char *firmware_filename,
exit(1);
}
static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry)
static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info)
{
const char *filename = machine->initrd_filename;
uint64_t mem_size = machine->ram_size;
@ -196,7 +203,7 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry)
* halfway into RAM, and for boards with 1GB of RAM or more we put
* the initrd at 512MB.
*/
start = kernel_entry + MIN(mem_size / 2, 512 * MiB);
start = info->image_low_addr + MIN(mem_size / 2, 512 * MiB);
size = load_ramdisk(filename, start, mem_size - start);
if (size == -1) {
@ -207,6 +214,9 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry)
}
}
info->initrd_start = start;
info->initrd_size = size;
/* Some RISC-V machines (e.g. opentitan) don't have a fdt. */
if (fdt) {
end = start + size;
@ -215,14 +225,14 @@ static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry)
}
}
target_ulong riscv_load_kernel(MachineState *machine,
RISCVHartArrayState *harts,
target_ulong kernel_start_addr,
bool load_initrd,
symbol_fn_t sym_cb)
void riscv_load_kernel(MachineState *machine,
RISCVBootInfo *info,
target_ulong kernel_start_addr,
bool load_initrd,
symbol_fn_t sym_cb)
{
const char *kernel_filename = machine->kernel_filename;
uint64_t kernel_load_base, kernel_entry;
ssize_t kernel_size;
void *fdt = machine->fdt;
g_assert(kernel_filename != NULL);
@ -234,21 +244,28 @@ target_ulong riscv_load_kernel(MachineState *machine,
* the (expected) load address load address. This allows kernels to have
* separate SBI and ELF entry points (used by FreeBSD, for example).
*/
if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL,
NULL, &kernel_load_base, NULL, NULL, 0,
EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) {
kernel_entry = kernel_load_base;
kernel_size = load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL,
&info->image_low_addr, &info->image_high_addr,
NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb);
if (kernel_size > 0) {
info->kernel_size = kernel_size;
goto out;
}
if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL,
NULL, NULL, NULL) > 0) {
kernel_size = load_uimage_as(kernel_filename, &info->image_low_addr,
NULL, NULL, NULL, NULL, NULL);
if (kernel_size > 0) {
info->kernel_size = kernel_size;
info->image_high_addr = info->image_low_addr + kernel_size;
goto out;
}
if (load_image_targphys_as(kernel_filename, kernel_start_addr,
current_machine->ram_size, NULL) > 0) {
kernel_entry = kernel_start_addr;
kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr,
current_machine->ram_size, NULL);
if (kernel_size > 0) {
info->kernel_size = kernel_size;
info->image_low_addr = kernel_start_addr;
info->image_high_addr = info->image_low_addr + kernel_size;
goto out;
}
@ -257,23 +274,21 @@ target_ulong riscv_load_kernel(MachineState *machine,
out:
/*
* For 32 bit CPUs 'kernel_entry' can be sign-extended by
* For 32 bit CPUs 'image_low_addr' can be sign-extended by
* load_elf_ram_sym().
*/
if (riscv_is_32bit(harts)) {
kernel_entry = extract64(kernel_entry, 0, 32);
if (info->is_32bit) {
info->image_low_addr = extract64(info->image_low_addr, 0, 32);
}
if (load_initrd && machine->initrd_filename) {
riscv_load_initrd(machine, kernel_entry);
riscv_load_initrd(machine, info);
}
if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) {
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
machine->kernel_cmdline);
}
return kernel_entry;
}
/*
@ -293,11 +308,12 @@ out:
* The FDT is fdt_packed() during the calculation.
*/
uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
MachineState *ms)
MachineState *ms, RISCVBootInfo *info)
{
int ret = fdt_pack(ms->fdt);
hwaddr dram_end, temp;
int fdtsize;
uint64_t dtb_start, dtb_start_limit;
/* Should only fail if we've built a corrupted tree */
g_assert(ret == 0);
@ -308,6 +324,17 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
exit(1);
}
if (info->initrd_size) {
/* If initrd is successfully loaded, place DTB after it. */
dtb_start_limit = info->initrd_start + info->initrd_size;
} else if (info->kernel_size) {
/* If only kernel is successfully loaded, place DTB after it. */
dtb_start_limit = info->image_high_addr;
} else {
/* Otherwise, do not check DTB overlapping */
dtb_start_limit = 0;
}
/*
* A dram_size == 0, usually from a MemMapEntry[].size element,
* means that the DRAM block goes all the way to ms->ram_size.
@ -317,13 +344,24 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
/*
* We should put fdt as far as possible to avoid kernel/initrd overwriting
* its content. But it should be addressable by 32 bit system as well.
* Thus, put it at an 2MB aligned address that less than fdt size from the
* end of dram or 3GB whichever is lesser.
* its content. But it should be addressable by 32 bit system as well in RV32.
* Thus, put it near to the end of dram in RV64, and put it near to the end
* of dram or 3GB whichever is lesser in RV32.
*/
temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end;
if (!info->is_32bit) {
temp = dram_end;
} else {
temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end;
}
return QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
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");
exit(1);
}
return dtb_start;
}
/*

View file

@ -10,6 +10,7 @@ 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_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_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c'))
riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
hw_arch += {'riscv': riscv_ss}

View file

@ -0,0 +1,184 @@
/*
* QEMU model of Microblaze V generic board.
*
* based on hw/microblaze/petalogix_ml605_mmu.c
*
* Copyright (c) 2011 Michal Simek <monstr@monstr.eu>
* Copyright (c) 2011 PetaLogix
* Copyright (c) 2009 Edgar E. Iglesias.
* Copyright (C) 2024, Advanced Micro Devices, Inc.
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Written by Sai Pavan Boddu <sai.pavan.boddu@amd.com
* and by Michal Simek <michal.simek@amd.com>.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h"
#include "cpu.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/char/serial-mm.h"
#include "exec/address-spaces.h"
#include "hw/char/xilinx_uartlite.h"
#include "hw/misc/unimp.h"
#define LMB_BRAM_SIZE (128 * KiB)
#define MEMORY_BASEADDR 0x80000000
#define INTC_BASEADDR 0x41200000
#define TIMER_BASEADDR 0x41c00000
#define TIMER_BASEADDR2 0x41c10000
#define UARTLITE_BASEADDR 0x40600000
#define ETHLITE_BASEADDR 0x40e00000
#define UART16550_BASEADDR 0x44a10000
#define AXIENET_BASEADDR 0x40c00000
#define AXIDMA_BASEADDR 0x41e00000
#define GPIO_BASEADDR 0x40000000
#define GPIO_BASEADDR2 0x40010000
#define GPIO_BASEADDR3 0x40020000
#define I2C_BASEADDR 0x40800000
#define QSPI_BASEADDR 0x44a00000
#define TIMER_IRQ 0
#define UARTLITE_IRQ 1
#define UART16550_IRQ 4
#define ETHLITE_IRQ 5
#define TIMER_IRQ2 6
#define AXIENET_IRQ 7
#define AXIDMA_IRQ1 8
#define AXIDMA_IRQ0 9
static void mb_v_generic_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
DeviceState *dev, *dma, *eth0;
Object *ds, *cs;
int i;
RISCVCPU *cpu;
hwaddr ddr_base = MEMORY_BASEADDR;
MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
qemu_irq irq[32];
MemoryRegion *sysmem = get_system_memory();
cpu = RISCV_CPU(object_new(machine->cpu_type));
object_property_set_bool(OBJECT(cpu), "h", false, NULL);
object_property_set_bool(OBJECT(cpu), "d", false, NULL);
qdev_realize(DEVICE(cpu), NULL, &error_abort);
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL,
"mb_v.lmb_bram", LMB_BRAM_SIZE,
&error_fatal);
memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram);
memory_region_init_ram(phys_ram, NULL, "mb_v.ram",
ram_size, &error_fatal);
memory_region_add_subregion(sysmem, ddr_base, phys_ram);
dev = qdev_new("xlnx.xps-intc");
qdev_prop_set_uint32(dev, "kind-of-intr",
1 << UARTLITE_IRQ);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
qdev_get_gpio_in(DEVICE(cpu), 11));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(dev, i);
}
/* Uartlite */
dev = qdev_new(TYPE_XILINX_UARTLITE);
qdev_prop_set_chr(dev, "chardev", serial_hd(0));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, UARTLITE_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[UARTLITE_IRQ]);
/* Full uart */
serial_mm_init(sysmem, UART16550_BASEADDR + 0x1000, 2,
irq[UART16550_IRQ], 115200, serial_hd(1),
DEVICE_LITTLE_ENDIAN);
/* 2 timers at irq 0 @ 100 Mhz. */
dev = qdev_new("xlnx.xps-timer");
qdev_prop_set_uint32(dev, "one-timer-only", 0);
qdev_prop_set_uint32(dev, "clock-frequency", 100000000);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]);
/* 2 timers at irq 3 @ 100 Mhz. */
dev = qdev_new("xlnx.xps-timer");
qdev_prop_set_uint32(dev, "one-timer-only", 0);
qdev_prop_set_uint32(dev, "clock-frequency", 100000000);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR2);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ2]);
/* Emaclite */
dev = qdev_new("xlnx.xps-ethernetlite");
qemu_configure_nic_device(dev, true, NULL);
qdev_prop_set_uint32(dev, "tx-ping-pong", 0);
qdev_prop_set_uint32(dev, "rx-ping-pong", 0);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]);
/* axi ethernet and dma initialization. */
eth0 = qdev_new("xlnx.axi-ethernet");
dma = qdev_new("xlnx.axi-dma");
/* FIXME: attach to the sysbus instead */
object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0));
object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma));
ds = object_property_get_link(OBJECT(dma),
"axistream-connected-target", NULL);
cs = object_property_get_link(OBJECT(dma),
"axistream-control-connected-target", NULL);
qemu_configure_nic_device(eth0, true, NULL);
qdev_prop_set_uint32(eth0, "rxmem", 0x1000);
qdev_prop_set_uint32(eth0, "txmem", 0x1000);
object_property_set_link(OBJECT(eth0), "axistream-connected", ds,
&error_abort);
object_property_set_link(OBJECT(eth0), "axistream-control-connected", cs,
&error_abort);
sysbus_realize_and_unref(SYS_BUS_DEVICE(eth0), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]);
ds = object_property_get_link(OBJECT(eth0),
"axistream-connected-target", NULL);
cs = object_property_get_link(OBJECT(eth0),
"axistream-control-connected-target", NULL);
qdev_prop_set_uint32(dma, "freqhz", 100000000);
object_property_set_link(OBJECT(dma), "axistream-connected", ds,
&error_abort);
object_property_set_link(OBJECT(dma), "axistream-control-connected", cs,
&error_abort);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dma), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR);
sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]);
sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]);
/* unimplemented devices */
create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000);
create_unimplemented_device("gpio2", GPIO_BASEADDR2, 0x10000);
create_unimplemented_device("gpio3", GPIO_BASEADDR3, 0x10000);
create_unimplemented_device("i2c", I2C_BASEADDR, 0x10000);
create_unimplemented_device("qspi", QSPI_BASEADDR, 0x10000);
}
static void mb_v_generic_machine_init(MachineClass *mc)
{
mc->desc = "AMD Microblaze-V generic platform";
mc->init = mb_v_generic_init;
mc->min_cpus = 1;
mc->max_cpus = 1;
mc->default_cpu_type = TYPE_RISCV_CPU_BASE;
mc->default_cpus = 1;
}
DEFINE_MACHINE("amd-microblaze-v-generic", mb_v_generic_machine_init)

View file

@ -519,8 +519,9 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
bool kernel_as_payload = false;
target_ulong firmware_end_addr, kernel_start_addr;
uint64_t kernel_entry;
uint32_t fdt_load_addr;
uint64_t fdt_load_addr;
DriveInfo *dinfo = drive_get(IF_SD, 0, 0);
RISCVBootInfo boot_info;
/* Sanity check on RAM size */
if (machine->ram_size < mc->default_ram_size) {
@ -615,17 +616,19 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
&firmware_load_addr, NULL);
riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
if (kernel_as_payload) {
kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus,
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus,
kernel_start_addr, true, NULL);
riscv_load_kernel(machine, &boot_info, kernel_start_addr,
true, NULL);
kernel_entry = boot_info.image_low_addr;
/* Compute the fdt load address in dram */
fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base,
memmap[MICROCHIP_PFSOC_DRAM_LO].size,
machine);
machine, &boot_info);
riscv_load_fdt(fdt_load_addr, machine->fdt);
/* Load the reset vector */

View file

@ -81,6 +81,7 @@ static void opentitan_machine_init(MachineState *machine)
OpenTitanState *s = OPENTITAN_MACHINE(machine);
const MemMapEntry *memmap = ibex_memmap;
MemoryRegion *sys_mem = get_system_memory();
RISCVBootInfo boot_info;
if (machine->ram_size != mc->default_ram_size) {
char *sz = size_to_str(mc->default_ram_size);
@ -102,8 +103,9 @@ static void opentitan_machine_init(MachineState *machine)
riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL);
}
riscv_boot_info_init(&boot_info, &s->soc.cpus);
if (machine->kernel_filename) {
riscv_load_kernel(machine, &s->soc.cpus,
riscv_load_kernel(machine, &boot_info,
memmap[IBEX_DEV_RAM].base,
false, NULL);
}

View file

@ -88,6 +88,12 @@ struct riscv_iommu_pq_record {
#define RISCV_IOMMU_CAP_PD17 BIT_ULL(39)
#define RISCV_IOMMU_CAP_PD20 BIT_ULL(40)
enum riscv_iommu_igs_modes {
RISCV_IOMMU_CAP_IGS_MSI = 0,
RISCV_IOMMU_CAP_IGS_WSI,
RISCV_IOMMU_CAP_IGS_BOTH
};
/* 5.4 Features control register (32bits) */
#define RISCV_IOMMU_REG_FCTL 0x0008
#define RISCV_IOMMU_FCTL_BE BIT(0)

View file

@ -31,6 +31,7 @@
#include "cpu_bits.h"
#include "riscv-iommu.h"
#include "riscv-iommu-bits.h"
#include "trace.h"
/* RISC-V IOMMU PCI Device Emulation */
#define RISCV_PCI_CLASS_SYSTEM_IOMMU 0x0806
@ -66,6 +67,12 @@ typedef struct RISCVIOMMUStatePci {
RISCVIOMMUState iommu; /* common IOMMU state */
} RISCVIOMMUStatePci;
struct RISCVIOMMUPciClass {
/*< public >*/
DeviceRealize parent_realize;
ResettablePhases parent_phases;
};
/* interrupt delivery callback */
static void riscv_iommu_pci_notify(RISCVIOMMUState *iommu, unsigned vector)
{
@ -155,6 +162,7 @@ static void riscv_iommu_pci_init(Object *obj)
qdev_alias_all_properties(DEVICE(iommu), obj);
iommu->icvec_avail_vectors = RISCV_IOMMU_PCI_ICVEC_VECTORS;
riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_MSI);
}
static const Property riscv_iommu_pci_properties[] = {
@ -165,10 +173,23 @@ static const Property riscv_iommu_pci_properties[] = {
DEFINE_PROP_UINT8("revision", RISCVIOMMUStatePci, revision, 0x01),
};
static void riscv_iommu_pci_reset_hold(Object *obj, ResetType type)
{
RISCVIOMMUStatePci *pci = RISCV_IOMMU_PCI(obj);
RISCVIOMMUState *iommu = &pci->iommu;
riscv_iommu_reset(iommu);
trace_riscv_iommu_pci_reset_hold(type);
}
static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.hold = riscv_iommu_pci_reset_hold;
k->realize = riscv_iommu_pci_realize;
k->exit = riscv_iommu_pci_exit;

255
hw/riscv/riscv-iommu-sys.c Normal file
View file

@ -0,0 +1,255 @@
/*
* QEMU emulation of an RISC-V IOMMU Platform Device
*
* Copyright (C) 2022-2023 Rivos Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "hw/irq.h"
#include "hw/pci/pci_bus.h"
#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qom/object.h"
#include "exec/exec-all.h"
#include "trace.h"
#include "riscv-iommu.h"
#define RISCV_IOMMU_SYSDEV_ICVEC_VECTORS 0x3333
#define RISCV_IOMMU_PCI_MSIX_VECTORS 5
/* RISC-V IOMMU System Platform Device Emulation */
struct RISCVIOMMUStateSys {
SysBusDevice parent;
uint64_t addr;
uint32_t base_irq;
DeviceState *irqchip;
RISCVIOMMUState iommu;
/* Wired int support */
qemu_irq irqs[RISCV_IOMMU_INTR_COUNT];
/* Memory Regions for MSIX table and pending bit entries. */
MemoryRegion msix_table_mmio;
MemoryRegion msix_pba_mmio;
uint8_t *msix_table;
uint8_t *msix_pba;
};
struct RISCVIOMMUSysClass {
/*< public >*/
DeviceRealize parent_realize;
ResettablePhases parent_phases;
};
static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
RISCVIOMMUStateSys *s = opaque;
g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE);
return pci_get_long(s->msix_table + addr);
}
static void msix_table_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
RISCVIOMMUStateSys *s = opaque;
g_assert(addr + size <= RISCV_IOMMU_PCI_MSIX_VECTORS * PCI_MSIX_ENTRY_SIZE);
pci_set_long(s->msix_table + addr, val);
}
static const MemoryRegionOps msix_table_mmio_ops = {
.read = msix_table_mmio_read,
.write = msix_table_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 8,
},
.impl = {
.max_access_size = 4,
},
};
static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
RISCVIOMMUStateSys *s = opaque;
return pci_get_long(s->msix_pba + addr);
}
static void msix_pba_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
}
static const MemoryRegionOps msix_pba_mmio_ops = {
.read = msix_pba_mmio_read,
.write = msix_pba_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 8,
},
.impl = {
.max_access_size = 4,
},
};
static void riscv_iommu_sysdev_init_msi(RISCVIOMMUStateSys *s,
uint32_t n_vectors)
{
RISCVIOMMUState *iommu = &s->iommu;
uint32_t table_size = table_size = n_vectors * PCI_MSIX_ENTRY_SIZE;
uint32_t table_offset = RISCV_IOMMU_REG_MSI_CONFIG;
uint32_t pba_size = QEMU_ALIGN_UP(n_vectors, 64) / 8;
uint32_t pba_offset = RISCV_IOMMU_REG_MSI_CONFIG + 256;
s->msix_table = g_malloc0(table_size);
s->msix_pba = g_malloc0(pba_size);
memory_region_init_io(&s->msix_table_mmio, OBJECT(s), &msix_table_mmio_ops,
s, "msix-table", table_size);
memory_region_add_subregion(&iommu->regs_mr, table_offset,
&s->msix_table_mmio);
memory_region_init_io(&s->msix_pba_mmio, OBJECT(s), &msix_pba_mmio_ops, s,
"msix-pba", pba_size);
memory_region_add_subregion(&iommu->regs_mr, pba_offset,
&s->msix_pba_mmio);
}
static void riscv_iommu_sysdev_send_MSI(RISCVIOMMUStateSys *s,
uint32_t vector)
{
uint8_t *table_entry = s->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
uint64_t msi_addr = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
uint32_t msi_data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
MemTxResult result;
address_space_stl_le(&address_space_memory, msi_addr,
msi_data, MEMTXATTRS_UNSPECIFIED, &result);
trace_riscv_iommu_sys_msi_sent(vector, msi_addr, msi_data, result);
}
static void riscv_iommu_sysdev_notify(RISCVIOMMUState *iommu,
unsigned vector)
{
RISCVIOMMUStateSys *s = container_of(iommu, RISCVIOMMUStateSys, iommu);
uint32_t fctl = riscv_iommu_reg_get32(iommu, RISCV_IOMMU_REG_FCTL);
if (fctl & RISCV_IOMMU_FCTL_WSI) {
qemu_irq_pulse(s->irqs[vector]);
trace_riscv_iommu_sys_irq_sent(vector);
return;
}
riscv_iommu_sysdev_send_MSI(s, vector);
}
static void riscv_iommu_sys_realize(DeviceState *dev, Error **errp)
{
RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(dev);
SysBusDevice *sysdev = SYS_BUS_DEVICE(s);
PCIBus *pci_bus;
qemu_irq irq;
qdev_realize(DEVICE(&s->iommu), NULL, errp);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iommu.regs_mr);
if (s->addr) {
sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, s->addr);
}
pci_bus = (PCIBus *) object_resolve_path_type("", TYPE_PCI_BUS, NULL);
if (pci_bus) {
riscv_iommu_pci_setup_iommu(&s->iommu, pci_bus, errp);
}
s->iommu.notify = riscv_iommu_sysdev_notify;
/* 4 IRQs are defined starting from s->base_irq */
for (int i = 0; i < RISCV_IOMMU_INTR_COUNT; i++) {
sysbus_init_irq(sysdev, &s->irqs[i]);
irq = qdev_get_gpio_in(s->irqchip, s->base_irq + i);
sysbus_connect_irq(sysdev, i, irq);
}
riscv_iommu_sysdev_init_msi(s, RISCV_IOMMU_PCI_MSIX_VECTORS);
}
static void riscv_iommu_sys_init(Object *obj)
{
RISCVIOMMUStateSys *s = RISCV_IOMMU_SYS(obj);
RISCVIOMMUState *iommu = &s->iommu;
object_initialize_child(obj, "iommu", iommu, TYPE_RISCV_IOMMU);
qdev_alias_all_properties(DEVICE(iommu), obj);
iommu->icvec_avail_vectors = RISCV_IOMMU_SYSDEV_ICVEC_VECTORS;
riscv_iommu_set_cap_igs(iommu, RISCV_IOMMU_CAP_IGS_BOTH);
}
static const Property riscv_iommu_sys_properties[] = {
DEFINE_PROP_UINT64("addr", RISCVIOMMUStateSys, addr, 0),
DEFINE_PROP_UINT32("base-irq", RISCVIOMMUStateSys, base_irq, 0),
DEFINE_PROP_LINK("irqchip", RISCVIOMMUStateSys, irqchip,
TYPE_DEVICE, DeviceState *),
};
static void riscv_iommu_sys_reset_hold(Object *obj, ResetType type)
{
RISCVIOMMUStateSys *sys = RISCV_IOMMU_SYS(obj);
RISCVIOMMUState *iommu = &sys->iommu;
riscv_iommu_reset(iommu);
trace_riscv_iommu_sys_reset_hold(type);
}
static void riscv_iommu_sys_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.hold = riscv_iommu_sys_reset_hold;
dc->realize = riscv_iommu_sys_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
device_class_set_props(dc, riscv_iommu_sys_properties);
}
static const TypeInfo riscv_iommu_sys = {
.name = TYPE_RISCV_IOMMU_SYS,
.parent = TYPE_SYS_BUS_DEVICE,
.class_init = riscv_iommu_sys_class_init,
.instance_init = riscv_iommu_sys_init,
.instance_size = sizeof(RISCVIOMMUStateSys),
};
static void riscv_iommu_register_sys(void)
{
type_register_static(&riscv_iommu_sys);
}
type_init(riscv_iommu_register_sys)

View file

@ -94,10 +94,9 @@ static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type)
static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type)
{
const uint32_t fctl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FCTL);
uint32_t ipsr, icvec, vector;
if (fctl & RISCV_IOMMU_FCTL_WSI || !s->notify) {
if (!s->notify) {
return;
}
@ -392,9 +391,26 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx,
/* Address range check before first level lookup */
if (!sc[pass].step) {
const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1;
if ((addr & va_mask) != addr) {
return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED;
const uint64_t va_len = va_skip + va_bits;
const uint64_t va_mask = (1ULL << va_len) - 1;
if (pass == S_STAGE && va_len > 32) {
target_ulong mask, masked_msbs;
mask = (1L << (TARGET_LONG_BITS - (va_len - 1))) - 1;
masked_msbs = (addr >> (va_len - 1)) & mask;
if (masked_msbs != 0 && masked_msbs != mask) {
return (iotlb->perm & IOMMU_WO) ?
RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S :
RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S;
}
} else {
if ((addr & va_mask) != addr) {
return (iotlb->perm & IOMMU_WO) ?
RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS :
RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS;
}
}
}
@ -2113,11 +2129,53 @@ static const MemoryRegionOps riscv_iommu_trap_ops = {
}
};
void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode)
{
s->cap = set_field(s->cap, RISCV_IOMMU_CAP_IGS, mode);
}
static void riscv_iommu_instance_init(Object *obj)
{
RISCVIOMMUState *s = RISCV_IOMMU(obj);
/* Enable translation debug interface */
s->cap = RISCV_IOMMU_CAP_DBG;
/* Report QEMU target physical address space limits */
s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
TARGET_PHYS_ADDR_SPACE_BITS);
/* TODO: method to report supported PID bits */
s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
s->cap |= RISCV_IOMMU_CAP_PD8;
/* register storage */
s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
/* Mark all registers read-only */
memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
/* Device translation context cache */
s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
riscv_iommu_ctx_equal,
g_free, NULL);
s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
riscv_iommu_iot_equal,
g_free, NULL);
s->iommus.le_next = NULL;
s->iommus.le_prev = NULL;
QLIST_INIT(&s->spaces);
}
static void riscv_iommu_realize(DeviceState *dev, Error **errp)
{
RISCVIOMMUState *s = RISCV_IOMMU(dev);
s->cap = s->version & RISCV_IOMMU_CAP_VERSION;
s->cap |= s->version & RISCV_IOMMU_CAP_VERSION;
if (s->enable_msi) {
s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF;
}
@ -2132,29 +2190,11 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp)
s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 |
RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4;
}
/* Enable translation debug interface */
s->cap |= RISCV_IOMMU_CAP_DBG;
/* Report QEMU target physical address space limits */
s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS,
TARGET_PHYS_ADDR_SPACE_BITS);
/* TODO: method to report supported PID bits */
s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */
s->cap |= RISCV_IOMMU_CAP_PD8;
/* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */
s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ?
RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE);
/* register storage */
s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE);
/* Mark all registers read-only */
memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE);
/*
* Register complete MMIO space, including MSI/PBA registers.
* Note, PCIDevice implementation will add overlapping MR for MSI/PBA,
@ -2212,19 +2252,6 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s,
"riscv-iommu-trap", ~0ULL);
address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as");
/* Device translation context cache */
s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash,
riscv_iommu_ctx_equal,
g_free, NULL);
s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash,
riscv_iommu_iot_equal,
g_free, NULL);
s->iommus.le_next = NULL;
s->iommus.le_prev = NULL;
QLIST_INIT(&s->spaces);
}
static void riscv_iommu_unrealize(DeviceState *dev)
@ -2235,6 +2262,41 @@ static void riscv_iommu_unrealize(DeviceState *dev)
g_hash_table_unref(s->ctx_cache);
}
void riscv_iommu_reset(RISCVIOMMUState *s)
{
uint32_t reg_clr;
int ddtp_mode;
/*
* Clear DDTP while setting DDTP_mode back to user
* initial setting.
*/
ddtp_mode = s->enable_off ?
RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE;
s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, ddtp_mode);
riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, s->ddtp);
reg_clr = RISCV_IOMMU_CQCSR_CQEN | RISCV_IOMMU_CQCSR_CIE |
RISCV_IOMMU_CQCSR_CQON | RISCV_IOMMU_CQCSR_BUSY;
riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, 0, reg_clr);
reg_clr = RISCV_IOMMU_FQCSR_FQEN | RISCV_IOMMU_FQCSR_FIE |
RISCV_IOMMU_FQCSR_FQON | RISCV_IOMMU_FQCSR_BUSY;
riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, 0, reg_clr);
reg_clr = RISCV_IOMMU_PQCSR_PQEN | RISCV_IOMMU_PQCSR_PIE |
RISCV_IOMMU_PQCSR_PQON | RISCV_IOMMU_PQCSR_BUSY;
riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, 0, reg_clr);
riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0,
RISCV_IOMMU_TR_REQ_CTL_GO_BUSY);
riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0);
g_hash_table_remove_all(s->ctx_cache);
g_hash_table_remove_all(s->iot_cache);
}
static const Property riscv_iommu_properties[] = {
DEFINE_PROP_UINT32("version", RISCVIOMMUState, version,
RISCV_IOMMU_SPEC_DOT_VER),
@ -2265,6 +2327,7 @@ static const TypeInfo riscv_iommu_info = {
.name = TYPE_RISCV_IOMMU,
.parent = TYPE_DEVICE,
.instance_size = sizeof(RISCVIOMMUState),
.instance_init = riscv_iommu_instance_init,
.class_init = riscv_iommu_class_init,
};

View file

@ -21,6 +21,9 @@
#include "qom/object.h"
#include "hw/riscv/iommu.h"
#include "hw/riscv/riscv-iommu-bits.h"
typedef enum riscv_iommu_igs_modes riscv_iommu_igs_mode;
struct RISCVIOMMUState {
/*< private >*/
@ -85,6 +88,8 @@ struct RISCVIOMMUState {
void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus,
Error **errp);
void riscv_iommu_set_cap_igs(RISCVIOMMUState *s, riscv_iommu_igs_mode mode);
void riscv_iommu_reset(RISCVIOMMUState *s);
/* private helpers */

View file

@ -78,6 +78,7 @@ static void sifive_e_machine_init(MachineState *machine)
SiFiveEState *s = RISCV_E_MACHINE(machine);
MemoryRegion *sys_mem = get_system_memory();
int i;
RISCVBootInfo boot_info;
if (machine->ram_size != mc->default_ram_size) {
char *sz = size_to_str(mc->default_ram_size);
@ -113,8 +114,9 @@ static void sifive_e_machine_init(MachineState *machine)
rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory);
riscv_boot_info_init(&boot_info, &s->soc.cpus);
if (machine->kernel_filename) {
riscv_load_kernel(machine, &s->soc.cpus,
riscv_load_kernel(machine, &boot_info,
memmap[SIFIVE_E_DEV_DTIM].base,
false, NULL);
}

View file

@ -518,13 +518,15 @@ static void sifive_u_machine_init(MachineState *machine)
target_ulong firmware_end_addr, kernel_start_addr;
const char *firmware_name;
uint32_t start_addr_hi32 = 0x00000000;
uint32_t fdt_load_addr_hi32 = 0x00000000;
int i;
uint32_t fdt_load_addr;
uint64_t fdt_load_addr;
uint64_t kernel_entry;
DriveInfo *dinfo;
BlockBackend *blk;
DeviceState *flash_dev, *sd_dev, *card_dev;
qemu_irq flash_cs, sd_cs;
RISCVBootInfo boot_info;
/* Initialize SoC */
object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC);
@ -590,12 +592,13 @@ static void sifive_u_machine_init(MachineState *machine)
firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
&start_addr, NULL);
riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus,
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus,
kernel_start_addr, true, NULL);
riscv_load_kernel(machine, &boot_info, kernel_start_addr,
true, NULL);
kernel_entry = boot_info.image_low_addr;
} else {
/*
* If dynamic firmware is used, it doesn't know where is the next mode
@ -606,11 +609,12 @@ static void sifive_u_machine_init(MachineState *machine)
fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base,
memmap[SIFIVE_U_DEV_DRAM].size,
machine);
machine, &boot_info);
riscv_load_fdt(fdt_load_addr, machine->fdt);
if (!riscv_is_32bit(&s->soc.u_cpus)) {
start_addr_hi32 = (uint64_t)start_addr >> 32;
fdt_load_addr_hi32 = fdt_load_addr >> 32;
}
/* reset vector */
@ -625,7 +629,7 @@ static void sifive_u_machine_init(MachineState *machine)
start_addr, /* start: .dword */
start_addr_hi32,
fdt_load_addr, /* fdt_laddr: .dword */
0x00000000,
fdt_load_addr_hi32,
0x00000000,
/* fw_dyn: */
};

View file

@ -201,11 +201,12 @@ static void spike_board_init(MachineState *machine)
hwaddr firmware_load_addr = memmap[SPIKE_DRAM].base;
target_ulong kernel_start_addr;
char *firmware_name;
uint32_t fdt_load_addr;
uint64_t fdt_load_addr;
uint64_t kernel_entry;
char *soc_name;
int i, base_hartid, hart_count;
bool htif_custom_base = false;
RISCVBootInfo boot_info;
/* Check socket count limit */
if (SPIKE_SOCKETS_MAX < riscv_socket_count(machine)) {
@ -300,13 +301,14 @@ 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(&s->soc[0],
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
kernel_entry = riscv_load_kernel(machine, &s->soc[0],
kernel_start_addr,
true, htif_symbol_callback);
riscv_load_kernel(machine, &boot_info, kernel_start_addr,
true, htif_symbol_callback);
kernel_entry = boot_info.image_low_addr;
} else {
/*
* If dynamic firmware is used, it doesn't know where is the next mode
@ -317,7 +319,7 @@ static void spike_board_init(MachineState *machine)
fdt_load_addr = riscv_compute_fdt_addr(memmap[SPIKE_DRAM].base,
memmap[SPIKE_DRAM].size,
machine);
machine, &boot_info);
riscv_load_fdt(fdt_load_addr, machine->fdt);
/* load the reset vector */

View file

@ -15,3 +15,7 @@ riscv_iommu_icvec_write(uint32_t orig, uint32_t actual) "ICVEC write: incoming 0
riscv_iommu_ats(const char *id, unsigned b, unsigned d, unsigned f, uint64_t iova) "%s: translate request %04x:%02x.%u iova: 0x%"PRIx64
riscv_iommu_ats_inval(const char *id) "%s: dev-iotlb invalidate"
riscv_iommu_ats_prgr(const char *id) "%s: dev-iotlb page request group response"
riscv_iommu_sys_irq_sent(uint32_t vector) "IRQ sent to vector %u"
riscv_iommu_sys_msi_sent(uint32_t vector, uint64_t msi_addr, uint32_t msi_data, uint32_t result) "MSI sent to vector %u msi_addr 0x%"PRIx64" msi_data 0x%x result %u"
riscv_iommu_sys_reset_hold(int reset_type) "reset type %d"
riscv_iommu_pci_reset_hold(int reset_type) "reset type %d"

View file

@ -200,14 +200,15 @@ acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
/*
* Serial Port Console Redirection Table (SPCR)
* Rev: 1.07
* Rev: 1.10
*/
static void
spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s)
{
const char name[] = ".";
AcpiSpcrData serial = {
.interface_type = 0, /* 16550 compatible */
.interface_type = 0x12, /* 16550 compatible */
.base_addr.id = AML_AS_SYSTEM_MEMORY,
.base_addr.width = 32,
.base_addr.offset = 0,
@ -229,9 +230,14 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s)
.pci_function = 0,
.pci_flags = 0,
.pci_segment = 0,
.uart_clk_freq = 0,
.precise_baudrate = 0,
.namespace_string_length = sizeof(name),
.namespace_string_offset = 88,
};
build_spcr(table_data, linker, &serial, 2, s->oem_id, s->oem_table_id);
build_spcr(table_data, linker, &serial, 4, s->oem_id, s->oem_table_id,
name);
}
/* RHCT Node[N] starts at offset 56 */

View file

@ -33,6 +33,7 @@
#include "target/riscv/pmu.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/iommu.h"
#include "hw/riscv/riscv-iommu-bits.h"
#include "hw/riscv/virt.h"
#include "hw/riscv/boot.h"
#include "hw/riscv/numa.h"
@ -58,9 +59,18 @@
#include "hw/virtio/virtio-iommu.h"
/* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */
static bool virt_use_kvm_aia(RISCVVirtState *s)
static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type)
{
return kvm_irqchip_in_kernel() && s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
return riscv_is_kvm_aia_aplic_imsic(msimode);
}
static bool virt_use_emulated_aplic(RISCVVirtAIAType aia_type)
{
bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
return riscv_use_emulated_aplic(msimode);
}
static bool virt_aclint_allowed(void)
@ -76,6 +86,7 @@ static const MemMapEntry virt_memmap[] = {
[VIRT_CLINT] = { 0x2000000, 0x10000 },
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
[VIRT_IOMMU_SYS] = { 0x3010000, 0x1000 },
[VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 },
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
[VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) },
@ -775,12 +786,19 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
*msi_pcie_phandle = msi_s_phandle;
}
/* KVM AIA only has one APLIC instance */
if (kvm_enabled() && virt_use_kvm_aia(s)) {
/*
* With KVM AIA aplic-imsic, using an irqchip without split
* mode, we'll use only one APLIC instance.
*/
if (!virt_use_emulated_aplic(s->aia_type)) {
create_fdt_socket_aplic(s, memmap, 0,
msi_m_phandle, msi_s_phandle, phandle,
&intc_phandles[0], xplic_phandles,
ms->smp.cpus);
*irq_mmio_phandle = xplic_phandles[0];
*irq_virtio_phandle = xplic_phandles[0];
*irq_pcie_phandle = xplic_phandles[0];
} else {
phandle_pos = ms->smp.cpus;
for (socket = (socket_count - 1); socket >= 0; socket--) {
@ -798,13 +816,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
s->soc[socket].num_harts);
}
}
}
if (kvm_enabled() && virt_use_kvm_aia(s)) {
*irq_mmio_phandle = xplic_phandles[0];
*irq_virtio_phandle = xplic_phandles[0];
*irq_pcie_phandle = xplic_phandles[0];
} else {
for (socket = 0; socket < socket_count; socket++) {
if (socket == 0) {
*irq_mmio_phandle = xplic_phandles[socket];
@ -853,7 +865,8 @@ static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap,
static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap,
uint32_t irq_pcie_phandle,
uint32_t msi_pcie_phandle)
uint32_t msi_pcie_phandle,
uint32_t iommu_sys_phandle)
{
g_autofree char *name = NULL;
MachineState *ms = MACHINE(s);
@ -887,6 +900,12 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap,
2, virt_high_pcie_memmap.base,
2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size);
if (virt_is_iommu_sys_enabled(s)) {
qemu_fdt_setprop_cells(ms->fdt, name, "iommu-map",
0, iommu_sys_phandle, 0, 0, 0,
iommu_sys_phandle, 0, 0xffff);
}
create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle);
}
@ -1033,6 +1052,47 @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf)
bdf + 1, iommu_phandle, bdf + 1, 0xffff - bdf);
}
static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip,
uint32_t msi_phandle,
uint32_t *iommu_sys_phandle)
{
const char comp[] = "riscv,iommu";
void *fdt = MACHINE(s)->fdt;
uint32_t iommu_phandle;
g_autofree char *iommu_node = NULL;
hwaddr addr = s->memmap[VIRT_IOMMU_SYS].base;
hwaddr size = s->memmap[VIRT_IOMMU_SYS].size;
uint32_t iommu_irq_map[RISCV_IOMMU_INTR_COUNT] = {
IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_CQ,
IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_FQ,
IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PM,
IOMMU_SYS_IRQ + RISCV_IOMMU_INTR_PQ,
};
iommu_node = g_strdup_printf("/soc/iommu@%x",
(unsigned int) s->memmap[VIRT_IOMMU_SYS].base);
iommu_phandle = qemu_fdt_alloc_phandle(fdt);
qemu_fdt_add_subnode(fdt, iommu_node);
qemu_fdt_setprop(fdt, iommu_node, "compatible", comp, sizeof(comp));
qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1);
qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle);
qemu_fdt_setprop_cells(fdt, iommu_node, "reg",
addr >> 32, addr, size >> 32, size);
qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip);
qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts",
iommu_irq_map[0], FDT_IRQ_TYPE_EDGE_LOW,
iommu_irq_map[1], FDT_IRQ_TYPE_EDGE_LOW,
iommu_irq_map[2], FDT_IRQ_TYPE_EDGE_LOW,
iommu_irq_map[3], FDT_IRQ_TYPE_EDGE_LOW);
qemu_fdt_setprop_cell(fdt, iommu_node, "msi-parent", msi_phandle);
*iommu_sys_phandle = iommu_phandle;
}
static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf)
{
const char comp[] = "riscv,pci-iommu";
@ -1061,6 +1121,7 @@ static void finalize_fdt(RISCVVirtState *s)
{
uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1;
uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1;
uint32_t iommu_sys_phandle = 1;
create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle,
&irq_pcie_phandle, &irq_virtio_phandle,
@ -1068,7 +1129,12 @@ static void finalize_fdt(RISCVVirtState *s)
create_fdt_virtio(s, virt_memmap, irq_virtio_phandle);
create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle);
if (virt_is_iommu_sys_enabled(s)) {
create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle,
&iommu_sys_phandle);
}
create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle,
iommu_sys_phandle);
create_fdt_reset(s, virt_memmap, &phandle);
@ -1234,7 +1300,7 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
int base_hartid, int hart_count)
{
int i;
hwaddr addr;
hwaddr addr = 0;
uint32_t guest_bits;
DeviceState *aplic_s = NULL;
DeviceState *aplic_m = NULL;
@ -1284,6 +1350,10 @@ static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
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;
}
@ -1364,6 +1434,7 @@ static void virt_machine_done(Notifier *notifier, void *data)
uint64_t fdt_load_addr;
uint64_t kernel_entry = 0;
BlockBackend *pflash_blk0;
RISCVBootInfo boot_info;
/*
* An user provided dtb must include everything, including
@ -1412,17 +1483,19 @@ static void virt_machine_done(Notifier *notifier, void *data)
}
}
if (machine->kernel_filename && !kernel_entry) {
kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0],
firmware_end_addr);
riscv_boot_info_init(&boot_info, &s->soc[0]);
kernel_entry = riscv_load_kernel(machine, &s->soc[0],
kernel_start_addr, true, NULL);
if (machine->kernel_filename && !kernel_entry) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
riscv_load_kernel(machine, &boot_info, kernel_start_addr,
true, NULL);
kernel_entry = boot_info.image_low_addr;
}
fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base,
memmap[VIRT_DRAM].size,
machine);
machine, &boot_info);
riscv_load_fdt(fdt_load_addr, machine->fdt);
/* load the reset vector */
@ -1565,7 +1638,7 @@ static void virt_machine_init(MachineState *machine)
}
}
if (kvm_enabled() && virt_use_kvm_aia(s)) {
if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) {
kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT,
VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS,
memmap[VIRT_APLIC_S].base,
@ -1648,6 +1721,22 @@ static void virt_machine_init(MachineState *machine)
create_fdt(s, memmap);
}
if (virt_is_iommu_sys_enabled(s)) {
DeviceState *iommu_sys = qdev_new(TYPE_RISCV_IOMMU_SYS);
object_property_set_uint(OBJECT(iommu_sys), "addr",
s->memmap[VIRT_IOMMU_SYS].base,
&error_fatal);
object_property_set_uint(OBJECT(iommu_sys), "base-irq",
IOMMU_SYS_IRQ,
&error_fatal);
object_property_set_link(OBJECT(iommu_sys), "irqchip",
OBJECT(mmio_irqchip),
&error_fatal);
sysbus_realize_and_unref(SYS_BUS_DEVICE(iommu_sys), &error_fatal);
}
s->machine_done.notify = virt_machine_done;
qemu_add_machine_init_done_notifier(&s->machine_done);
}
@ -1661,6 +1750,7 @@ static void virt_machine_instance_init(Object *obj)
s->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
s->acpi = ON_OFF_AUTO_AUTO;
s->iommu_sys = ON_OFF_AUTO_AUTO;
}
static char *virt_get_aia_guests(Object *obj, Error **errp)
@ -1733,6 +1823,28 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp)
s->have_aclint = value;
}
bool virt_is_iommu_sys_enabled(RISCVVirtState *s)
{
return s->iommu_sys == ON_OFF_AUTO_ON;
}
static void virt_get_iommu_sys(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
OnOffAuto iommu_sys = s->iommu_sys;
visit_type_OnOffAuto(v, name, &iommu_sys, errp);
}
static void virt_set_iommu_sys(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
visit_type_OnOffAuto(v, name, &s->iommu_sys, errp);
}
bool virt_is_acpi_enabled(RISCVVirtState *s)
{
return s->acpi != ON_OFF_AUTO_OFF;
@ -1759,10 +1871,12 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
RISCVVirtState *s = RISCV_VIRT_MACHINE(machine);
if (device_is_dynamic_sysbus(mc, dev) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) {
s->iommu_sys = ON_OFF_AUTO_OFF;
return HOTPLUG_HANDLER(machine);
}
@ -1789,6 +1903,7 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) {
create_fdt_iommu(s, pci_get_bdf(PCI_DEVICE(dev)));
s->iommu_sys = ON_OFF_AUTO_OFF;
}
}
@ -1851,6 +1966,12 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
NULL, NULL);
object_class_property_set_description(oc, "acpi",
"Enable ACPI");
object_class_property_add(oc, "iommu-sys", "OnOffAuto",
virt_get_iommu_sys, virt_set_iommu_sys,
NULL, NULL);
object_class_property_set_description(oc, "iommu-sys",
"Enable IOMMU platform device");
}
static const TypeInfo virt_machine_typeinfo = {

View file

@ -112,7 +112,6 @@ typedef struct AcpiSpcrData {
uint8_t flow_control;
uint8_t terminal_type;
uint8_t language;
uint8_t reserved1;
uint16_t pci_device_id; /* Must be 0xffff if not PCI device */
uint16_t pci_vendor_id; /* Must be 0xffff if not PCI device */
uint8_t pci_bus;
@ -120,7 +119,11 @@ typedef struct AcpiSpcrData {
uint8_t pci_function;
uint32_t pci_flags;
uint8_t pci_segment;
uint32_t reserved2;
uint32_t uart_clk_freq;
uint32_t precise_baudrate;
uint32_t namespace_string_length;
uint32_t namespace_string_offset;
char namespace_string[];
} AcpiSpcrData;
#define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0)

View file

@ -507,5 +507,5 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog,
void build_spcr(GArray *table_data, BIOSLinker *linker,
const AcpiSpcrData *f, const uint8_t rev,
const char *oem_id, const char *oem_table_id);
const char *oem_id, const char *oem_table_id, const char *name);
#endif

View file

@ -68,9 +68,17 @@ struct RISCVAPLICState {
uint32_t num_irqs;
bool msimode;
bool mmode;
/* To support KVM aia=aplic-imsic with irqchip split mode */
bool kvm_splitmode;
uint32_t kvm_msicfgaddr;
uint32_t kvm_msicfgaddrH;
};
void riscv_aplic_add_child(DeviceState *parent, DeviceState *child);
bool riscv_is_kvm_aia_aplic_imsic(bool msimode);
bool riscv_use_emulated_aplic(bool msimode);
void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr);
DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size,
uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources,

View file

@ -27,11 +27,23 @@
#define RISCV32_BIOS_BIN "opensbi-riscv32-generic-fw_dynamic.bin"
#define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin"
typedef struct RISCVBootInfo {
ssize_t kernel_size;
hwaddr image_low_addr;
hwaddr image_high_addr;
hwaddr initrd_start;
ssize_t initrd_size;
bool is_32bit;
} RISCVBootInfo;
bool riscv_is_32bit(RISCVHartArrayState *harts);
char *riscv_plic_hart_config_string(int hart_count);
target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts,
void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts);
target_ulong riscv_calc_kernel_start_addr(RISCVBootInfo *info,
target_ulong firmware_end_addr);
target_ulong riscv_find_and_load_firmware(MachineState *machine,
const char *default_machine_firmware,
@ -43,13 +55,13 @@ char *riscv_find_firmware(const char *firmware_filename,
target_ulong riscv_load_firmware(const char *firmware_filename,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb);
target_ulong riscv_load_kernel(MachineState *machine,
RISCVHartArrayState *harts,
target_ulong firmware_end_addr,
bool load_initrd,
symbol_fn_t sym_cb);
uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size,
MachineState *ms);
void riscv_load_kernel(MachineState *machine,
RISCVBootInfo *info,
target_ulong kernel_start_addr,
bool load_initrd,
symbol_fn_t sym_cb);
uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
MachineState *ms, RISCVBootInfo *info);
void riscv_load_fdt(hwaddr fdt_addr, void *fdt);
void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts,
hwaddr saddr,

View file

@ -30,7 +30,15 @@ typedef struct RISCVIOMMUState RISCVIOMMUState;
typedef struct RISCVIOMMUSpace RISCVIOMMUSpace;
#define TYPE_RISCV_IOMMU_PCI "riscv-iommu-pci"
OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI)
OBJECT_DECLARE_TYPE(RISCVIOMMUStatePci, RISCVIOMMUPciClass, RISCV_IOMMU_PCI)
typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci;
typedef struct RISCVIOMMUPciClass RISCVIOMMUPciClass;
#define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device"
OBJECT_DECLARE_TYPE(RISCVIOMMUStateSys, RISCVIOMMUSysClass, RISCV_IOMMU_SYS)
typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys;
typedef struct RISCVIOMMUSysClass RISCVIOMMUSysClass;
#define FDT_IRQ_TYPE_EDGE_LOW 1
#endif

View file

@ -62,6 +62,7 @@ struct RISCVVirtState {
OnOffAuto acpi;
const MemMapEntry *memmap;
struct GPEXHost *gpex_host;
OnOffAuto iommu_sys;
};
enum {
@ -84,7 +85,8 @@ enum {
VIRT_PCIE_MMIO,
VIRT_PCIE_PIO,
VIRT_PLATFORM_BUS,
VIRT_PCIE_ECAM
VIRT_PCIE_ECAM,
VIRT_IOMMU_SYS,
};
enum {
@ -93,6 +95,7 @@ enum {
VIRTIO_IRQ = 1, /* 1 to 8 */
VIRTIO_COUNT = 8,
PCIE_IRQ = 0x20, /* 32 to 35 */
IOMMU_SYS_IRQ = 0x24, /* 36-39 */
VIRT_PLATFORM_BUS_IRQ = 64, /* 64 to 95 */
};
@ -129,6 +132,7 @@ enum {
1 + FDT_APLIC_INT_CELLS)
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);

View file

@ -49,6 +49,8 @@
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")
#define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906")
#define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1")
#define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon")
#define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu")
#define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host")
OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU)

View file

@ -191,6 +191,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(ssccptr, PRIV_VERSION_1_11_0, has_priv_1_11),
ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf),
ISA_EXT_DATA_ENTRY(sscounterenw, PRIV_VERSION_1_12_0, has_priv_1_12),
ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen),
ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc),
ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12),
ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12),
@ -199,6 +200,7 @@ const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval),
ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot),
ISA_EXT_DATA_ENTRY(svpbmt, PRIV_VERSION_1_12_0, ext_svpbmt),
ISA_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte),
ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc),
ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba),
ISA_EXT_DATA_ENTRY(xtheadbb, PRIV_VERSION_1_11_0, ext_xtheadbb),
@ -579,6 +581,100 @@ static void rv64_veyron_v1_cpu_init(Object *obj)
#endif
}
/* Tenstorrent Ascalon */
static void rv64_tt_ascalon_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU | RVH | RVV);
env->priv_ver = PRIV_VERSION_1_13_0;
/* Enable ISA extensions */
cpu->cfg.mmu = true;
cpu->cfg.vlenb = 256 >> 3;
cpu->cfg.elen = 64;
cpu->env.vext_ver = VEXT_VERSION_1_00_0;
cpu->cfg.rvv_ma_all_1s = true;
cpu->cfg.rvv_ta_all_1s = true;
cpu->cfg.misa_w = true;
cpu->cfg.pmp = true;
cpu->cfg.cbom_blocksize = 64;
cpu->cfg.cbop_blocksize = 64;
cpu->cfg.cboz_blocksize = 64;
cpu->cfg.ext_zic64b = true;
cpu->cfg.ext_zicbom = true;
cpu->cfg.ext_zicbop = true;
cpu->cfg.ext_zicboz = true;
cpu->cfg.ext_zicntr = true;
cpu->cfg.ext_zicond = true;
cpu->cfg.ext_zicsr = true;
cpu->cfg.ext_zifencei = true;
cpu->cfg.ext_zihintntl = true;
cpu->cfg.ext_zihintpause = true;
cpu->cfg.ext_zihpm = true;
cpu->cfg.ext_zimop = true;
cpu->cfg.ext_zawrs = true;
cpu->cfg.ext_zfa = true;
cpu->cfg.ext_zfbfmin = true;
cpu->cfg.ext_zfh = true;
cpu->cfg.ext_zfhmin = true;
cpu->cfg.ext_zcb = true;
cpu->cfg.ext_zcmop = true;
cpu->cfg.ext_zba = true;
cpu->cfg.ext_zbb = true;
cpu->cfg.ext_zbs = true;
cpu->cfg.ext_zkt = true;
cpu->cfg.ext_zvbb = true;
cpu->cfg.ext_zvbc = true;
cpu->cfg.ext_zvfbfmin = true;
cpu->cfg.ext_zvfbfwma = true;
cpu->cfg.ext_zvfh = true;
cpu->cfg.ext_zvfhmin = true;
cpu->cfg.ext_zvkng = true;
cpu->cfg.ext_smaia = true;
cpu->cfg.ext_smstateen = true;
cpu->cfg.ext_ssaia = true;
cpu->cfg.ext_sscofpmf = true;
cpu->cfg.ext_sstc = true;
cpu->cfg.ext_svade = true;
cpu->cfg.ext_svinval = true;
cpu->cfg.ext_svnapot = true;
cpu->cfg.ext_svpbmt = true;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(cpu, VM_1_10_SV57);
#endif
}
static void rv64_xiangshan_nanhu_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
riscv_cpu_set_misa_ext(env, RVG | RVC | RVB | RVS | RVU);
env->priv_ver = PRIV_VERSION_1_12_0;
/* Enable ISA extensions */
cpu->cfg.ext_zbc = true;
cpu->cfg.ext_zbkb = true;
cpu->cfg.ext_zbkc = true;
cpu->cfg.ext_zbkx = true;
cpu->cfg.ext_zknd = true;
cpu->cfg.ext_zkne = true;
cpu->cfg.ext_zknh = true;
cpu->cfg.ext_zksed = true;
cpu->cfg.ext_zksh = true;
cpu->cfg.ext_svinval = true;
cpu->cfg.mmu = true;
cpu->cfg.pmp = true;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(cpu, VM_1_10_SV39);
#endif
}
#ifdef CONFIG_TCG
static void rv128_base_cpu_init(Object *obj)
{
@ -1597,6 +1693,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = {
/* These are experimental so mark with 'x-' */
const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = {
MULTI_EXT_CFG_BOOL("x-svukte", ext_svukte, false),
{ },
};
@ -1609,6 +1707,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = {
*/
const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = {
MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true),
MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true),
{ },
};
@ -2983,7 +3082,10 @@ static const TypeInfo riscv_cpu_type_infos[] = {
DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init),
DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init),
DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init),
DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init),
DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init),
DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU,
MXL_RV64, rv64_xiangshan_nanhu_cpu_init),
#ifdef CONFIG_TCG
DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init),
#endif /* CONFIG_TCG */

View file

@ -604,6 +604,7 @@ typedef enum {
#define HSTATUS_VTVM 0x00100000
#define HSTATUS_VTW 0x00200000
#define HSTATUS_VTSR 0x00400000
#define HSTATUS_HUKTE 0x01000000
#define HSTATUS_VSXL 0x300000000
#define HSTATUS32_WPRI 0xFF8FF87E
@ -785,6 +786,7 @@ typedef enum RISCVException {
#define SENVCFG_CBIE MENVCFG_CBIE
#define SENVCFG_CBCFE MENVCFG_CBCFE
#define SENVCFG_CBZE MENVCFG_CBZE
#define SENVCFG_UKTE BIT(8)
#define HENVCFG_FIOM MENVCFG_FIOM
#define HENVCFG_LPE MENVCFG_LPE

View file

@ -84,6 +84,7 @@ struct RISCVCPUConfig {
bool ext_svnapot;
bool ext_svpbmt;
bool ext_svvptc;
bool ext_svukte;
bool ext_zdinx;
bool ext_zaamo;
bool ext_zacas;
@ -139,6 +140,7 @@ struct RISCVCPUConfig {
/* Named features */
bool ext_svade;
bool ext_zic64b;
bool ext_ssstateen;
/*
* Always 'true' booleans for named features

View file

@ -857,6 +857,55 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr,
return TRANSLATE_SUCCESS;
}
/* Returns 'true' if a svukte address check is needed */
static bool do_svukte_check(CPURISCVState *env, bool first_stage,
int mode, bool virt)
{
/* Svukte extension depends on Sv39. */
if (!(env_archcpu(env)->cfg.ext_svukte ||
!first_stage ||
VM_1_10_SV39 != get_field(env->satp, SATP64_MODE))) {
return false;
}
/*
* Check hstatus.HUKTE if the effective mode is switched to VU-mode by
* executing HLV/HLVX/HSV in U-mode.
* For other cases, check senvcfg.UKTE.
*/
if (env->priv == PRV_U && !env->virt_enabled && virt) {
if (!get_field(env->hstatus, HSTATUS_HUKTE)) {
return false;
}
} else if (!get_field(env->senvcfg, SENVCFG_UKTE)) {
return false;
}
/*
* Svukte extension is qualified only in U or VU-mode.
*
* Effective mode can be switched to U or VU-mode by:
* - M-mode + mstatus.MPRV=1 + mstatus.MPP=U-mode.
* - Execute HLV/HLVX/HSV from HS-mode + hstatus.SPVP=0.
* - U-mode.
* - VU-mode.
* - Execute HLV/HLVX/HSV from U-mode + hstatus.HU=1.
*/
if (mode != PRV_U) {
return false;
}
return true;
}
static bool check_svukte_addr(CPURISCVState *env, vaddr addr)
{
/* svukte extension excludes RV32 */
uint32_t sxlen = 32 * riscv_cpu_sxl(env);
uint64_t high_bit = addr & (1UL << (sxlen - 1));
return !high_bit;
}
/*
* get_physical_address - get the physical address for this virtual address
*
@ -894,6 +943,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
MemTxResult res;
MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
int mode = mmuidx_priv(mmu_idx);
bool virt = mmuidx_2stage(mmu_idx);
bool use_background = false;
hwaddr ppn;
int napot_bits = 0;
@ -901,6 +951,11 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
bool is_sstack_idx = ((mmu_idx & MMU_IDX_SS_WRITE) == MMU_IDX_SS_WRITE);
bool sstack_page = false;
if (do_svukte_check(env, first_stage, mode, virt) &&
!check_svukte_addr(env, addr)) {
return TRANSLATE_FAIL;
}
/*
* Check if we should use the background registers for the two
* stage translation. We don't need to check if we actually need

View file

@ -2453,6 +2453,10 @@ static RISCVException write_senvcfg(CPURISCVState *env, int csrno,
mask |= SENVCFG_SSE;
}
if (env_archcpu(env)->cfg.ext_svukte) {
mask |= SENVCFG_UKTE;
}
env->senvcfg = (env->senvcfg & ~mask) | (val & mask);
return RISCV_EXCP_NONE;
}
@ -3536,6 +3540,9 @@ static RISCVException read_hstatus(CPURISCVState *env, int csrno,
static RISCVException write_hstatus(CPURISCVState *env, int csrno,
target_ulong val)
{
if (!env_archcpu(env)->cfg.ext_svukte) {
val = val & (~HSTATUS_HUKTE);
}
env->hstatus = val;
if (riscv_cpu_mxl(env) != MXL_RV32 && get_field(val, HSTATUS_VSXL) != 2) {
qemu_log_mask(LOG_UNIMP,

View file

@ -19,7 +19,10 @@
#ifndef RISCV_CPU_INTERNALS_H
#define RISCV_CPU_INTERNALS_H
#include "exec/cpu-common.h"
#include "hw/registerfields.h"
#include "fpu/softfloat-types.h"
#include "target/riscv/cpu_bits.h"
/*
* The current MMU Modes are:

View file

@ -1408,11 +1408,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
int kvm_arch_irqchip_create(KVMState *s)
{
if (kvm_kernel_irqchip_split()) {
error_report("-machine kernel_irqchip=split is not supported on RISC-V.");
exit(1);
}
/*
* We can create the VAIA using the newer device control API.
*/
@ -1734,13 +1729,29 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
}
}
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
KVM_DEV_RISCV_AIA_CONFIG_SRCS,
&aia_irq_num, true, NULL);
if (ret < 0) {
error_report("KVM AIA: failed to set number of input irq lines");
exit(1);
}
/*
* Skip APLIC creation in KVM if we're running split mode.
* This is done by leaving KVM_DEV_RISCV_AIA_CONFIG_SRCS
* unset. We can also skip KVM_DEV_RISCV_AIA_ADDR_APLIC
* since KVM won't be using it.
*/
if (!kvm_kernel_irqchip_split()) {
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
KVM_DEV_RISCV_AIA_CONFIG_SRCS,
&aia_irq_num, true, NULL);
if (ret < 0) {
error_report("KVM AIA: failed to set number of input irq lines");
exit(1);
}
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR,
KVM_DEV_RISCV_AIA_ADDR_APLIC,
&aplic_base, true, NULL);
if (ret < 0) {
error_report("KVM AIA: failed to set the base address of APLIC");
exit(1);
}
}
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
KVM_DEV_RISCV_AIA_CONFIG_IDS,
@ -1781,14 +1792,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
exit(1);
}
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR,
KVM_DEV_RISCV_AIA_ADDR_APLIC,
&aplic_base, true, NULL);
if (ret < 0) {
error_report("KVM AIA: failed to set the base address of APLIC");
exit(1);
}
for (socket = 0; socket < socket_count; socket++) {
socket_imsic_base = imsic_base + socket * (1U << group_shift);
hart_count = riscv_socket_hart_count(machine, socket);

View file

@ -204,10 +204,15 @@ static void riscv_cpu_enable_named_feat(RISCVCPU *cpu, uint32_t feat_offset)
* All other named features are already enabled
* in riscv_tcg_cpu_instance_init().
*/
if (feat_offset == CPU_CFG_OFFSET(ext_zic64b)) {
switch (feat_offset) {
case CPU_CFG_OFFSET(ext_zic64b):
cpu->cfg.cbom_blocksize = 64;
cpu->cfg.cbop_blocksize = 64;
cpu->cfg.cboz_blocksize = 64;
break;
case CPU_CFG_OFFSET(ext_ssstateen):
cpu->cfg.ext_smstateen = true;
break;
}
}
@ -304,6 +309,15 @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu)
}
isa_ext_update_enabled(cpu, edata->ext_enable_offset, false);
/*
* Do not show user warnings for named features that users
* can't enable/disable in the command line. See commit
* 68c9e54bea for more info.
*/
if (cpu_cfg_offset_is_named_feat(edata->ext_enable_offset)) {
continue;
}
#ifndef CONFIG_USER_ONLY
warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx
" because privilege spec version does not match",
@ -331,11 +345,11 @@ static void riscv_cpu_update_named_features(RISCVCPU *cpu)
cpu->cfg.has_priv_1_13 = true;
}
/* zic64b is 1.12 or later */
cpu->cfg.ext_zic64b = cpu->cfg.cbom_blocksize == 64 &&
cpu->cfg.cbop_blocksize == 64 &&
cpu->cfg.cboz_blocksize == 64 &&
cpu->cfg.has_priv_1_12;
cpu->cfg.cboz_blocksize == 64;
cpu->cfg.ext_ssstateen = cpu->cfg.ext_smstateen;
}
static void riscv_cpu_validate_g(RISCVCPU *cpu)
@ -652,6 +666,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
return;
}
if (mcc->misa_mxl_max == MXL_RV32 && cpu->cfg.ext_svukte) {
error_setg(errp, "svukte is not supported for RV32");
return;
}
/*
* Disable isa extensions based on priv spec after we
* validated and set everything we need.

View file

@ -20,6 +20,7 @@
#define TARGET_RISCV_VECTOR_INTERNALS_H
#include "qemu/bitops.h"
#include "hw/registerfields.h"
#include "cpu.h"
#include "tcg/tcg-gvec-desc.h"
#include "internals.h"

Binary file not shown.