mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 15:23:53 -06:00
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:
commit
60a07d4a6e
46 changed files with 1381 additions and 178 deletions
|
@ -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
|
||||
|
|
|
@ -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
83
docs/specs/riscv-aia.rst
Normal 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
|
|
@ -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/
|
||||
|
|
42
docs/system/riscv/microblaze-v-generic.rst
Normal file
42
docs/system/riscv/microblaze-v-generic.rst
Normal 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
|
|
@ -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
|
||||
--------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
100
hw/riscv/boot.c
100
hw/riscv/boot.c
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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}
|
||||
|
|
184
hw/riscv/microblaze-v-generic.c
Normal file
184
hw/riscv/microblaze-v-generic.c
Normal 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)
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
255
hw/riscv/riscv-iommu-sys.c
Normal 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)
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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: */
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 */
|
||||
|
|
161
hw/riscv/virt.c
161
hw/riscv/virt.c
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
Loading…
Add table
Add a link
Reference in a new issue