ppc patch queue 2019-05-29
Next pull request against qemu-4.1. Highlights: * KVM accelerated support for the XIVE interrupt controller in PAPR guests * A number of TCG vector fixes * Fixes for the PReP / 40p machine * Improvements to make check-tcg test coverage Other than that it's just a bunch of assorted fixes, cleanups and minor improvements. This supersedes both the pull request dated 2019-05-21 and the one dated 2019-05-22. I've dropped one hunk which I think may have caused the check-tcg failure that Peter saw (by enabling the ppc64abi32 build, which I think has been broken for ages). I'm not entirely certain, since I haven't reproduced exactly the same failure. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAlzuK2AACgkQbDjKyiDZ s5LFfxAAuvBI2d5gYDSDiniJPMrEzM8ANynf8fYMGSySRNpeKz5PnMhNQieoxaEt pS9qJnnaCDrpV09jJo6QWStUaqFqnLPOYdWvRsnb7mx0yXe5eWUyYPp0TRAqKj8S Ainv9ma8WfhVphsH3E01KR6evdC6BDC0F2afDToFGMKcDKXafmnSOEV9ZtFAzFXO xqh/Az+Y2ATwDmt92uSq7JBS5YRUvhYQORoKslxnrJswKkN+Uwi5+a2FzOHk3Jwe BlV6soEAVqb9ItFtgwcArclryCMMVxrqzs2VTWOYbhznFX0X1xUNeSQ8H+7F+IVy Xu1e2fnwufvilvWSsjtYvdYnnCbNvwgWjYfZNMrQ2hmSDtCQnRKyVIYwiU08Qj2y LmVlQzWN3WYHIRBTACLMDf5VHa9P01QZeJEoVIV6i4m4PCxbSmlzI62eRKNhW917 2d3h8dGIxSDm9/WpXefKMMrt2P7fAqkiz5ZUZIjkspcHaPPmk7qQp0ngFjeEuyFk tJMd87hgemm9gg+mcF9XQ8yZGkR3oTq7nwDGwZHrp8S0GyRvNwhTbT2iKzAG2cxe kfWRFswxn1zYPShqkcj3rwNsg8LnC3b22Og/obHYVjQ8ONx4ZB0q8xJSkUpvsQf5 HEUHLHtstBmrInFMf+2KbViUIpobmn4woojjNsqZ32W7OZv6Yk4= =2q3B -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-4.1-20190529' into staging ppc patch queue 2019-05-29 Next pull request against qemu-4.1. Highlights: * KVM accelerated support for the XIVE interrupt controller in PAPR guests * A number of TCG vector fixes * Fixes for the PReP / 40p machine * Improvements to make check-tcg test coverage Other than that it's just a bunch of assorted fixes, cleanups and minor improvements. This supersedes both the pull request dated 2019-05-21 and the one dated 2019-05-22. I've dropped one hunk which I think may have caused the check-tcg failure that Peter saw (by enabling the ppc64abi32 build, which I think has been broken for ages). I'm not entirely certain, since I haven't reproduced exactly the same failure. # gpg: Signature made Wed 29 May 2019 07:49:04 BST # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-4.1-20190529: (44 commits) ppc/pnv: add dummy XSCOM registers for PRD initialization ppc/pnv: introduce new skiboot platform properties spapr: Don't migrate the hpt_maxpagesize cap to older machine types spapr: change default interrupt mode to 'dual' spapr/xive: fix multiple resets when using the 'dual' interrupt mode docs: provide documentation on the POWER9 XIVE interrupt controller spapr/irq: add KVM support to the 'dual' machine ppc/xics: fix irq priority in ics_set_irq_type() spapr/irq: initialize the IRQ device only once spapr/irq: introduce a spapr_irq_init_device() helper spapr: check for the activation of the KVM IRQ device spapr: introduce routines to delete the KVM IRQ device sysbus: add a sysbus_mmio_unmap() helper spapr/xive: activate KVM support spapr/xive: add migration support for KVM spapr/xive: introduce a VM state change handler spapr/xive: add state synchronization with KVM spapr/xive: add hcall support when under KVM spapr/xive: add KVM support spapr: Print out extra hints when CAS negotiation of interrupt mode fails ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
60905286cb
@ -1720,6 +1720,7 @@ L: qemu-ppc@nongnu.org
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: hw/*/*xive*
|
F: hw/*/*xive*
|
||||||
F: include/hw/*/*xive*
|
F: include/hw/*/*xive*
|
||||||
|
F: docs/*/*xive*
|
||||||
|
|
||||||
Subsystems
|
Subsystems
|
||||||
----------
|
----------
|
||||||
|
49
configure
vendored
49
configure
vendored
@ -198,7 +198,7 @@ supported_kvm_target() {
|
|||||||
i386:i386 | i386:x86_64 | i386:x32 | \
|
i386:i386 | i386:x86_64 | i386:x32 | \
|
||||||
x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \
|
x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \
|
||||||
mips:mips | mipsel:mips | \
|
mips:mips | mipsel:mips | \
|
||||||
ppc:ppc | ppc64:ppc | ppc:ppc64 | ppc64:ppc64 | \
|
ppc:ppc | ppc64:ppc | ppc:ppc64 | ppc64:ppc64 | ppc64:ppc64le | \
|
||||||
s390x:s390x)
|
s390x:s390x)
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
@ -502,8 +502,11 @@ cross_cc_arm="arm-linux-gnueabihf-gcc"
|
|||||||
cross_cc_cflags_armeb="-mbig-endian"
|
cross_cc_cflags_armeb="-mbig-endian"
|
||||||
cross_cc_i386="i386-pc-linux-gnu-gcc"
|
cross_cc_i386="i386-pc-linux-gnu-gcc"
|
||||||
cross_cc_cflags_i386=""
|
cross_cc_cflags_i386=""
|
||||||
cross_cc_powerpc="powerpc-linux-gnu-gcc"
|
cross_cc_ppc="powerpc-linux-gnu-gcc"
|
||||||
cross_cc_powerpc="powerpc-linux-gnu-gcc"
|
cross_cc_cflags_ppc="-m32"
|
||||||
|
cross_cc_ppc64="powerpc-linux-gnu-gcc"
|
||||||
|
cross_cc_cflags_ppc64="-m64"
|
||||||
|
cross_cc_ppc64le="powerpc64le-linux-gnu-gcc"
|
||||||
|
|
||||||
enabled_cross_compilers=""
|
enabled_cross_compilers=""
|
||||||
|
|
||||||
@ -700,7 +703,11 @@ elif check_define __sparc__ ; then
|
|||||||
fi
|
fi
|
||||||
elif check_define _ARCH_PPC ; then
|
elif check_define _ARCH_PPC ; then
|
||||||
if check_define _ARCH_PPC64 ; then
|
if check_define _ARCH_PPC64 ; then
|
||||||
cpu="ppc64"
|
if check_define _LITTLE_ENDIAN ; then
|
||||||
|
cpu="ppc64le"
|
||||||
|
else
|
||||||
|
cpu="ppc64"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
cpu="ppc"
|
cpu="ppc"
|
||||||
fi
|
fi
|
||||||
@ -731,10 +738,14 @@ ARCH=
|
|||||||
# Note that this case should only have supported host CPUs, not guests.
|
# Note that this case should only have supported host CPUs, not guests.
|
||||||
case "$cpu" in
|
case "$cpu" in
|
||||||
ppc|ppc64|s390|s390x|sparc64|x32|riscv32|riscv64)
|
ppc|ppc64|s390|s390x|sparc64|x32|riscv32|riscv64)
|
||||||
cpu="$cpu"
|
|
||||||
supported_cpu="yes"
|
supported_cpu="yes"
|
||||||
eval "cross_cc_${cpu}=\$host_cc"
|
eval "cross_cc_${cpu}=\$host_cc"
|
||||||
;;
|
;;
|
||||||
|
ppc64le)
|
||||||
|
ARCH="ppc64"
|
||||||
|
supported_cpu="yes"
|
||||||
|
cross_cc_ppc64le=$host_cc
|
||||||
|
;;
|
||||||
i386|i486|i586|i686|i86pc|BePC)
|
i386|i486|i586|i686|i86pc|BePC)
|
||||||
cpu="i386"
|
cpu="i386"
|
||||||
supported_cpu="yes"
|
supported_cpu="yes"
|
||||||
@ -1538,44 +1549,44 @@ case "$cpu" in
|
|||||||
ppc)
|
ppc)
|
||||||
CPU_CFLAGS="-m32"
|
CPU_CFLAGS="-m32"
|
||||||
LDFLAGS="-m32 $LDFLAGS"
|
LDFLAGS="-m32 $LDFLAGS"
|
||||||
cross_cc_powerpc=$cc
|
cross_cc_ppc=$cc
|
||||||
cross_cc_cflags_powerpc=$CPU_CFLAGS
|
cross_cc_cflags_ppc="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
ppc64)
|
ppc64)
|
||||||
CPU_CFLAGS="-m64"
|
CPU_CFLAGS="-m64"
|
||||||
LDFLAGS="-m64 $LDFLAGS"
|
LDFLAGS="-m64 $LDFLAGS"
|
||||||
cross_cc_ppc64=$cc
|
cross_cc_ppc64=$cc
|
||||||
cross_cc_cflags_ppc64=$CPU_CFLAGS
|
cross_cc_cflags_ppc64="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
sparc)
|
sparc)
|
||||||
CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc"
|
CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc"
|
||||||
LDFLAGS="-m32 -mv8plus $LDFLAGS"
|
LDFLAGS="-m32 -mv8plus $LDFLAGS"
|
||||||
cross_cc_sparc=$cc
|
cross_cc_sparc=$cc
|
||||||
cross_cc_cflags_sparc=$CPU_CFLAGS
|
cross_cc_cflags_sparc="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
sparc64)
|
sparc64)
|
||||||
CPU_CFLAGS="-m64 -mcpu=ultrasparc"
|
CPU_CFLAGS="-m64 -mcpu=ultrasparc"
|
||||||
LDFLAGS="-m64 $LDFLAGS"
|
LDFLAGS="-m64 $LDFLAGS"
|
||||||
cross_cc_sparc64=$cc
|
cross_cc_sparc64=$cc
|
||||||
cross_cc_cflags_sparc64=$CPU_CFLAGS
|
cross_cc_cflags_sparc64="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
s390)
|
s390)
|
||||||
CPU_CFLAGS="-m31"
|
CPU_CFLAGS="-m31"
|
||||||
LDFLAGS="-m31 $LDFLAGS"
|
LDFLAGS="-m31 $LDFLAGS"
|
||||||
cross_cc_s390=$cc
|
cross_cc_s390=$cc
|
||||||
cross_cc_cflags_s390=$CPU_CFLAGS
|
cross_cc_cflags_s390="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
s390x)
|
s390x)
|
||||||
CPU_CFLAGS="-m64"
|
CPU_CFLAGS="-m64"
|
||||||
LDFLAGS="-m64 $LDFLAGS"
|
LDFLAGS="-m64 $LDFLAGS"
|
||||||
cross_cc_s390x=$cc
|
cross_cc_s390x=$cc
|
||||||
cross_cc_cflags_s390x=$CPU_CFLAGS
|
cross_cc_cflags_s390x="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
i386)
|
i386)
|
||||||
CPU_CFLAGS="-m32"
|
CPU_CFLAGS="-m32"
|
||||||
LDFLAGS="-m32 $LDFLAGS"
|
LDFLAGS="-m32 $LDFLAGS"
|
||||||
cross_cc_i386=$cc
|
cross_cc_i386=$cc
|
||||||
cross_cc_cflags_i386=$CPU_CFLAGS
|
cross_cc_cflags_i386="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
x86_64)
|
x86_64)
|
||||||
# ??? Only extremely old AMD cpus do not have cmpxchg16b.
|
# ??? Only extremely old AMD cpus do not have cmpxchg16b.
|
||||||
@ -1584,13 +1595,13 @@ case "$cpu" in
|
|||||||
CPU_CFLAGS="-m64 -mcx16"
|
CPU_CFLAGS="-m64 -mcx16"
|
||||||
LDFLAGS="-m64 $LDFLAGS"
|
LDFLAGS="-m64 $LDFLAGS"
|
||||||
cross_cc_x86_64=$cc
|
cross_cc_x86_64=$cc
|
||||||
cross_cc_cflags_x86_64=$CPU_CFLAGS
|
cross_cc_cflags_x86_64="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
x32)
|
x32)
|
||||||
CPU_CFLAGS="-mx32"
|
CPU_CFLAGS="-mx32"
|
||||||
LDFLAGS="-mx32 $LDFLAGS"
|
LDFLAGS="-mx32 $LDFLAGS"
|
||||||
cross_cc_i386=$cc
|
cross_cc_i386=$cc
|
||||||
cross_cc_cflags_i386=$CPU_CFLAGS
|
cross_cc_cflags_i386="$CPU_CFLAGS"
|
||||||
;;
|
;;
|
||||||
# No special flags required for other host CPUs
|
# No special flags required for other host CPUs
|
||||||
esac
|
esac
|
||||||
@ -6198,7 +6209,7 @@ if { test "$cpu" = "i386" || test "$cpu" = "x86_64"; } && \
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
if test "$cpu" = "ppc64" && test "$targetos" != "Darwin" ; then
|
if test "$ARCH" = "ppc64" && test "$targetos" != "Darwin" ; then
|
||||||
roms="$roms spapr-rtas"
|
roms="$roms spapr-rtas"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -7392,7 +7403,7 @@ if test "$linux" = "yes" ; then
|
|||||||
i386|x86_64|x32)
|
i386|x86_64|x32)
|
||||||
linux_arch=x86
|
linux_arch=x86
|
||||||
;;
|
;;
|
||||||
ppc|ppc64)
|
ppc|ppc64|ppc64le)
|
||||||
linux_arch=powerpc
|
linux_arch=powerpc
|
||||||
;;
|
;;
|
||||||
s390x)
|
s390x)
|
||||||
@ -7553,7 +7564,8 @@ case "$target_name" in
|
|||||||
;;
|
;;
|
||||||
ppc)
|
ppc)
|
||||||
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||||
target_compiler=$cross_cc_powerpc
|
target_compiler=$cross_cc_ppc
|
||||||
|
target_compiler_cflags="$cross_cc_cflags_ppc"
|
||||||
;;
|
;;
|
||||||
ppc64)
|
ppc64)
|
||||||
TARGET_BASE_ARCH=ppc
|
TARGET_BASE_ARCH=ppc
|
||||||
@ -7561,6 +7573,7 @@ case "$target_name" in
|
|||||||
mttcg=yes
|
mttcg=yes
|
||||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
||||||
target_compiler=$cross_cc_ppc64
|
target_compiler=$cross_cc_ppc64
|
||||||
|
target_compiler_cflags="$cross_cc_cflags_ppc64"
|
||||||
;;
|
;;
|
||||||
ppc64le)
|
ppc64le)
|
||||||
TARGET_ARCH=ppc64
|
TARGET_ARCH=ppc64
|
||||||
|
@ -12,4 +12,5 @@ Welcome to QEMU's documentation!
|
|||||||
|
|
||||||
interop/index
|
interop/index
|
||||||
devel/index
|
devel/index
|
||||||
|
specs/index
|
||||||
|
|
||||||
|
13
docs/specs/index.rst
Normal file
13
docs/specs/index.rst
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
. This is the top level page for the 'specs' manual
|
||||||
|
|
||||||
|
|
||||||
|
QEMU full-system emulation guest hardware specifications
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
xive
|
174
docs/specs/ppc-spapr-xive.rst
Normal file
174
docs/specs/ppc-spapr-xive.rst
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
XIVE for sPAPR (pseries machines)
|
||||||
|
=================================
|
||||||
|
|
||||||
|
The POWER9 processor comes with a new interrupt controller
|
||||||
|
architecture, called XIVE as "eXternal Interrupt Virtualization
|
||||||
|
Engine". It supports a larger number of interrupt sources and offers
|
||||||
|
virtualization features which enables the HW to deliver interrupts
|
||||||
|
directly to virtual processors without hypervisor assistance.
|
||||||
|
|
||||||
|
A QEMU ``pseries`` machine (which is PAPR compliant) using POWER9
|
||||||
|
processors can run under two interrupt modes:
|
||||||
|
|
||||||
|
- *Legacy Compatibility Mode*
|
||||||
|
|
||||||
|
the hypervisor provides identical interfaces and similar
|
||||||
|
functionality to PAPR+ Version 2.7. This is the default mode
|
||||||
|
|
||||||
|
It is also referred as *XICS* in QEMU.
|
||||||
|
|
||||||
|
- *XIVE native exploitation mode*
|
||||||
|
|
||||||
|
the hypervisor provides new interfaces to manage the XIVE control
|
||||||
|
structures, and provides direct control for interrupt management
|
||||||
|
through MMIO pages.
|
||||||
|
|
||||||
|
Which interrupt modes can be used by the machine is negotiated with
|
||||||
|
the guest O/S during the Client Architecture Support negotiation
|
||||||
|
sequence. The two modes are mutually exclusive.
|
||||||
|
|
||||||
|
Both interrupt mode share the same IRQ number space. See below for the
|
||||||
|
layout.
|
||||||
|
|
||||||
|
CAS Negotiation
|
||||||
|
---------------
|
||||||
|
|
||||||
|
QEMU advertises the supported interrupt modes in the device tree
|
||||||
|
property "ibm,arch-vec-5-platform-support" in byte 23 and the OS
|
||||||
|
Selection for XIVE is indicated in the "ibm,architecture-vec-5"
|
||||||
|
property byte 23.
|
||||||
|
|
||||||
|
The interrupt modes supported by the machine depend on the CPU type
|
||||||
|
(POWER9 is required for XIVE) but also on the machine property
|
||||||
|
``ic-mode`` which can be set on the command line. It can take the
|
||||||
|
following values: ``xics``, ``xive``, ``dual`` and currently ``xics``
|
||||||
|
is the default but it may change in the future.
|
||||||
|
|
||||||
|
The choosen interrupt mode is activated after a reconfiguration done
|
||||||
|
in a machine reset.
|
||||||
|
|
||||||
|
XIVE Device tree properties
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
The properties for the PAPR interrupt controller node when the *XIVE
|
||||||
|
native exploitation mode* is selected shoud contain:
|
||||||
|
|
||||||
|
- ``device_type``
|
||||||
|
|
||||||
|
value should be "power-ivpe".
|
||||||
|
|
||||||
|
- ``compatible``
|
||||||
|
|
||||||
|
value should be "ibm,power-ivpe".
|
||||||
|
|
||||||
|
- ``reg``
|
||||||
|
|
||||||
|
contains the base address and size of the thread interrupt
|
||||||
|
managnement areas (TIMA), for the User level and for the Guest OS
|
||||||
|
level. Only the Guest OS level is taken into account today.
|
||||||
|
|
||||||
|
- ``ibm,xive-eq-sizes``
|
||||||
|
|
||||||
|
the size of the event queues. One cell per size supported, contains
|
||||||
|
log2 of size, in ascending order.
|
||||||
|
|
||||||
|
- ``ibm,xive-lisn-ranges``
|
||||||
|
|
||||||
|
the IRQ interrupt number ranges assigned to the guest for the IPIs.
|
||||||
|
|
||||||
|
The root node also exports :
|
||||||
|
|
||||||
|
- ``ibm,plat-res-int-priorities``
|
||||||
|
|
||||||
|
contains a list of priorities that the hypervisor has reserved for
|
||||||
|
its own use.
|
||||||
|
|
||||||
|
IRQ number space
|
||||||
|
----------------
|
||||||
|
|
||||||
|
IRQ Number space of the ``pseries`` machine is 8K wide and is the same
|
||||||
|
for both interrupt mode. The different ranges are defined as follow :
|
||||||
|
|
||||||
|
- ``0x0000 .. 0x0FFF`` 4K CPU IPIs (only used under XIVE)
|
||||||
|
- ``0x1000 .. 0x1000`` 1 EPOW
|
||||||
|
- ``0x1001 .. 0x1001`` 1 HOTPLUG
|
||||||
|
- ``0x1100 .. 0x11FF`` 256 VIO devices
|
||||||
|
- ``0x1200 .. 0x127F`` 32 PHBs devices
|
||||||
|
- ``0x1280 .. 0x12FF`` unused
|
||||||
|
- ``0x1300 .. 0x1FFF`` PHB MSIs
|
||||||
|
|
||||||
|
Monitoring XIVE
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The state of the XIVE interrupt controller can be queried through the
|
||||||
|
monitor commands ``info pic``. The output comes in two parts.
|
||||||
|
|
||||||
|
First, the state of the thread interrupt context registers is dumped
|
||||||
|
for each CPU :
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
(qemu) info pic
|
||||||
|
CPU[0000]: QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR W2
|
||||||
|
CPU[0000]: USER 00 00 00 00 00 00 00 00 00000000
|
||||||
|
CPU[0000]: OS 00 ff 00 00 ff 00 ff ff 80000400
|
||||||
|
CPU[0000]: POOL 00 00 00 00 00 00 00 00 00000000
|
||||||
|
CPU[0000]: PHYS 00 00 00 00 00 00 00 ff 00000000
|
||||||
|
...
|
||||||
|
|
||||||
|
In the case of a ``pseries`` machine, QEMU acts as the hypervisor and only
|
||||||
|
the O/S and USER register rings make sense. ``W2`` contains the vCPU CAM
|
||||||
|
line which is set to the VP identifier.
|
||||||
|
|
||||||
|
Then comes the routing information which aggregates the EAS and the
|
||||||
|
END configuration:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
...
|
||||||
|
LISN PQ EISN CPU/PRIO EQ
|
||||||
|
00000000 MSI -- 00000010 0/6 380/16384 @1fe3e0000 ^1 [ 80000010 ... ]
|
||||||
|
00000001 MSI -- 00000010 1/6 305/16384 @1fc230000 ^1 [ 80000010 ... ]
|
||||||
|
00000002 MSI -- 00000010 2/6 220/16384 @1fc2f0000 ^1 [ 80000010 ... ]
|
||||||
|
00000003 MSI -- 00000010 3/6 201/16384 @1fc390000 ^1 [ 80000010 ... ]
|
||||||
|
00000004 MSI -Q M 00000000
|
||||||
|
00000005 MSI -Q M 00000000
|
||||||
|
00000006 MSI -Q M 00000000
|
||||||
|
00000007 MSI -Q M 00000000
|
||||||
|
00001000 MSI -- 00000012 0/6 380/16384 @1fe3e0000 ^1 [ 80000010 ... ]
|
||||||
|
00001001 MSI -- 00000013 0/6 380/16384 @1fe3e0000 ^1 [ 80000010 ... ]
|
||||||
|
00001100 MSI -- 00000100 1/6 305/16384 @1fc230000 ^1 [ 80000010 ... ]
|
||||||
|
00001101 MSI -Q M 00000000
|
||||||
|
00001200 LSI -Q M 00000000
|
||||||
|
00001201 LSI -Q M 00000000
|
||||||
|
00001202 LSI -Q M 00000000
|
||||||
|
00001203 LSI -Q M 00000000
|
||||||
|
00001300 MSI -- 00000102 1/6 305/16384 @1fc230000 ^1 [ 80000010 ... ]
|
||||||
|
00001301 MSI -- 00000103 2/6 220/16384 @1fc2f0000 ^1 [ 80000010 ... ]
|
||||||
|
00001302 MSI -- 00000104 3/6 201/16384 @1fc390000 ^1 [ 80000010 ... ]
|
||||||
|
|
||||||
|
The source information and configuration:
|
||||||
|
|
||||||
|
- The ``LISN`` column outputs the interrupt number of the source in
|
||||||
|
range ``[ 0x0 ... 0x1FFF ]`` and its type : ``MSI`` or ``LSI``
|
||||||
|
- The ``PQ`` column reflects the state of the PQ bits of the source :
|
||||||
|
|
||||||
|
- ``--`` source is ready to take events
|
||||||
|
- ``P-`` an event was sent and an EOI is PENDING
|
||||||
|
- ``PQ`` an event was QUEUED
|
||||||
|
- ``-Q`` source is OFF
|
||||||
|
|
||||||
|
a ``M`` indicates that source is *MASKED* at the EAS level,
|
||||||
|
|
||||||
|
The targeting configuration :
|
||||||
|
|
||||||
|
- The ``EISN`` column is the event data that will be queued in the event
|
||||||
|
queue of the O/S.
|
||||||
|
- The ``CPU/PRIO`` column is the tuple defining the CPU number and
|
||||||
|
priority queue serving the source.
|
||||||
|
- The ``EQ`` column outputs :
|
||||||
|
|
||||||
|
- the current index of the event queue/ the max number of entries
|
||||||
|
- the O/S event queue address
|
||||||
|
- the toggle bit
|
||||||
|
- the last entries that were pushed in the event queue.
|
199
docs/specs/ppc-xive.rst
Normal file
199
docs/specs/ppc-xive.rst
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
================================
|
||||||
|
POWER9 XIVE interrupt controller
|
||||||
|
================================
|
||||||
|
|
||||||
|
The POWER9 processor comes with a new interrupt controller
|
||||||
|
architecture, called XIVE as "eXternal Interrupt Virtualization
|
||||||
|
Engine".
|
||||||
|
|
||||||
|
Compared to the previous architecture, the main characteristics of
|
||||||
|
XIVE are to support a larger number of interrupt sources and to
|
||||||
|
deliver interrupts directly to virtual processors without hypervisor
|
||||||
|
assistance. This removes the context switches required for the
|
||||||
|
delivery process.
|
||||||
|
|
||||||
|
|
||||||
|
XIVE architecture
|
||||||
|
=================
|
||||||
|
|
||||||
|
The XIVE IC is composed of three sub-engines, each taking care of a
|
||||||
|
processing layer of external interrupts:
|
||||||
|
|
||||||
|
- Interrupt Virtualization Source Engine (IVSE), or Source Controller
|
||||||
|
(SC). These are found in PCI PHBs, in the PSI host bridge
|
||||||
|
controller, but also inside the main controller for the core IPIs
|
||||||
|
and other sub-chips (NX, CAP, NPU) of the chip/processor. They are
|
||||||
|
configured to feed the IVRE with events.
|
||||||
|
- Interrupt Virtualization Routing Engine (IVRE) or Virtualization
|
||||||
|
Controller (VC). It handles event coalescing and perform interrupt
|
||||||
|
routing by matching an event source number with an Event
|
||||||
|
Notification Descriptor (END).
|
||||||
|
- Interrupt Virtualization Presentation Engine (IVPE) or Presentation
|
||||||
|
Controller (PC). It maintains the interrupt context state of each
|
||||||
|
thread and handles the delivery of the external interrupt to the
|
||||||
|
thread.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
XIVE Interrupt Controller
|
||||||
|
+------------------------------------+ IPIs
|
||||||
|
| +---------+ +---------+ +--------+ | +-------+
|
||||||
|
| |IVRE | |Common Q | |IVPE |----> | CORES |
|
||||||
|
| | esb | | | | |----> | |
|
||||||
|
| | eas | | Bridge | | tctx |----> | |
|
||||||
|
| |SC end | | | | nvt | | | |
|
||||||
|
+------+ | +---------+ +----+----+ +--------+ | +-+-+-+-+
|
||||||
|
| RAM | +------------------|-----------------+ | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | |
|
||||||
|
| | +--------------------v------------------------v-v-v--+ other
|
||||||
|
| <--+ Power Bus +--> chips
|
||||||
|
| esb | +---------+-----------------------+------------------+
|
||||||
|
| eas | | |
|
||||||
|
| end | +--|------+ |
|
||||||
|
| nvt | +----+----+ | +----+----+
|
||||||
|
+------+ |IVSE | | |IVSE |
|
||||||
|
| | | | |
|
||||||
|
| PQ-bits | | | PQ-bits |
|
||||||
|
| local |-+ | in VC |
|
||||||
|
+---------+ +---------+
|
||||||
|
PCIe NX,NPU,CAPI
|
||||||
|
|
||||||
|
|
||||||
|
PQ-bits: 2 bits source state machine (P:pending Q:queued)
|
||||||
|
esb: Event State Buffer (Array of PQ bits in an IVSE)
|
||||||
|
eas: Event Assignment Structure
|
||||||
|
end: Event Notification Descriptor
|
||||||
|
nvt: Notification Virtual Target
|
||||||
|
tctx: Thread interrupt Context registers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
XIVE internal tables
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Each of the sub-engines uses a set of tables to redirect interrupts
|
||||||
|
from event sources to CPU threads.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
+-------+
|
||||||
|
User or O/S | EQ |
|
||||||
|
or +------>|entries|
|
||||||
|
Hypervisor | | .. |
|
||||||
|
Memory | +-------+
|
||||||
|
| ^
|
||||||
|
| |
|
||||||
|
+-------------------------------------------------+
|
||||||
|
| |
|
||||||
|
Hypervisor +------+ +---+--+ +---+--+ +------+
|
||||||
|
Memory | ESB | | EAT | | ENDT | | NVTT |
|
||||||
|
(skiboot) +----+-+ +----+-+ +----+-+ +------+
|
||||||
|
^ | ^ | ^ | ^
|
||||||
|
| | | | | | |
|
||||||
|
+-------------------------------------------------+
|
||||||
|
| | | | | | |
|
||||||
|
| | | | | | |
|
||||||
|
+----|--|--------|--|--------|--|-+ +-|-----+ +------+
|
||||||
|
| | | | | | | | | | tctx| |Thread|
|
||||||
|
IPI or ---+ + v + v + v |---| + .. |-----> |
|
||||||
|
HW events | | | | | |
|
||||||
|
| IVRE | | IVPE | +------+
|
||||||
|
+---------------------------------+ +-------+
|
||||||
|
|
||||||
|
|
||||||
|
The IVSE have a 2-bits state machine, P for pending and Q for queued,
|
||||||
|
for each source that allows events to be triggered. They are stored in
|
||||||
|
an Event State Buffer (ESB) array and can be controlled by MMIOs.
|
||||||
|
|
||||||
|
If the event is let through, the IVRE looks up in the Event Assignment
|
||||||
|
Structure (EAS) table for an Event Notification Descriptor (END)
|
||||||
|
configured for the source. Each Event Notification Descriptor defines
|
||||||
|
a notification path to a CPU and an in-memory Event Queue, in which
|
||||||
|
will be enqueued an EQ data for the O/S to pull.
|
||||||
|
|
||||||
|
The IVPE determines if a Notification Virtual Target (NVT) can handle
|
||||||
|
the event by scanning the thread contexts of the VCPUs dispatched on
|
||||||
|
the processor HW threads. It maintains the interrupt context state of
|
||||||
|
each thread in a NVT table.
|
||||||
|
|
||||||
|
XIVE thread interrupt context
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The XIVE presenter can generate four different exceptions to its
|
||||||
|
HW threads:
|
||||||
|
|
||||||
|
- hypervisor exception
|
||||||
|
- O/S exception
|
||||||
|
- Event-Based Branch (user level)
|
||||||
|
- msgsnd (doorbell)
|
||||||
|
|
||||||
|
Each exception has a state independent from the others called a Thread
|
||||||
|
Interrupt Management context. This context is a set of registers which
|
||||||
|
lets the thread handle priority management and interrupt
|
||||||
|
acknowledgment among other things. The most important ones being :
|
||||||
|
|
||||||
|
- Interrupt Priority Register (PIPR)
|
||||||
|
- Interrupt Pending Buffer (IPB)
|
||||||
|
- Current Processor Priority (CPPR)
|
||||||
|
- Notification Source Register (NSR)
|
||||||
|
|
||||||
|
TIMA
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
The Thread Interrupt Management registers are accessible through a
|
||||||
|
specific MMIO region, called the Thread Interrupt Management Area
|
||||||
|
(TIMA), four aligned pages, each exposing a different view of the
|
||||||
|
registers. First page (page address ending in ``0b00``) gives access
|
||||||
|
to the entire context and is reserved for the ring 0 view for the
|
||||||
|
physical thread context. The second (page address ending in ``0b01``)
|
||||||
|
is for the hypervisor, ring 1 view. The third (page address ending in
|
||||||
|
``0b10``) is for the operating system, ring 2 view. The fourth (page
|
||||||
|
address ending in ``0b11``) is for user level, ring 3 view.
|
||||||
|
|
||||||
|
Interrupt flow from an O/S perspective
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
After an event data has been enqueued in the O/S Event Queue, the IVPE
|
||||||
|
raises the bit corresponding to the priority of the pending interrupt
|
||||||
|
in the register IBP (Interrupt Pending Buffer) to indicate that an
|
||||||
|
event is pending in one of the 8 priority queues. The Pending
|
||||||
|
Interrupt Priority Register (PIPR) is also updated using the IPB. This
|
||||||
|
register represent the priority of the most favored pending
|
||||||
|
notification.
|
||||||
|
|
||||||
|
The PIPR is then compared to the the Current Processor Priority
|
||||||
|
Register (CPPR). If it is more favored (numerically less than), the
|
||||||
|
CPU interrupt line is raised and the EO bit of the Notification Source
|
||||||
|
Register (NSR) is updated to notify the presence of an exception for
|
||||||
|
the O/S. The O/S acknowledges the interrupt with a special load in the
|
||||||
|
Thread Interrupt Management Area.
|
||||||
|
|
||||||
|
The O/S handles the interrupt and when done, performs an EOI using a
|
||||||
|
MMIO operation on the ESB management page of the associate source.
|
||||||
|
|
||||||
|
Overview of the QEMU models for XIVE
|
||||||
|
====================================
|
||||||
|
|
||||||
|
The XiveSource models the IVSE in general, internal and external. It
|
||||||
|
handles the source ESBs and the MMIO interface to control them.
|
||||||
|
|
||||||
|
The XiveNotifier is a small helper interface interconnecting the
|
||||||
|
XiveSource to the XiveRouter.
|
||||||
|
|
||||||
|
The XiveRouter is an abstract model acting as a combined IVRE and
|
||||||
|
IVPE. It routes event notifications using the EAS and END tables to
|
||||||
|
the IVPE sub-engine which does a CAM scan to find a CPU to deliver the
|
||||||
|
exception. Storage should be provided by the inheriting classes.
|
||||||
|
|
||||||
|
XiveEnDSource is a special source object. It exposes the END ESB MMIOs
|
||||||
|
of the Event Queues which are used for coalescing event notifications
|
||||||
|
and for escalation. Not used on the field, only to sync the EQ cache
|
||||||
|
in OPAL.
|
||||||
|
|
||||||
|
Finally, the XiveTCTX contains the interrupt state context of a thread,
|
||||||
|
four sets of registers, one for each exception that can be delivered
|
||||||
|
to a CPU. These contexts are scanned by the IVPE to find a matching VP
|
||||||
|
when a notification is triggered. It also models the Thread Interrupt
|
||||||
|
Management Area (TIMA), which exposes the thread context registers to
|
||||||
|
the CPU for interrupt management.
|
@ -153,6 +153,16 @@ static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sysbus_mmio_unmap(SysBusDevice *dev, int n)
|
||||||
|
{
|
||||||
|
assert(n >= 0 && n < dev->num_mmio);
|
||||||
|
|
||||||
|
if (dev->mmio[n].addr != (hwaddr)-1) {
|
||||||
|
memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory);
|
||||||
|
dev->mmio[n].addr = (hwaddr)-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
|
void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
|
||||||
{
|
{
|
||||||
sysbus_mmio_map_common(dev, n, addr, false, 0);
|
sysbus_mmio_map_common(dev, n, addr, false, 0);
|
||||||
|
@ -39,6 +39,7 @@ obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
|
|||||||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||||
obj-$(CONFIG_XIVE) += xive.o
|
obj-$(CONFIG_XIVE) += xive.o
|
||||||
obj-$(CONFIG_XIVE_SPAPR) += spapr_xive.o
|
obj-$(CONFIG_XIVE_SPAPR) += spapr_xive.o
|
||||||
|
obj-$(CONFIG_XIVE_KVM) += spapr_xive_kvm.o
|
||||||
obj-$(CONFIG_POWERNV) += xics_pnv.o pnv_xive.o
|
obj-$(CONFIG_POWERNV) += xics_pnv.o pnv_xive.o
|
||||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||||
|
@ -40,13 +40,6 @@
|
|||||||
|
|
||||||
#define SPAPR_XIVE_NVT_BASE 0x400
|
#define SPAPR_XIVE_NVT_BASE 0x400
|
||||||
|
|
||||||
/*
|
|
||||||
* The sPAPR machine has a unique XIVE IC device. Assign a fixed value
|
|
||||||
* to the controller block id value. It can nevertheless be changed
|
|
||||||
* for testing purpose.
|
|
||||||
*/
|
|
||||||
#define SPAPR_XIVE_BLOCK_ID 0x0
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sPAPR NVT and END indexing helpers
|
* sPAPR NVT and END indexing helpers
|
||||||
*/
|
*/
|
||||||
@ -86,6 +79,22 @@ static int spapr_xive_target_to_nvt(uint32_t target,
|
|||||||
* sPAPR END indexing uses a simple mapping of the CPU vcpu_id, 8
|
* sPAPR END indexing uses a simple mapping of the CPU vcpu_id, 8
|
||||||
* priorities per CPU
|
* priorities per CPU
|
||||||
*/
|
*/
|
||||||
|
int spapr_xive_end_to_target(uint8_t end_blk, uint32_t end_idx,
|
||||||
|
uint32_t *out_server, uint8_t *out_prio)
|
||||||
|
{
|
||||||
|
|
||||||
|
assert(end_blk == SPAPR_XIVE_BLOCK_ID);
|
||||||
|
|
||||||
|
if (out_server) {
|
||||||
|
*out_server = end_idx >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_prio) {
|
||||||
|
*out_prio = end_idx & 0x7;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void spapr_xive_cpu_to_end(PowerPCCPU *cpu, uint8_t prio,
|
static void spapr_xive_cpu_to_end(PowerPCCPU *cpu, uint8_t prio,
|
||||||
uint8_t *out_end_blk, uint32_t *out_end_idx)
|
uint8_t *out_end_blk, uint32_t *out_end_idx)
|
||||||
{
|
{
|
||||||
@ -120,6 +129,7 @@ static int spapr_xive_target_to_end(uint32_t target, uint8_t prio,
|
|||||||
static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end,
|
static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end,
|
||||||
Monitor *mon)
|
Monitor *mon)
|
||||||
{
|
{
|
||||||
|
uint64_t qaddr_base = xive_end_qaddr(end);
|
||||||
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
||||||
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
||||||
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
||||||
@ -127,9 +137,9 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end,
|
|||||||
uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6);
|
uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6);
|
||||||
uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7);
|
uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7);
|
||||||
|
|
||||||
monitor_printf(mon, "%3d/%d % 6d/%5d ^%d",
|
monitor_printf(mon, "%3d/%d % 6d/%5d @%"PRIx64" ^%d",
|
||||||
spapr_xive_nvt_to_target(0, nvt),
|
spapr_xive_nvt_to_target(0, nvt),
|
||||||
priority, qindex, qentries, qgen);
|
priority, qindex, qentries, qaddr_base, qgen);
|
||||||
|
|
||||||
xive_end_queue_pic_print_info(end, 6, mon);
|
xive_end_queue_pic_print_info(end, 6, mon);
|
||||||
monitor_printf(mon, "]");
|
monitor_printf(mon, "]");
|
||||||
@ -140,7 +150,17 @@ void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon)
|
|||||||
XiveSource *xsrc = &xive->source;
|
XiveSource *xsrc = &xive->source;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
monitor_printf(mon, " LSIN PQ EISN CPU/PRIO EQ\n");
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_synchronize_state(xive, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor_printf(mon, " LISN PQ EISN CPU/PRIO EQ\n");
|
||||||
|
|
||||||
for (i = 0; i < xive->nr_irqs; i++) {
|
for (i = 0; i < xive->nr_irqs; i++) {
|
||||||
uint8_t pq = xive_source_esb_get(xsrc, i);
|
uint8_t pq = xive_source_esb_get(xsrc, i);
|
||||||
@ -173,7 +193,7 @@ void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_xive_map_mmio(SpaprXive *xive)
|
void spapr_xive_map_mmio(SpaprXive *xive)
|
||||||
{
|
{
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base);
|
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base);
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base);
|
sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base);
|
||||||
@ -250,6 +270,9 @@ static void spapr_xive_instance_init(Object *obj)
|
|||||||
object_initialize_child(obj, "end_source", &xive->end_source,
|
object_initialize_child(obj, "end_source", &xive->end_source,
|
||||||
sizeof(xive->end_source), TYPE_XIVE_END_SOURCE,
|
sizeof(xive->end_source), TYPE_XIVE_END_SOURCE,
|
||||||
&error_abort, NULL);
|
&error_abort, NULL);
|
||||||
|
|
||||||
|
/* Not connected to the KVM XIVE device */
|
||||||
|
xive->fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
||||||
@ -304,22 +327,36 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
|||||||
xive->eat = g_new0(XiveEAS, xive->nr_irqs);
|
xive->eat = g_new0(XiveEAS, xive->nr_irqs);
|
||||||
xive->endt = g_new0(XiveEND, xive->nr_ends);
|
xive->endt = g_new0(XiveEND, xive->nr_ends);
|
||||||
|
|
||||||
/* TIMA initialization */
|
xive->nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
|
||||||
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive,
|
xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
|
||||||
"xive.tima", 4ull << TM_SHIFT);
|
|
||||||
|
qemu_register_reset(spapr_xive_reset, dev);
|
||||||
|
|
||||||
/* Define all XIVE MMIO regions on SysBus */
|
/* Define all XIVE MMIO regions on SysBus */
|
||||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio);
|
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio);
|
||||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio);
|
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio);
|
||||||
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
|
sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spapr_xive_init(SpaprXive *xive, Error **errp)
|
||||||
|
{
|
||||||
|
XiveSource *xsrc = &xive->source;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The emulated XIVE device can only be initialized once. If the
|
||||||
|
* ESB memory region has been already mapped, it means we have been
|
||||||
|
* through there.
|
||||||
|
*/
|
||||||
|
if (memory_region_is_mapped(&xsrc->esb_mmio)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TIMA initialization */
|
||||||
|
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive,
|
||||||
|
"xive.tima", 4ull << TM_SHIFT);
|
||||||
|
|
||||||
/* Map all regions */
|
/* Map all regions */
|
||||||
spapr_xive_map_mmio(xive);
|
spapr_xive_map_mmio(xive);
|
||||||
|
|
||||||
xive->nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
|
|
||||||
xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
|
|
||||||
|
|
||||||
qemu_register_reset(spapr_xive_reset, dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk,
|
static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk,
|
||||||
@ -427,10 +464,34 @@ static const VMStateDescription vmstate_spapr_xive_eas = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int vmstate_spapr_xive_pre_save(void *opaque)
|
||||||
|
{
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
return kvmppc_xive_pre_save(SPAPR_XIVE(opaque));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by the sPAPR IRQ backend 'post_load' method at the machine
|
||||||
|
* level.
|
||||||
|
*/
|
||||||
|
int spapr_xive_post_load(SpaprXive *xive, int version_id)
|
||||||
|
{
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
return kvmppc_xive_post_load(xive, version_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_spapr_xive = {
|
static const VMStateDescription vmstate_spapr_xive = {
|
||||||
.name = TYPE_SPAPR_XIVE,
|
.name = TYPE_SPAPR_XIVE,
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
|
.pre_save = vmstate_spapr_xive_pre_save,
|
||||||
|
.post_load = NULL, /* handled at the machine level */
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32_EQUAL(nr_irqs, SpaprXive, NULL),
|
VMSTATE_UINT32_EQUAL(nr_irqs, SpaprXive, NULL),
|
||||||
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(eat, SpaprXive, nr_irqs,
|
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(eat, SpaprXive, nr_irqs,
|
||||||
@ -494,6 +555,17 @@ bool spapr_xive_irq_claim(SpaprXive *xive, uint32_t lisn, bool lsi)
|
|||||||
if (lsi) {
|
if (lsi) {
|
||||||
xive_source_irq_set_lsi(xsrc, lisn);
|
xive_source_irq_set_lsi(xsrc, lisn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_source_reset_one(xsrc, lisn, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,6 +827,16 @@ static target_ulong h_int_set_source_config(PowerPCCPU *cpu,
|
|||||||
new_eas.w = xive_set_field64(EAS_END_DATA, new_eas.w, eisn);
|
new_eas.w = xive_set_field64(EAS_END_DATA, new_eas.w, eisn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_set_source_config(xive, lisn, &new_eas, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return H_HARDWARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
xive->eat[lisn] = new_eas;
|
xive->eat[lisn] = new_eas;
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
@ -993,6 +1075,12 @@ static target_ulong h_int_set_queue_config(PowerPCCPU *cpu,
|
|||||||
case 16:
|
case 16:
|
||||||
case 21:
|
case 21:
|
||||||
case 24:
|
case 24:
|
||||||
|
if (!QEMU_IS_ALIGNED(qpage, 1ul << qsize)) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: EQ @0x%" HWADDR_PRIx
|
||||||
|
" is not naturally aligned with %" HWADDR_PRIx "\n",
|
||||||
|
qpage, (hwaddr)1 << qsize);
|
||||||
|
return H_P4;
|
||||||
|
}
|
||||||
end.w2 = cpu_to_be32((qpage >> 32) & 0x0fffffff);
|
end.w2 = cpu_to_be32((qpage >> 32) & 0x0fffffff);
|
||||||
end.w3 = cpu_to_be32(qpage & 0xffffffff);
|
end.w3 = cpu_to_be32(qpage & 0xffffffff);
|
||||||
end.w0 |= cpu_to_be32(END_W0_ENQUEUE);
|
end.w0 |= cpu_to_be32(END_W0_ENQUEUE);
|
||||||
@ -1060,6 +1148,16 @@ static target_ulong h_int_set_queue_config(PowerPCCPU *cpu,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_set_queue_config(xive, end_blk, end_idx, &end, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return H_HARDWARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Update END */
|
/* Update END */
|
||||||
memcpy(&xive->endt[end_idx], &end, sizeof(XiveEND));
|
memcpy(&xive->endt[end_idx], &end, sizeof(XiveEND));
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
@ -1144,14 +1242,23 @@ static target_ulong h_int_get_queue_config(PowerPCCPU *cpu,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (xive_end_is_enqueue(end)) {
|
if (xive_end_is_enqueue(end)) {
|
||||||
args[1] = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
args[1] = xive_end_qaddr(end);
|
||||||
| be32_to_cpu(end->w3);
|
|
||||||
args[2] = xive_get_field32(END_W0_QSIZE, end->w0) + 12;
|
args[2] = xive_get_field32(END_W0_QSIZE, end->w0) + 12;
|
||||||
} else {
|
} else {
|
||||||
args[1] = 0;
|
args[1] = 0;
|
||||||
args[2] = 0;
|
args[2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_get_queue_config(xive, end_blk, end_idx, end, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return H_HARDWARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: do we need any locking on the END ? */
|
/* TODO: do we need any locking on the END ? */
|
||||||
if (flags & SPAPR_XIVE_END_DEBUG) {
|
if (flags & SPAPR_XIVE_END_DEBUG) {
|
||||||
/* Load the event queue generation number into the return flags */
|
/* Load the event queue generation number into the return flags */
|
||||||
@ -1304,15 +1411,20 @@ static target_ulong h_int_esb(PowerPCCPU *cpu,
|
|||||||
return H_P3;
|
return H_P3;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmio_addr = xive->vc_base + xive_source_esb_mgmt(xsrc, lisn) + offset;
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
args[0] = kvmppc_xive_esb_rw(xsrc, lisn, offset, data,
|
||||||
|
flags & SPAPR_XIVE_ESB_STORE);
|
||||||
|
} else {
|
||||||
|
mmio_addr = xive->vc_base + xive_source_esb_mgmt(xsrc, lisn) + offset;
|
||||||
|
|
||||||
if (dma_memory_rw(&address_space_memory, mmio_addr, &data, 8,
|
if (dma_memory_rw(&address_space_memory, mmio_addr, &data, 8,
|
||||||
(flags & SPAPR_XIVE_ESB_STORE))) {
|
(flags & SPAPR_XIVE_ESB_STORE))) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to access ESB @0x%"
|
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to access ESB @0x%"
|
||||||
HWADDR_PRIx "\n", mmio_addr);
|
HWADDR_PRIx "\n", mmio_addr);
|
||||||
return H_HARDWARE;
|
return H_HARDWARE;
|
||||||
|
}
|
||||||
|
args[0] = (flags & SPAPR_XIVE_ESB_STORE) ? -1 : data;
|
||||||
}
|
}
|
||||||
args[0] = (flags & SPAPR_XIVE_ESB_STORE) ? -1 : data;
|
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1369,7 +1481,20 @@ static target_ulong h_int_sync(PowerPCCPU *cpu,
|
|||||||
* This is not needed when running the emulation under QEMU
|
* This is not needed when running the emulation under QEMU
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* This is not real hardware. Nothing to be done */
|
/*
|
||||||
|
* This is not real hardware. Nothing to be done unless when
|
||||||
|
* under KVM
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_sync_source(xive, lisn, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return H_HARDWARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1404,6 +1529,16 @@ static target_ulong h_int_reset(PowerPCCPU *cpu,
|
|||||||
}
|
}
|
||||||
|
|
||||||
device_reset(DEVICE(xive));
|
device_reset(DEVICE(xive));
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_reset(xive, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return H_HARDWARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
823
hw/intc/spapr_xive_kvm.c
Normal file
823
hw/intc/spapr_xive_kvm.c
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
/*
|
||||||
|
* QEMU PowerPC sPAPR XIVE interrupt controller model
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2019, IBM Corporation.
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL version 2 or later. See the
|
||||||
|
* COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "target/ppc/cpu.h"
|
||||||
|
#include "sysemu/cpus.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
#include "hw/ppc/spapr.h"
|
||||||
|
#include "hw/ppc/spapr_cpu_core.h"
|
||||||
|
#include "hw/ppc/spapr_xive.h"
|
||||||
|
#include "hw/ppc/xive.h"
|
||||||
|
#include "kvm_ppc.h"
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helpers for CPU hotplug
|
||||||
|
*
|
||||||
|
* TODO: make a common KVMEnabledCPU layer for XICS and XIVE
|
||||||
|
*/
|
||||||
|
typedef struct KVMEnabledCPU {
|
||||||
|
unsigned long vcpu_id;
|
||||||
|
QLIST_ENTRY(KVMEnabledCPU) node;
|
||||||
|
} KVMEnabledCPU;
|
||||||
|
|
||||||
|
static QLIST_HEAD(, KVMEnabledCPU)
|
||||||
|
kvm_enabled_cpus = QLIST_HEAD_INITIALIZER(&kvm_enabled_cpus);
|
||||||
|
|
||||||
|
static bool kvm_cpu_is_enabled(CPUState *cs)
|
||||||
|
{
|
||||||
|
KVMEnabledCPU *enabled_cpu;
|
||||||
|
unsigned long vcpu_id = kvm_arch_vcpu_id(cs);
|
||||||
|
|
||||||
|
QLIST_FOREACH(enabled_cpu, &kvm_enabled_cpus, node) {
|
||||||
|
if (enabled_cpu->vcpu_id == vcpu_id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_cpu_enable(CPUState *cs)
|
||||||
|
{
|
||||||
|
KVMEnabledCPU *enabled_cpu;
|
||||||
|
unsigned long vcpu_id = kvm_arch_vcpu_id(cs);
|
||||||
|
|
||||||
|
enabled_cpu = g_malloc(sizeof(*enabled_cpu));
|
||||||
|
enabled_cpu->vcpu_id = vcpu_id;
|
||||||
|
QLIST_INSERT_HEAD(&kvm_enabled_cpus, enabled_cpu, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_cpu_disable_all(void)
|
||||||
|
{
|
||||||
|
KVMEnabledCPU *enabled_cpu, *next;
|
||||||
|
|
||||||
|
QLIST_FOREACH_SAFE(enabled_cpu, &kvm_enabled_cpus, node, next) {
|
||||||
|
QLIST_REMOVE(enabled_cpu, node);
|
||||||
|
g_free(enabled_cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XIVE Thread Interrupt Management context (KVM)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void kvmppc_xive_cpu_set_state(XiveTCTX *tctx, Error **errp)
|
||||||
|
{
|
||||||
|
uint64_t state[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* word0 and word1 of the OS ring. */
|
||||||
|
state[0] = *((uint64_t *) &tctx->regs[TM_QW1_OS]);
|
||||||
|
|
||||||
|
ret = kvm_set_one_reg(tctx->cs, KVM_REG_PPC_VP_STATE, state);
|
||||||
|
if (ret != 0) {
|
||||||
|
error_setg_errno(errp, errno,
|
||||||
|
"XIVE: could not restore KVM state of CPU %ld",
|
||||||
|
kvm_arch_vcpu_id(tctx->cs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_cpu_get_state(XiveTCTX *tctx, Error **errp)
|
||||||
|
{
|
||||||
|
SpaprXive *xive = SPAPR_MACHINE(qdev_get_machine())->xive;
|
||||||
|
uint64_t state[2] = { 0 };
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* The KVM XIVE device is not in use */
|
||||||
|
if (xive->fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kvm_get_one_reg(tctx->cs, KVM_REG_PPC_VP_STATE, state);
|
||||||
|
if (ret != 0) {
|
||||||
|
error_setg_errno(errp, errno,
|
||||||
|
"XIVE: could not capture KVM state of CPU %ld",
|
||||||
|
kvm_arch_vcpu_id(tctx->cs));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* word0 and word1 of the OS ring. */
|
||||||
|
*((uint64_t *) &tctx->regs[TM_QW1_OS]) = state[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
XiveTCTX *tctx;
|
||||||
|
Error *err;
|
||||||
|
} XiveCpuGetState;
|
||||||
|
|
||||||
|
static void kvmppc_xive_cpu_do_synchronize_state(CPUState *cpu,
|
||||||
|
run_on_cpu_data arg)
|
||||||
|
{
|
||||||
|
XiveCpuGetState *s = arg.host_ptr;
|
||||||
|
|
||||||
|
kvmppc_xive_cpu_get_state(s->tctx, &s->err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_cpu_synchronize_state(XiveTCTX *tctx, Error **errp)
|
||||||
|
{
|
||||||
|
XiveCpuGetState s = {
|
||||||
|
.tctx = tctx,
|
||||||
|
.err = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kick the vCPU to make sure they are available for the KVM ioctl.
|
||||||
|
*/
|
||||||
|
run_on_cpu(tctx->cs, kvmppc_xive_cpu_do_synchronize_state,
|
||||||
|
RUN_ON_CPU_HOST_PTR(&s));
|
||||||
|
|
||||||
|
if (s.err) {
|
||||||
|
error_propagate(errp, s.err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_cpu_connect(XiveTCTX *tctx, Error **errp)
|
||||||
|
{
|
||||||
|
SpaprXive *xive = SPAPR_MACHINE(qdev_get_machine())->xive;
|
||||||
|
unsigned long vcpu_id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* The KVM XIVE device is not in use */
|
||||||
|
if (xive->fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if CPU was hot unplugged and replugged. */
|
||||||
|
if (kvm_cpu_is_enabled(tctx->cs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vcpu_id = kvm_arch_vcpu_id(tctx->cs);
|
||||||
|
|
||||||
|
ret = kvm_vcpu_enable_cap(tctx->cs, KVM_CAP_PPC_IRQ_XIVE, 0, xive->fd,
|
||||||
|
vcpu_id, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg(errp, "XIVE: unable to connect CPU%ld to KVM device: %s",
|
||||||
|
vcpu_id, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_cpu_enable(tctx->cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XIVE Interrupt Source (KVM)
|
||||||
|
*/
|
||||||
|
|
||||||
|
void kvmppc_xive_set_source_config(SpaprXive *xive, uint32_t lisn, XiveEAS *eas,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
uint32_t end_idx;
|
||||||
|
uint32_t end_blk;
|
||||||
|
uint8_t priority;
|
||||||
|
uint32_t server;
|
||||||
|
bool masked;
|
||||||
|
uint32_t eisn;
|
||||||
|
uint64_t kvm_src;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
assert(xive_eas_is_valid(eas));
|
||||||
|
|
||||||
|
end_idx = xive_get_field64(EAS_END_INDEX, eas->w);
|
||||||
|
end_blk = xive_get_field64(EAS_END_BLOCK, eas->w);
|
||||||
|
eisn = xive_get_field64(EAS_END_DATA, eas->w);
|
||||||
|
masked = xive_eas_is_masked(eas);
|
||||||
|
|
||||||
|
spapr_xive_end_to_target(end_blk, end_idx, &server, &priority);
|
||||||
|
|
||||||
|
kvm_src = priority << KVM_XIVE_SOURCE_PRIORITY_SHIFT &
|
||||||
|
KVM_XIVE_SOURCE_PRIORITY_MASK;
|
||||||
|
kvm_src |= server << KVM_XIVE_SOURCE_SERVER_SHIFT &
|
||||||
|
KVM_XIVE_SOURCE_SERVER_MASK;
|
||||||
|
kvm_src |= ((uint64_t) masked << KVM_XIVE_SOURCE_MASKED_SHIFT) &
|
||||||
|
KVM_XIVE_SOURCE_MASKED_MASK;
|
||||||
|
kvm_src |= ((uint64_t)eisn << KVM_XIVE_SOURCE_EISN_SHIFT) &
|
||||||
|
KVM_XIVE_SOURCE_EISN_MASK;
|
||||||
|
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_SOURCE_CONFIG, lisn,
|
||||||
|
&kvm_src, true, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_sync_source(SpaprXive *xive, uint32_t lisn, Error **errp)
|
||||||
|
{
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_SOURCE_SYNC, lisn,
|
||||||
|
NULL, true, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At reset, the interrupt sources are simply created and MASKED. We
|
||||||
|
* only need to inform the KVM XIVE device about their type: LSI or
|
||||||
|
* MSI.
|
||||||
|
*/
|
||||||
|
void kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp)
|
||||||
|
{
|
||||||
|
SpaprXive *xive = SPAPR_XIVE(xsrc->xive);
|
||||||
|
uint64_t state = 0;
|
||||||
|
|
||||||
|
/* The KVM XIVE device is not in use */
|
||||||
|
if (xive->fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xive_source_irq_is_lsi(xsrc, srcno)) {
|
||||||
|
state |= KVM_XIVE_LEVEL_SENSITIVE;
|
||||||
|
if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
|
||||||
|
state |= KVM_XIVE_LEVEL_ASSERTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_SOURCE, srcno, &state,
|
||||||
|
true, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvmppc_xive_source_reset(XiveSource *xsrc, Error **errp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < xsrc->nr_irqs; i++) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_source_reset_one(xsrc, i, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is used to perform the magic loads on the ESB pages, described
|
||||||
|
* in xive.h.
|
||||||
|
*
|
||||||
|
* Memory barriers should not be needed for loads (no store for now).
|
||||||
|
*/
|
||||||
|
static uint64_t xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset,
|
||||||
|
uint64_t data, bool write)
|
||||||
|
{
|
||||||
|
uint64_t *addr = xsrc->esb_mmap + xive_source_esb_mgmt(xsrc, srcno) +
|
||||||
|
offset;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
*addr = cpu_to_be64(data);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
/* Prevent the compiler from optimizing away the load */
|
||||||
|
volatile uint64_t value = be64_to_cpu(*addr);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t xive_esb_read(XiveSource *xsrc, int srcno, uint32_t offset)
|
||||||
|
{
|
||||||
|
return xive_esb_rw(xsrc, srcno, offset, 0, 0) & 0x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xive_esb_trigger(XiveSource *xsrc, int srcno)
|
||||||
|
{
|
||||||
|
uint64_t *addr = xsrc->esb_mmap + xive_source_esb_page(xsrc, srcno);
|
||||||
|
|
||||||
|
*addr = 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset,
|
||||||
|
uint64_t data, bool write)
|
||||||
|
{
|
||||||
|
if (write) {
|
||||||
|
return xive_esb_rw(xsrc, srcno, offset, data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special Load EOI handling for LSI sources. Q bit is never set
|
||||||
|
* and the interrupt should be re-triggered if the level is still
|
||||||
|
* asserted.
|
||||||
|
*/
|
||||||
|
if (xive_source_irq_is_lsi(xsrc, srcno) &&
|
||||||
|
offset == XIVE_ESB_LOAD_EOI) {
|
||||||
|
xive_esb_read(xsrc, srcno, XIVE_ESB_SET_PQ_00);
|
||||||
|
if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
|
||||||
|
xive_esb_trigger(xsrc, srcno);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return xive_esb_rw(xsrc, srcno, offset, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvmppc_xive_source_get_state(XiveSource *xsrc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < xsrc->nr_irqs; i++) {
|
||||||
|
/* Perform a load without side effect to retrieve the PQ bits */
|
||||||
|
uint8_t pq = xive_esb_read(xsrc, i, XIVE_ESB_GET);
|
||||||
|
|
||||||
|
/* and save PQ locally */
|
||||||
|
xive_source_esb_set(xsrc, i, pq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val)
|
||||||
|
{
|
||||||
|
XiveSource *xsrc = opaque;
|
||||||
|
SpaprXive *xive = SPAPR_XIVE(xsrc->xive);
|
||||||
|
struct kvm_irq_level args;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* The KVM XIVE device should be in use */
|
||||||
|
assert(xive->fd != -1);
|
||||||
|
|
||||||
|
args.irq = srcno;
|
||||||
|
if (!xive_source_irq_is_lsi(xsrc, srcno)) {
|
||||||
|
if (!val) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
args.level = KVM_INTERRUPT_SET;
|
||||||
|
} else {
|
||||||
|
if (val) {
|
||||||
|
xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
|
||||||
|
args.level = KVM_INTERRUPT_SET_LEVEL;
|
||||||
|
} else {
|
||||||
|
xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
|
||||||
|
args.level = KVM_INTERRUPT_UNSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args);
|
||||||
|
if (rc < 0) {
|
||||||
|
error_report("XIVE: kvm_irq_line() failed : %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sPAPR XIVE interrupt controller (KVM)
|
||||||
|
*/
|
||||||
|
void kvmppc_xive_get_queue_config(SpaprXive *xive, uint8_t end_blk,
|
||||||
|
uint32_t end_idx, XiveEND *end,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
struct kvm_ppc_xive_eq kvm_eq = { 0 };
|
||||||
|
uint64_t kvm_eq_idx;
|
||||||
|
uint8_t priority;
|
||||||
|
uint32_t server;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
assert(xive_end_is_valid(end));
|
||||||
|
|
||||||
|
/* Encode the tuple (server, prio) as a KVM EQ index */
|
||||||
|
spapr_xive_end_to_target(end_blk, end_idx, &server, &priority);
|
||||||
|
|
||||||
|
kvm_eq_idx = priority << KVM_XIVE_EQ_PRIORITY_SHIFT &
|
||||||
|
KVM_XIVE_EQ_PRIORITY_MASK;
|
||||||
|
kvm_eq_idx |= server << KVM_XIVE_EQ_SERVER_SHIFT &
|
||||||
|
KVM_XIVE_EQ_SERVER_MASK;
|
||||||
|
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_EQ_CONFIG, kvm_eq_idx,
|
||||||
|
&kvm_eq, false, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The EQ index and toggle bit are updated by HW. These are the
|
||||||
|
* only fields from KVM we want to update QEMU with. The other END
|
||||||
|
* fields should already be in the QEMU END table.
|
||||||
|
*/
|
||||||
|
end->w1 = xive_set_field32(END_W1_GENERATION, 0ul, kvm_eq.qtoggle) |
|
||||||
|
xive_set_field32(END_W1_PAGE_OFF, 0ul, kvm_eq.qindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_set_queue_config(SpaprXive *xive, uint8_t end_blk,
|
||||||
|
uint32_t end_idx, XiveEND *end,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
struct kvm_ppc_xive_eq kvm_eq = { 0 };
|
||||||
|
uint64_t kvm_eq_idx;
|
||||||
|
uint8_t priority;
|
||||||
|
uint32_t server;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the KVM state from the local END structure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
kvm_eq.flags = 0;
|
||||||
|
if (xive_get_field32(END_W0_UCOND_NOTIFY, end->w0)) {
|
||||||
|
kvm_eq.flags |= KVM_XIVE_EQ_ALWAYS_NOTIFY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the hcall is disabling the EQ, set the size and page address
|
||||||
|
* to zero. When migrating, only valid ENDs are taken into
|
||||||
|
* account.
|
||||||
|
*/
|
||||||
|
if (xive_end_is_valid(end)) {
|
||||||
|
kvm_eq.qshift = xive_get_field32(END_W0_QSIZE, end->w0) + 12;
|
||||||
|
kvm_eq.qaddr = xive_end_qaddr(end);
|
||||||
|
/*
|
||||||
|
* The EQ toggle bit and index should only be relevant when
|
||||||
|
* restoring the EQ state
|
||||||
|
*/
|
||||||
|
kvm_eq.qtoggle = xive_get_field32(END_W1_GENERATION, end->w1);
|
||||||
|
kvm_eq.qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
||||||
|
} else {
|
||||||
|
kvm_eq.qshift = 0;
|
||||||
|
kvm_eq.qaddr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode the tuple (server, prio) as a KVM EQ index */
|
||||||
|
spapr_xive_end_to_target(end_blk, end_idx, &server, &priority);
|
||||||
|
|
||||||
|
kvm_eq_idx = priority << KVM_XIVE_EQ_PRIORITY_SHIFT &
|
||||||
|
KVM_XIVE_EQ_PRIORITY_MASK;
|
||||||
|
kvm_eq_idx |= server << KVM_XIVE_EQ_SERVER_SHIFT &
|
||||||
|
KVM_XIVE_EQ_SERVER_MASK;
|
||||||
|
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_EQ_CONFIG, kvm_eq_idx,
|
||||||
|
&kvm_eq, true, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_reset(SpaprXive *xive, Error **errp)
|
||||||
|
{
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_CTRL, KVM_DEV_XIVE_RESET,
|
||||||
|
NULL, true, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvmppc_xive_get_queues(SpaprXive *xive, Error **errp)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < xive->nr_ends; i++) {
|
||||||
|
if (!xive_end_is_valid(&xive->endt[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvmppc_xive_get_queue_config(xive, SPAPR_XIVE_BLOCK_ID, i,
|
||||||
|
&xive->endt[i], &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The primary goal of the XIVE VM change handler is to mark the EQ
|
||||||
|
* pages dirty when all XIVE event notifications have stopped.
|
||||||
|
*
|
||||||
|
* Whenever the VM is stopped, the VM change handler sets the source
|
||||||
|
* PQs to PENDING to stop the flow of events and to possibly catch a
|
||||||
|
* triggered interrupt occuring while the VM is stopped. The previous
|
||||||
|
* state is saved in anticipation of a migration. The XIVE controller
|
||||||
|
* is then synced through KVM to flush any in-flight event
|
||||||
|
* notification and stabilize the EQs.
|
||||||
|
*
|
||||||
|
* At this stage, we can mark the EQ page dirty and let a migration
|
||||||
|
* sequence transfer the EQ pages to the destination, which is done
|
||||||
|
* just after the stop state.
|
||||||
|
*
|
||||||
|
* The previous configuration of the sources is restored when the VM
|
||||||
|
* runs again. If an interrupt was queued while the VM was stopped,
|
||||||
|
* simply generate a trigger.
|
||||||
|
*/
|
||||||
|
static void kvmppc_xive_change_state_handler(void *opaque, int running,
|
||||||
|
RunState state)
|
||||||
|
{
|
||||||
|
SpaprXive *xive = opaque;
|
||||||
|
XiveSource *xsrc = &xive->source;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the sources to their initial state. This is called when
|
||||||
|
* the VM resumes after a stop or a migration.
|
||||||
|
*/
|
||||||
|
if (running) {
|
||||||
|
for (i = 0; i < xsrc->nr_irqs; i++) {
|
||||||
|
uint8_t pq = xive_source_esb_get(xsrc, i);
|
||||||
|
uint8_t old_pq;
|
||||||
|
|
||||||
|
old_pq = xive_esb_read(xsrc, i, XIVE_ESB_SET_PQ_00 + (pq << 8));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An interrupt was queued while the VM was stopped,
|
||||||
|
* generate a trigger.
|
||||||
|
*/
|
||||||
|
if (pq == XIVE_ESB_RESET && old_pq == XIVE_ESB_QUEUED) {
|
||||||
|
xive_esb_trigger(xsrc, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mask the sources, to stop the flow of event notifications, and
|
||||||
|
* save the PQs locally in the XiveSource object. The XiveSource
|
||||||
|
* state will be collected later on by its vmstate handler if a
|
||||||
|
* migration is in progress.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < xsrc->nr_irqs; i++) {
|
||||||
|
uint8_t pq = xive_esb_read(xsrc, i, XIVE_ESB_GET);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PQ is set to PENDING to possibly catch a triggered
|
||||||
|
* interrupt occuring while the VM is stopped (hotplug event
|
||||||
|
* for instance) .
|
||||||
|
*/
|
||||||
|
if (pq != XIVE_ESB_OFF) {
|
||||||
|
pq = xive_esb_read(xsrc, i, XIVE_ESB_SET_PQ_10);
|
||||||
|
}
|
||||||
|
xive_source_esb_set(xsrc, i, pq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sync the XIVE controller in KVM, to flush in-flight event
|
||||||
|
* notification that should be enqueued in the EQs and mark the
|
||||||
|
* XIVE EQ pages dirty to collect all updates.
|
||||||
|
*/
|
||||||
|
kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_CTRL,
|
||||||
|
KVM_DEV_XIVE_EQ_SYNC, NULL, true, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_synchronize_state(SpaprXive *xive, Error **errp)
|
||||||
|
{
|
||||||
|
/* The KVM XIVE device is not in use */
|
||||||
|
if (xive->fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the VM is stopped, the sources are masked and the previous
|
||||||
|
* state is saved in anticipation of a migration. We should not
|
||||||
|
* synchronize the source state in that case else we will override
|
||||||
|
* the saved state.
|
||||||
|
*/
|
||||||
|
if (runstate_is_running()) {
|
||||||
|
kvmppc_xive_source_get_state(&xive->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EAT: there is no extra state to query from KVM */
|
||||||
|
|
||||||
|
/* ENDT */
|
||||||
|
kvmppc_xive_get_queues(xive, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The SpaprXive 'pre_save' method is called by the vmstate handler of
|
||||||
|
* the SpaprXive model, after the XIVE controller is synced in the VM
|
||||||
|
* change handler.
|
||||||
|
*/
|
||||||
|
int kvmppc_xive_pre_save(SpaprXive *xive)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
/* The KVM XIVE device is not in use */
|
||||||
|
if (xive->fd == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EAT: there is no extra state to query from KVM */
|
||||||
|
|
||||||
|
/* ENDT */
|
||||||
|
kvmppc_xive_get_queues(xive, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The SpaprXive 'post_load' method is not called by a vmstate
|
||||||
|
* handler. It is called at the sPAPR machine level at the end of the
|
||||||
|
* migration sequence by the sPAPR IRQ backend 'post_load' method,
|
||||||
|
* when all XIVE states have been transferred and loaded.
|
||||||
|
*/
|
||||||
|
int kvmppc_xive_post_load(SpaprXive *xive, int version_id)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
CPUState *cs;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* The KVM XIVE device should be in use */
|
||||||
|
assert(xive->fd != -1);
|
||||||
|
|
||||||
|
/* Restore the ENDT first. The targetting depends on it. */
|
||||||
|
for (i = 0; i < xive->nr_ends; i++) {
|
||||||
|
if (!xive_end_is_valid(&xive->endt[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvmppc_xive_set_queue_config(xive, SPAPR_XIVE_BLOCK_ID, i,
|
||||||
|
&xive->endt[i], &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore the EAT */
|
||||||
|
for (i = 0; i < xive->nr_irqs; i++) {
|
||||||
|
if (!xive_eas_is_valid(&xive->eat[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvmppc_xive_set_source_config(xive, i, &xive->eat[i], &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore the thread interrupt contexts */
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
|
||||||
|
kvmppc_xive_cpu_set_state(spapr_cpu_state(cpu)->tctx, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The source states will be restored when the machine starts running */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *kvmppc_xive_mmap(SpaprXive *xive, int pgoff, size_t len,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
void *addr;
|
||||||
|
uint32_t page_shift = 16; /* TODO: fix page_shift */
|
||||||
|
|
||||||
|
addr = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, xive->fd,
|
||||||
|
pgoff << page_shift);
|
||||||
|
if (addr == MAP_FAILED) {
|
||||||
|
error_setg_errno(errp, errno, "XIVE: unable to set memory mapping");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the XIVE memory regions are now backed by mappings from the KVM
|
||||||
|
* XIVE device.
|
||||||
|
*/
|
||||||
|
void kvmppc_xive_connect(SpaprXive *xive, Error **errp)
|
||||||
|
{
|
||||||
|
XiveSource *xsrc = &xive->source;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
size_t esb_len = (1ull << xsrc->esb_shift) * xsrc->nr_irqs;
|
||||||
|
size_t tima_len = 4ull << TM_SHIFT;
|
||||||
|
CPUState *cs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The KVM XIVE device already in use. This is the case when
|
||||||
|
* rebooting under the XIVE-only interrupt mode.
|
||||||
|
*/
|
||||||
|
if (xive->fd != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kvmppc_has_cap_xive()) {
|
||||||
|
error_setg(errp, "IRQ_XIVE capability must be present for KVM");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First, create the KVM XIVE device */
|
||||||
|
xive->fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_XIVE, false);
|
||||||
|
if (xive->fd < 0) {
|
||||||
|
error_setg_errno(errp, -xive->fd, "XIVE: error creating KVM device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1. Source ESB pages - KVM mapping
|
||||||
|
*/
|
||||||
|
xsrc->esb_mmap = kvmppc_xive_mmap(xive, KVM_XIVE_ESB_PAGE_OFFSET, esb_len,
|
||||||
|
&local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_init_ram_device_ptr(&xsrc->esb_mmio, OBJECT(xsrc),
|
||||||
|
"xive.esb", esb_len, xsrc->esb_mmap);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2. END ESB pages (No KVM support yet)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3. TIMA pages - KVM mapping
|
||||||
|
*/
|
||||||
|
xive->tm_mmap = kvmppc_xive_mmap(xive, KVM_XIVE_TIMA_PAGE_OFFSET, tima_len,
|
||||||
|
&local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memory_region_init_ram_device_ptr(&xive->tm_mmio, OBJECT(xive),
|
||||||
|
"xive.tima", tima_len, xive->tm_mmap);
|
||||||
|
|
||||||
|
xive->change = qemu_add_vm_change_state_handler(
|
||||||
|
kvmppc_xive_change_state_handler, xive);
|
||||||
|
|
||||||
|
/* Connect the presenters to the initial VCPUs of the machine */
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
|
||||||
|
kvmppc_xive_cpu_connect(spapr_cpu_state(cpu)->tctx, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the KVM sources */
|
||||||
|
kvmppc_xive_source_reset(xsrc, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_kernel_irqchip = true;
|
||||||
|
kvm_msi_via_irqfd_allowed = true;
|
||||||
|
kvm_gsi_direct_mapping = true;
|
||||||
|
|
||||||
|
/* Map all regions */
|
||||||
|
spapr_xive_map_mmio(xive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp)
|
||||||
|
{
|
||||||
|
XiveSource *xsrc;
|
||||||
|
size_t esb_len;
|
||||||
|
|
||||||
|
/* The KVM XIVE device is not in use */
|
||||||
|
if (!xive || xive->fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kvmppc_has_cap_xive()) {
|
||||||
|
error_setg(errp, "IRQ_XIVE capability must be present for KVM");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the KVM mapping */
|
||||||
|
xsrc = &xive->source;
|
||||||
|
esb_len = (1ull << xsrc->esb_shift) * xsrc->nr_irqs;
|
||||||
|
|
||||||
|
sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 0);
|
||||||
|
munmap(xsrc->esb_mmap, esb_len);
|
||||||
|
|
||||||
|
sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 1);
|
||||||
|
|
||||||
|
sysbus_mmio_unmap(SYS_BUS_DEVICE(xive), 2);
|
||||||
|
munmap(xive->tm_mmap, 4ull << TM_SHIFT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the KVM device fd is closed, the KVM device is destroyed
|
||||||
|
* and removed from the list of devices of the VM. The VCPU
|
||||||
|
* presenters are also detached from the device.
|
||||||
|
*/
|
||||||
|
close(xive->fd);
|
||||||
|
xive->fd = -1;
|
||||||
|
|
||||||
|
kvm_kernel_irqchip = false;
|
||||||
|
kvm_msi_via_irqfd_allowed = false;
|
||||||
|
kvm_gsi_direct_mapping = false;
|
||||||
|
|
||||||
|
/* Clear the local list of presenter (hotplug) */
|
||||||
|
kvm_cpu_disable_all();
|
||||||
|
|
||||||
|
/* VM Change state handler is not needed anymore */
|
||||||
|
qemu_del_vm_change_state_handler(xive->change);
|
||||||
|
}
|
@ -610,6 +610,12 @@ static const TypeInfo ics_simple_info = {
|
|||||||
.class_size = sizeof(ICSStateClass),
|
.class_size = sizeof(ICSStateClass),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void ics_reset_irq(ICSIRQState *irq)
|
||||||
|
{
|
||||||
|
irq->priority = 0xff;
|
||||||
|
irq->saved_priority = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
static void ics_base_reset(DeviceState *dev)
|
static void ics_base_reset(DeviceState *dev)
|
||||||
{
|
{
|
||||||
ICSState *ics = ICS_BASE(dev);
|
ICSState *ics = ICS_BASE(dev);
|
||||||
@ -623,8 +629,7 @@ static void ics_base_reset(DeviceState *dev)
|
|||||||
memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs);
|
memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs);
|
||||||
|
|
||||||
for (i = 0; i < ics->nr_irqs; i++) {
|
for (i = 0; i < ics->nr_irqs; i++) {
|
||||||
ics->irqs[i].priority = 0xff;
|
ics_reset_irq(ics->irqs + i);
|
||||||
ics->irqs[i].saved_priority = 0xff;
|
|
||||||
ics->irqs[i].flags = flags[i];
|
ics->irqs[i].flags = flags[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,6 +765,7 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
|
|||||||
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
|
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
|
||||||
|
|
||||||
if (kvm_irqchip_in_kernel()) {
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
ics_reset_irq(ics->irqs + srcno);
|
||||||
ics_set_kvm_state_one(ics, srcno);
|
ics_set_kvm_state_one(ics, srcno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "hw/ppc/spapr.h"
|
#include "hw/ppc/spapr.h"
|
||||||
|
#include "hw/ppc/spapr_cpu_core.h"
|
||||||
#include "hw/ppc/xics.h"
|
#include "hw/ppc/xics.h"
|
||||||
#include "hw/ppc/xics_spapr.h"
|
#include "hw/ppc/xics_spapr.h"
|
||||||
#include "kvm_ppc.h"
|
#include "kvm_ppc.h"
|
||||||
@ -51,6 +52,16 @@ typedef struct KVMEnabledICP {
|
|||||||
static QLIST_HEAD(, KVMEnabledICP)
|
static QLIST_HEAD(, KVMEnabledICP)
|
||||||
kvm_enabled_icps = QLIST_HEAD_INITIALIZER(&kvm_enabled_icps);
|
kvm_enabled_icps = QLIST_HEAD_INITIALIZER(&kvm_enabled_icps);
|
||||||
|
|
||||||
|
static void kvm_disable_icps(void)
|
||||||
|
{
|
||||||
|
KVMEnabledICP *enabled_icp, *next;
|
||||||
|
|
||||||
|
QLIST_FOREACH_SAFE(enabled_icp, &kvm_enabled_icps, node, next) {
|
||||||
|
QLIST_REMOVE(enabled_icp, node);
|
||||||
|
g_free(enabled_icp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ICP-KVM
|
* ICP-KVM
|
||||||
*/
|
*/
|
||||||
@ -59,6 +70,11 @@ void icp_get_kvm_state(ICPState *icp)
|
|||||||
uint64_t state;
|
uint64_t state;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
|
if (kernel_xics_fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* ICP for this CPU thread is not in use, exiting */
|
/* ICP for this CPU thread is not in use, exiting */
|
||||||
if (!icp->cs) {
|
if (!icp->cs) {
|
||||||
return;
|
return;
|
||||||
@ -95,6 +111,11 @@ int icp_set_kvm_state(ICPState *icp)
|
|||||||
uint64_t state;
|
uint64_t state;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
|
if (kernel_xics_fd == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ICP for this CPU thread is not in use, exiting */
|
/* ICP for this CPU thread is not in use, exiting */
|
||||||
if (!icp->cs) {
|
if (!icp->cs) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -123,8 +144,9 @@ void icp_kvm_realize(DeviceState *dev, Error **errp)
|
|||||||
unsigned long vcpu_id;
|
unsigned long vcpu_id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
if (kernel_xics_fd == -1) {
|
if (kernel_xics_fd == -1) {
|
||||||
abort();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cs = icp->cs;
|
cs = icp->cs;
|
||||||
@ -160,6 +182,11 @@ void ics_get_kvm_state(ICSState *ics)
|
|||||||
uint64_t state;
|
uint64_t state;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
|
if (kernel_xics_fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ics->nr_irqs; i++) {
|
for (i = 0; i < ics->nr_irqs; i++) {
|
||||||
ICSIRQState *irq = &ics->irqs[i];
|
ICSIRQState *irq = &ics->irqs[i];
|
||||||
|
|
||||||
@ -220,6 +247,11 @@ int ics_set_kvm_state_one(ICSState *ics, int srcno)
|
|||||||
ICSIRQState *irq = &ics->irqs[srcno];
|
ICSIRQState *irq = &ics->irqs[srcno];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
|
if (kernel_xics_fd == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
state = irq->server;
|
state = irq->server;
|
||||||
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
|
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
|
||||||
<< KVM_XICS_PRIORITY_SHIFT;
|
<< KVM_XICS_PRIORITY_SHIFT;
|
||||||
@ -259,6 +291,11 @@ int ics_set_kvm_state(ICSState *ics)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
|
if (kernel_xics_fd == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ics->nr_irqs; i++) {
|
for (i = 0; i < ics->nr_irqs; i++) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -276,6 +313,9 @@ void ics_kvm_set_irq(ICSState *ics, int srcno, int val)
|
|||||||
struct kvm_irq_level args;
|
struct kvm_irq_level args;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
/* The KVM XICS device should be in use */
|
||||||
|
assert(kernel_xics_fd != -1);
|
||||||
|
|
||||||
args.irq = srcno + ics->offset;
|
args.irq = srcno + ics->offset;
|
||||||
if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MSI) {
|
if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MSI) {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
@ -303,6 +343,16 @@ static void rtas_dummy(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||||||
int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
|
int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
CPUState *cs;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The KVM XICS device already in use. This is the case when
|
||||||
|
* rebooting under the XICS-only interrupt mode.
|
||||||
|
*/
|
||||||
|
if (kernel_xics_fd != -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
|
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
@ -351,6 +401,26 @@ int xics_kvm_init(SpaprMachineState *spapr, Error **errp)
|
|||||||
kvm_msi_via_irqfd_allowed = true;
|
kvm_msi_via_irqfd_allowed = true;
|
||||||
kvm_gsi_direct_mapping = true;
|
kvm_gsi_direct_mapping = true;
|
||||||
|
|
||||||
|
/* Create the presenters */
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
|
||||||
|
icp_kvm_realize(DEVICE(spapr_cpu_state(cpu)->icp), &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the KVM sources */
|
||||||
|
ics_set_kvm_state(spapr->ics);
|
||||||
|
|
||||||
|
/* Connect the presenters to the initial VCPUs of the machine */
|
||||||
|
CPU_FOREACH(cs) {
|
||||||
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
|
icp_set_kvm_state(spapr_cpu_state(cpu)->icp);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -360,3 +430,44 @@ fail:
|
|||||||
kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
|
kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp)
|
||||||
|
{
|
||||||
|
/* The KVM XICS device is not in use */
|
||||||
|
if (kernel_xics_fd == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
|
||||||
|
error_setg(errp,
|
||||||
|
"KVM and IRQ_XICS capability must be present for KVM XICS device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only on P9 using the XICS-on XIVE KVM device:
|
||||||
|
*
|
||||||
|
* When the KVM device fd is closed, the device is destroyed and
|
||||||
|
* removed from the list of devices of the VM. The VCPU presenters
|
||||||
|
* are also detached from the device.
|
||||||
|
*/
|
||||||
|
close(kernel_xics_fd);
|
||||||
|
kernel_xics_fd = -1;
|
||||||
|
|
||||||
|
spapr_rtas_unregister(RTAS_IBM_SET_XIVE);
|
||||||
|
spapr_rtas_unregister(RTAS_IBM_GET_XIVE);
|
||||||
|
spapr_rtas_unregister(RTAS_IBM_INT_OFF);
|
||||||
|
spapr_rtas_unregister(RTAS_IBM_INT_ON);
|
||||||
|
|
||||||
|
kvmppc_define_rtas_kernel_token(0, "ibm,set-xive");
|
||||||
|
kvmppc_define_rtas_kernel_token(0, "ibm,get-xive");
|
||||||
|
kvmppc_define_rtas_kernel_token(0, "ibm,int-on");
|
||||||
|
kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
|
||||||
|
|
||||||
|
kvm_kernel_irqchip = false;
|
||||||
|
kvm_msi_via_irqfd_allowed = false;
|
||||||
|
kvm_gsi_direct_mapping = false;
|
||||||
|
|
||||||
|
/* Clear the presenter from the VCPUs */
|
||||||
|
kvm_disable_icps();
|
||||||
|
}
|
||||||
|
@ -239,6 +239,13 @@ static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||||||
|
|
||||||
void xics_spapr_init(SpaprMachineState *spapr)
|
void xics_spapr_init(SpaprMachineState *spapr)
|
||||||
{
|
{
|
||||||
|
/* Emulated mode can only be initialized once. */
|
||||||
|
if (spapr->ics->init) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spapr->ics->init = true;
|
||||||
|
|
||||||
/* Registration of global state belongs into realize */
|
/* Registration of global state belongs into realize */
|
||||||
spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
|
spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
|
||||||
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
|
spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
|
||||||
|
@ -493,6 +493,16 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon)
|
|||||||
int cpu_index = tctx->cs ? tctx->cs->cpu_index : -1;
|
int cpu_index = tctx->cs ? tctx->cs->cpu_index : -1;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
kvmppc_xive_cpu_synchronize_state(tctx, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
monitor_printf(mon, "CPU[%04x]: QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR"
|
monitor_printf(mon, "CPU[%04x]: QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR"
|
||||||
" W2\n", cpu_index);
|
" W2\n", cpu_index);
|
||||||
|
|
||||||
@ -555,6 +565,15 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Connect the presenter to the VCPU (required for CPU hotplug) */
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
kvmppc_xive_cpu_connect(tctx, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qemu_register_reset(xive_tctx_reset, dev);
|
qemu_register_reset(xive_tctx_reset, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,10 +582,27 @@ static void xive_tctx_unrealize(DeviceState *dev, Error **errp)
|
|||||||
qemu_unregister_reset(xive_tctx_reset, dev);
|
qemu_unregister_reset(xive_tctx_reset, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vmstate_xive_tctx_pre_save(void *opaque)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
kvmppc_xive_cpu_get_state(XIVE_TCTX(opaque), &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_xive_tctx = {
|
static const VMStateDescription vmstate_xive_tctx = {
|
||||||
.name = TYPE_XIVE_TCTX,
|
.name = TYPE_XIVE_TCTX,
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
|
.pre_save = vmstate_xive_tctx_pre_save,
|
||||||
|
.post_load = NULL, /* handled by the sPAPRxive model */
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_BUFFER(regs, XiveTCTX),
|
VMSTATE_BUFFER(regs, XiveTCTX),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
@ -990,9 +1026,11 @@ static void xive_source_realize(DeviceState *dev, Error **errp)
|
|||||||
xsrc->status = g_malloc0(xsrc->nr_irqs);
|
xsrc->status = g_malloc0(xsrc->nr_irqs);
|
||||||
xsrc->lsi_map = bitmap_new(xsrc->nr_irqs);
|
xsrc->lsi_map = bitmap_new(xsrc->nr_irqs);
|
||||||
|
|
||||||
memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
|
if (!kvm_irqchip_in_kernel()) {
|
||||||
&xive_source_esb_ops, xsrc, "xive.esb",
|
memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc),
|
||||||
(1ull << xsrc->esb_shift) * xsrc->nr_irqs);
|
&xive_source_esb_ops, xsrc, "xive.esb",
|
||||||
|
(1ull << xsrc->esb_shift) * xsrc->nr_irqs);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_register_reset(xive_source_reset, dev);
|
qemu_register_reset(xive_source_reset, dev);
|
||||||
}
|
}
|
||||||
@ -1042,8 +1080,7 @@ static const TypeInfo xive_source_info = {
|
|||||||
|
|
||||||
void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon)
|
void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon)
|
||||||
{
|
{
|
||||||
uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
uint64_t qaddr_base = xive_end_qaddr(end);
|
||||||
| be32_to_cpu(end->w3);
|
|
||||||
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
||||||
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
||||||
uint32_t qentries = 1 << (qsize + 10);
|
uint32_t qentries = 1 << (qsize + 10);
|
||||||
@ -1072,8 +1109,7 @@ void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon)
|
|||||||
|
|
||||||
void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon)
|
void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon)
|
||||||
{
|
{
|
||||||
uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
uint64_t qaddr_base = xive_end_qaddr(end);
|
||||||
| be32_to_cpu(end->w3);
|
|
||||||
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
||||||
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
||||||
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
||||||
@ -1101,8 +1137,7 @@ void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon)
|
|||||||
|
|
||||||
static void xive_end_enqueue(XiveEND *end, uint32_t data)
|
static void xive_end_enqueue(XiveEND *end, uint32_t data)
|
||||||
{
|
{
|
||||||
uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32
|
uint64_t qaddr_base = xive_end_qaddr(end);
|
||||||
| be32_to_cpu(end->w3);
|
|
||||||
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0);
|
||||||
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1);
|
||||||
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1);
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/i386/pc.h"
|
#include "hw/i386/pc.h"
|
||||||
#include "hw/timer/i8254.h"
|
#include "hw/timer/i8254.h"
|
||||||
#include "hw/timer/mc146818rtc.h"
|
|
||||||
#include "hw/audio/pcspk.h"
|
#include "hw/audio/pcspk.h"
|
||||||
|
|
||||||
#define TYPE_I82378 "i82378"
|
#define TYPE_I82378 "i82378"
|
||||||
@ -105,9 +104,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
|
|||||||
|
|
||||||
/* 2 82C37 (dma) */
|
/* 2 82C37 (dma) */
|
||||||
isa = isa_create_simple(isabus, "i82374");
|
isa = isa_create_simple(isabus, "i82374");
|
||||||
|
|
||||||
/* timer */
|
|
||||||
isa_create_simple(isabus, TYPE_MC146818_RTC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i82378_init(Object *obj)
|
static void i82378_init(Object *obj)
|
||||||
|
@ -122,3 +122,8 @@ config XIVE_SPAPR
|
|||||||
default y
|
default y
|
||||||
depends on PSERIES
|
depends on PSERIES
|
||||||
select XIVE
|
select XIVE
|
||||||
|
|
||||||
|
config XIVE_KVM
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
depends on XIVE_SPAPR && KVM
|
||||||
|
13
hw/ppc/pnv.c
13
hw/ppc/pnv.c
@ -450,7 +450,8 @@ static void pnv_dt_power_mgt(void *fdt)
|
|||||||
|
|
||||||
static void *pnv_dt_create(MachineState *machine)
|
static void *pnv_dt_create(MachineState *machine)
|
||||||
{
|
{
|
||||||
const char plat_compat[] = "qemu,powernv\0ibm,powernv";
|
const char plat_compat8[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv";
|
||||||
|
const char plat_compat9[] = "qemu,powernv9\0ibm,powernv";
|
||||||
PnvMachineState *pnv = PNV_MACHINE(machine);
|
PnvMachineState *pnv = PNV_MACHINE(machine);
|
||||||
void *fdt;
|
void *fdt;
|
||||||
char *buf;
|
char *buf;
|
||||||
@ -465,8 +466,14 @@ static void *pnv_dt_create(MachineState *machine)
|
|||||||
_FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
|
_FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
|
||||||
_FDT((fdt_setprop_string(fdt, 0, "model",
|
_FDT((fdt_setprop_string(fdt, 0, "model",
|
||||||
"IBM PowerNV (emulated by qemu)")));
|
"IBM PowerNV (emulated by qemu)")));
|
||||||
_FDT((fdt_setprop(fdt, 0, "compatible", plat_compat,
|
if (pnv_is_power9(pnv)) {
|
||||||
sizeof(plat_compat))));
|
_FDT((fdt_setprop(fdt, 0, "compatible", plat_compat9,
|
||||||
|
sizeof(plat_compat9))));
|
||||||
|
} else {
|
||||||
|
_FDT((fdt_setprop(fdt, 0, "compatible", plat_compat8,
|
||||||
|
sizeof(plat_compat8))));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
|
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
|
||||||
_FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
|
_FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
|
||||||
|
@ -29,6 +29,12 @@
|
|||||||
|
|
||||||
#include <libfdt.h>
|
#include <libfdt.h>
|
||||||
|
|
||||||
|
/* PRD registers */
|
||||||
|
#define PRD_P8_IPOLL_REG_MASK 0x01020013
|
||||||
|
#define PRD_P8_IPOLL_REG_STATUS 0x01020014
|
||||||
|
#define PRD_P9_IPOLL_REG_MASK 0x000F0033
|
||||||
|
#define PRD_P9_IPOLL_REG_STATUS 0x000F0034
|
||||||
|
|
||||||
static void xscom_complete(CPUState *cs, uint64_t hmer_bits)
|
static void xscom_complete(CPUState *cs, uint64_t hmer_bits)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -70,6 +76,12 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
|
|||||||
case 0x1010c00: /* PIBAM FIR */
|
case 0x1010c00: /* PIBAM FIR */
|
||||||
case 0x1010c03: /* PIBAM FIR MASK */
|
case 0x1010c03: /* PIBAM FIR MASK */
|
||||||
|
|
||||||
|
/* PRD registers */
|
||||||
|
case PRD_P8_IPOLL_REG_MASK:
|
||||||
|
case PRD_P8_IPOLL_REG_STATUS:
|
||||||
|
case PRD_P9_IPOLL_REG_MASK:
|
||||||
|
case PRD_P9_IPOLL_REG_STATUS:
|
||||||
|
|
||||||
/* P9 xscom reset */
|
/* P9 xscom reset */
|
||||||
case 0x0090018: /* Receive status reg */
|
case 0x0090018: /* Receive status reg */
|
||||||
case 0x0090012: /* log register */
|
case 0x0090012: /* log register */
|
||||||
@ -124,6 +136,12 @@ static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val)
|
|||||||
case 0x201302a: /* CAPP stuff */
|
case 0x201302a: /* CAPP stuff */
|
||||||
case 0x2013801: /* CAPP stuff */
|
case 0x2013801: /* CAPP stuff */
|
||||||
case 0x2013802: /* CAPP stuff */
|
case 0x2013802: /* CAPP stuff */
|
||||||
|
|
||||||
|
/* P8 PRD registers */
|
||||||
|
case PRD_P8_IPOLL_REG_MASK:
|
||||||
|
case PRD_P8_IPOLL_REG_STATUS:
|
||||||
|
case PRD_P9_IPOLL_REG_MASK:
|
||||||
|
case PRD_P9_IPOLL_REG_STATUS:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -601,7 +601,7 @@ static int prep_set_cmos_checksum(DeviceState *dev, void *opaque)
|
|||||||
uint16_t checksum = *(uint16_t *)opaque;
|
uint16_t checksum = *(uint16_t *)opaque;
|
||||||
ISADevice *rtc;
|
ISADevice *rtc;
|
||||||
|
|
||||||
if (object_dynamic_cast(OBJECT(dev), "mc146818rtc")) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) {
|
||||||
rtc = ISA_DEVICE(dev);
|
rtc = ISA_DEVICE(dev);
|
||||||
rtc_set_memory(rtc, 0x2e, checksum & 0xff);
|
rtc_set_memory(rtc, 0x2e, checksum & 0xff);
|
||||||
rtc_set_memory(rtc, 0x3e, checksum & 0xff);
|
rtc_set_memory(rtc, 0x3e, checksum & 0xff);
|
||||||
@ -675,6 +675,11 @@ static void ibm_40p_init(MachineState *machine)
|
|||||||
qdev_prop_set_uint32(dev, "ram-size", machine->ram_size);
|
qdev_prop_set_uint32(dev, "ram-size", machine->ram_size);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
|
/* RTC */
|
||||||
|
dev = DEVICE(isa_create(isa_bus, TYPE_MC146818_RTC));
|
||||||
|
qdev_prop_set_int32(dev, "base_year", 1900);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
/* initialize CMOS checksums */
|
/* initialize CMOS checksums */
|
||||||
cmos_checksum = 0x6aa9;
|
cmos_checksum = 0x6aa9;
|
||||||
qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL,
|
qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL,
|
||||||
|
@ -500,7 +500,10 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
|
|||||||
_FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
|
_FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
|
||||||
|
|
||||||
if (env->spr_cb[SPR_PURR].oea_read) {
|
if (env->spr_cb[SPR_PURR].oea_read) {
|
||||||
_FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
|
_FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1)));
|
||||||
|
}
|
||||||
|
if (env->spr_cb[SPR_SPURR].oea_read) {
|
||||||
|
_FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) {
|
if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) {
|
||||||
@ -2122,6 +2125,7 @@ static const VMStateDescription vmstate_spapr = {
|
|||||||
&vmstate_spapr_cap_cfpc,
|
&vmstate_spapr_cap_cfpc,
|
||||||
&vmstate_spapr_cap_sbbc,
|
&vmstate_spapr_cap_sbbc,
|
||||||
&vmstate_spapr_cap_ibs,
|
&vmstate_spapr_cap_ibs,
|
||||||
|
&vmstate_spapr_cap_hpt_maxpagesize,
|
||||||
&vmstate_spapr_irq_map,
|
&vmstate_spapr_irq_map,
|
||||||
&vmstate_spapr_cap_nested_kvm_hv,
|
&vmstate_spapr_cap_nested_kvm_hv,
|
||||||
&vmstate_spapr_dtb,
|
&vmstate_spapr_dtb,
|
||||||
@ -4348,7 +4352,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
|||||||
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON;
|
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON;
|
||||||
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF;
|
smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF;
|
||||||
spapr_caps_add_properties(smc, &error_abort);
|
spapr_caps_add_properties(smc, &error_abort);
|
||||||
smc->irq = &spapr_irq_xics;
|
smc->irq = &spapr_irq_dual;
|
||||||
smc->dr_phb_enabled = true;
|
smc->dr_phb_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4407,18 +4411,7 @@ DEFINE_SPAPR_MACHINE(4_1, "4.1", true);
|
|||||||
/*
|
/*
|
||||||
* pseries-4.0
|
* pseries-4.0
|
||||||
*/
|
*/
|
||||||
static void spapr_machine_4_0_class_options(MachineClass *mc)
|
static void phb_placement_4_0(SpaprMachineState *spapr, uint32_t index,
|
||||||
{
|
|
||||||
spapr_machine_4_1_class_options(mc);
|
|
||||||
compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_SPAPR_MACHINE(4_0, "4.0", false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* pseries-3.1
|
|
||||||
*/
|
|
||||||
static void phb_placement_3_1(SpaprMachineState *spapr, uint32_t index,
|
|
||||||
uint64_t *buid, hwaddr *pio,
|
uint64_t *buid, hwaddr *pio,
|
||||||
hwaddr *mmio32, hwaddr *mmio64,
|
hwaddr *mmio32, hwaddr *mmio64,
|
||||||
unsigned n_dma, uint32_t *liobns,
|
unsigned n_dma, uint32_t *liobns,
|
||||||
@ -4430,6 +4423,22 @@ static void phb_placement_3_1(SpaprMachineState *spapr, uint32_t index,
|
|||||||
*nv2atsd = 0;
|
*nv2atsd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spapr_machine_4_0_class_options(MachineClass *mc)
|
||||||
|
{
|
||||||
|
SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||||
|
|
||||||
|
spapr_machine_4_1_class_options(mc);
|
||||||
|
compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len);
|
||||||
|
smc->phb_placement = phb_placement_4_0;
|
||||||
|
smc->irq = &spapr_irq_xics;
|
||||||
|
smc->pre_4_1_migration = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_SPAPR_MACHINE(4_0, "4.0", false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pseries-3.1
|
||||||
|
*/
|
||||||
static void spapr_machine_3_1_class_options(MachineClass *mc)
|
static void spapr_machine_3_1_class_options(MachineClass *mc)
|
||||||
{
|
{
|
||||||
SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||||
@ -4445,7 +4454,6 @@ static void spapr_machine_3_1_class_options(MachineClass *mc)
|
|||||||
smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN;
|
smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN;
|
||||||
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
|
smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
|
||||||
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF;
|
smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF;
|
||||||
smc->phb_placement = phb_placement_3_1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_SPAPR_MACHINE(3_1, "3.1", false);
|
DEFINE_SPAPR_MACHINE(3_1, "3.1", false);
|
||||||
|
@ -64,6 +64,7 @@ typedef struct SpaprCapabilityInfo {
|
|||||||
void (*apply)(SpaprMachineState *spapr, uint8_t val, Error **errp);
|
void (*apply)(SpaprMachineState *spapr, uint8_t val, Error **errp);
|
||||||
void (*cpu_apply)(SpaprMachineState *spapr, PowerPCCPU *cpu,
|
void (*cpu_apply)(SpaprMachineState *spapr, PowerPCCPU *cpu,
|
||||||
uint8_t val, Error **errp);
|
uint8_t val, Error **errp);
|
||||||
|
bool (*migrate_needed)(void *opaque);
|
||||||
} SpaprCapabilityInfo;
|
} SpaprCapabilityInfo;
|
||||||
|
|
||||||
static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name,
|
static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name,
|
||||||
@ -350,6 +351,11 @@ static void cap_hpt_maxpagesize_apply(SpaprMachineState *spapr,
|
|||||||
spapr_check_pagesize(spapr, qemu_minrampagesize(), errp);
|
spapr_check_pagesize(spapr, qemu_minrampagesize(), errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cap_hpt_maxpagesize_migrate_needed(void *opaque)
|
||||||
|
{
|
||||||
|
return !SPAPR_MACHINE_GET_CLASS(opaque)->pre_4_1_migration;
|
||||||
|
}
|
||||||
|
|
||||||
static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift,
|
static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift,
|
||||||
uint32_t pshift)
|
uint32_t pshift)
|
||||||
{
|
{
|
||||||
@ -542,6 +548,7 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
|
|||||||
.type = "int",
|
.type = "int",
|
||||||
.apply = cap_hpt_maxpagesize_apply,
|
.apply = cap_hpt_maxpagesize_apply,
|
||||||
.cpu_apply = cap_hpt_maxpagesize_cpu_apply,
|
.cpu_apply = cap_hpt_maxpagesize_cpu_apply,
|
||||||
|
.migrate_needed = cap_hpt_maxpagesize_migrate_needed,
|
||||||
},
|
},
|
||||||
[SPAPR_CAP_NESTED_KVM_HV] = {
|
[SPAPR_CAP_NESTED_KVM_HV] = {
|
||||||
.name = "nested-hv",
|
.name = "nested-hv",
|
||||||
@ -679,8 +686,11 @@ int spapr_caps_post_migration(SpaprMachineState *spapr)
|
|||||||
static bool spapr_cap_##sname##_needed(void *opaque) \
|
static bool spapr_cap_##sname##_needed(void *opaque) \
|
||||||
{ \
|
{ \
|
||||||
SpaprMachineState *spapr = opaque; \
|
SpaprMachineState *spapr = opaque; \
|
||||||
|
bool (*needed)(void *opaque) = \
|
||||||
|
capability_table[cap].migrate_needed; \
|
||||||
\
|
\
|
||||||
return spapr->cmd_line_caps[cap] && \
|
return needed ? needed(opaque) : true && \
|
||||||
|
spapr->cmd_line_caps[cap] && \
|
||||||
(spapr->eff.caps[cap] != \
|
(spapr->eff.caps[cap] != \
|
||||||
spapr->def.caps[cap]); \
|
spapr->def.caps[cap]); \
|
||||||
} \
|
} \
|
||||||
@ -703,6 +713,7 @@ SPAPR_CAP_MIG_STATE(dfp, SPAPR_CAP_DFP);
|
|||||||
SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC);
|
SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC);
|
||||||
SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC);
|
SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC);
|
||||||
SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS);
|
SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS);
|
||||||
|
SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE);
|
||||||
SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV);
|
SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV);
|
||||||
SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
|
SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
|
||||||
SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
|
SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
|
||||||
|
@ -58,9 +58,11 @@ static void spapr_cpu_reset(void *opaque)
|
|||||||
*
|
*
|
||||||
* Disable Power-saving mode Exit Cause exceptions for the CPU, so
|
* Disable Power-saving mode Exit Cause exceptions for the CPU, so
|
||||||
* we don't get spurious wakups before an RTAS start-cpu call.
|
* we don't get spurious wakups before an RTAS start-cpu call.
|
||||||
|
* For the same reason, set PSSCR_EC.
|
||||||
*/
|
*/
|
||||||
lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm);
|
lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm);
|
||||||
lpcr |= LPCR_LPES0 | LPCR_LPES1;
|
lpcr |= LPCR_LPES0 | LPCR_LPES1;
|
||||||
|
env->spr[SPR_PSSCR] |= PSSCR_EC;
|
||||||
|
|
||||||
/* Set RMLS to the max (ie, 16G) */
|
/* Set RMLS to the max (ie, 16G) */
|
||||||
lpcr &= ~LPCR_RMLS;
|
lpcr &= ~LPCR_RMLS;
|
||||||
|
@ -1513,6 +1513,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
|||||||
bool guest_radix;
|
bool guest_radix;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
bool raw_mode_supported = false;
|
bool raw_mode_supported = false;
|
||||||
|
bool guest_xive;
|
||||||
|
|
||||||
cas_pvr = cas_check_pvr(spapr, cpu, &addr, &raw_mode_supported, &local_err);
|
cas_pvr = cas_check_pvr(spapr, cpu, &addr, &raw_mode_supported, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
@ -1545,10 +1546,17 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
|||||||
error_report("guest requested hash and radix MMU, which is invalid.");
|
error_report("guest requested hash and radix MMU, which is invalid.");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
if (spapr_ovec_test(ov5_guest, OV5_XIVE_BOTH)) {
|
||||||
|
error_report("guest requested an invalid interrupt mode");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
/* The radix/hash bit in byte 24 requires special handling: */
|
/* The radix/hash bit in byte 24 requires special handling: */
|
||||||
guest_radix = spapr_ovec_test(ov5_guest, OV5_MMU_RADIX_300);
|
guest_radix = spapr_ovec_test(ov5_guest, OV5_MMU_RADIX_300);
|
||||||
spapr_ovec_clear(ov5_guest, OV5_MMU_RADIX_300);
|
spapr_ovec_clear(ov5_guest, OV5_MMU_RADIX_300);
|
||||||
|
|
||||||
|
guest_xive = spapr_ovec_test(ov5_guest, OV5_XIVE_EXPLOIT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HPT resizing is a bit of a special case, because when enabled
|
* HPT resizing is a bit of a special case, because when enabled
|
||||||
* we assume an HPT guest will support it until it says it
|
* we assume an HPT guest will support it until it says it
|
||||||
@ -1632,6 +1640,24 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
|
|||||||
ov5_updates) != 0);
|
ov5_updates) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the guest asks for an interrupt mode we support; otherwise
|
||||||
|
* terminate the boot.
|
||||||
|
*/
|
||||||
|
if (guest_xive) {
|
||||||
|
if (spapr->irq->ov5 == SPAPR_OV5_XIVE_LEGACY) {
|
||||||
|
error_report(
|
||||||
|
"Guest requested unavailable interrupt mode (XIVE), try the ic-mode=xive or ic-mode=dual machine property");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (spapr->irq->ov5 == SPAPR_OV5_XIVE_EXPLOIT) {
|
||||||
|
error_report(
|
||||||
|
"Guest requested unavailable interrupt mode (XICS), either don't set the ic-mode machine property or try ic-mode=xics or ic-mode=dual");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate a machine reset when we have an update of the
|
* Generate a machine reset when we have an update of the
|
||||||
* interrupt mode. Only required when the machine supports both
|
* interrupt mode. Only required when the machine supports both
|
||||||
|
@ -62,6 +62,35 @@ void spapr_irq_msi_reset(SpaprMachineState *spapr)
|
|||||||
bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr);
|
bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_init_device(SpaprMachineState *spapr,
|
||||||
|
SpaprIrq *irq, Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *machine = MACHINE(spapr);
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) {
|
||||||
|
irq->init_kvm(spapr, &local_err);
|
||||||
|
if (local_err && machine_kernel_irqchip_required(machine)) {
|
||||||
|
error_prepend(&local_err,
|
||||||
|
"kernel_irqchip requested but unavailable: ");
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!local_err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We failed to initialize the KVM device, fallback to
|
||||||
|
* emulated mode
|
||||||
|
*/
|
||||||
|
error_prepend(&local_err, "kernel_irqchip allowed but unavailable: ");
|
||||||
|
warn_report_err(local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
irq->init_emu(spapr, errp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XICS IRQ backend.
|
* XICS IRQ backend.
|
||||||
@ -70,29 +99,8 @@ void spapr_irq_msi_reset(SpaprMachineState *spapr)
|
|||||||
static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs,
|
static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
MachineState *machine = MACHINE(spapr);
|
|
||||||
Object *obj;
|
Object *obj;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
bool xics_kvm = false;
|
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
|
||||||
if (machine_kernel_irqchip_allowed(machine) &&
|
|
||||||
!xics_kvm_init(spapr, &local_err)) {
|
|
||||||
xics_kvm = true;
|
|
||||||
}
|
|
||||||
if (machine_kernel_irqchip_required(machine) && !xics_kvm) {
|
|
||||||
error_prepend(&local_err,
|
|
||||||
"kernel_irqchip requested but unavailable: ");
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error_free(local_err);
|
|
||||||
local_err = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!xics_kvm) {
|
|
||||||
xics_spapr_init(spapr);
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = object_new(TYPE_ICS_SIMPLE);
|
obj = object_new(TYPE_ICS_SIMPLE);
|
||||||
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort);
|
||||||
@ -212,7 +220,13 @@ static void spapr_irq_set_irq_xics(void *opaque, int srcno, int val)
|
|||||||
|
|
||||||
static void spapr_irq_reset_xics(SpaprMachineState *spapr, Error **errp)
|
static void spapr_irq_reset_xics(SpaprMachineState *spapr, Error **errp)
|
||||||
{
|
{
|
||||||
/* TODO: create the KVM XICS device */
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
spapr_irq_init_device(spapr, &spapr_irq_xics, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *spapr_irq_get_nodename_xics(SpaprMachineState *spapr)
|
static const char *spapr_irq_get_nodename_xics(SpaprMachineState *spapr)
|
||||||
@ -220,6 +234,18 @@ static const char *spapr_irq_get_nodename_xics(SpaprMachineState *spapr)
|
|||||||
return XICS_NODENAME;
|
return XICS_NODENAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_init_emu_xics(SpaprMachineState *spapr, Error **errp)
|
||||||
|
{
|
||||||
|
xics_spapr_init(spapr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_init_kvm_xics(SpaprMachineState *spapr, Error **errp)
|
||||||
|
{
|
||||||
|
if (kvm_enabled()) {
|
||||||
|
xics_kvm_init(spapr, errp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define SPAPR_IRQ_XICS_NR_IRQS 0x1000
|
#define SPAPR_IRQ_XICS_NR_IRQS 0x1000
|
||||||
#define SPAPR_IRQ_XICS_NR_MSIS \
|
#define SPAPR_IRQ_XICS_NR_MSIS \
|
||||||
(XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
|
(XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
|
||||||
@ -240,6 +266,8 @@ SpaprIrq spapr_irq_xics = {
|
|||||||
.reset = spapr_irq_reset_xics,
|
.reset = spapr_irq_reset_xics,
|
||||||
.set_irq = spapr_irq_set_irq_xics,
|
.set_irq = spapr_irq_set_irq_xics,
|
||||||
.get_nodename = spapr_irq_get_nodename_xics,
|
.get_nodename = spapr_irq_get_nodename_xics,
|
||||||
|
.init_emu = spapr_irq_init_emu_xics,
|
||||||
|
.init_kvm = spapr_irq_init_kvm_xics,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -248,19 +276,10 @@ SpaprIrq spapr_irq_xics = {
|
|||||||
static void spapr_irq_init_xive(SpaprMachineState *spapr, int nr_irqs,
|
static void spapr_irq_init_xive(SpaprMachineState *spapr, int nr_irqs,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
MachineState *machine = MACHINE(spapr);
|
|
||||||
uint32_t nr_servers = spapr_max_server_number(spapr);
|
uint32_t nr_servers = spapr_max_server_number(spapr);
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* KVM XIVE device not yet available */
|
|
||||||
if (kvm_enabled()) {
|
|
||||||
if (machine_kernel_irqchip_required(machine)) {
|
|
||||||
error_setg(errp, "kernel_irqchip requested. no KVM XIVE support");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dev = qdev_create(NULL, TYPE_SPAPR_XIVE);
|
dev = qdev_create(NULL, TYPE_SPAPR_XIVE);
|
||||||
qdev_prop_set_uint32(dev, "nr-irqs", nr_irqs);
|
qdev_prop_set_uint32(dev, "nr-irqs", nr_irqs);
|
||||||
/*
|
/*
|
||||||
@ -350,12 +369,13 @@ static void spapr_irq_cpu_intc_create_xive(SpaprMachineState *spapr,
|
|||||||
|
|
||||||
static int spapr_irq_post_load_xive(SpaprMachineState *spapr, int version_id)
|
static int spapr_irq_post_load_xive(SpaprMachineState *spapr, int version_id)
|
||||||
{
|
{
|
||||||
return 0;
|
return spapr_xive_post_load(spapr->xive, version_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_irq_reset_xive(SpaprMachineState *spapr, Error **errp)
|
static void spapr_irq_reset_xive(SpaprMachineState *spapr, Error **errp)
|
||||||
{
|
{
|
||||||
CPUState *cs;
|
CPUState *cs;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
CPU_FOREACH(cs) {
|
CPU_FOREACH(cs) {
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
@ -364,6 +384,12 @@ static void spapr_irq_reset_xive(SpaprMachineState *spapr, Error **errp)
|
|||||||
spapr_xive_set_tctx_os_cam(spapr_cpu_state(cpu)->tctx);
|
spapr_xive_set_tctx_os_cam(spapr_cpu_state(cpu)->tctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spapr_irq_init_device(spapr, &spapr_irq_xive, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Activate the XIVE MMIOs */
|
/* Activate the XIVE MMIOs */
|
||||||
spapr_xive_mmio_set_enabled(spapr->xive, true);
|
spapr_xive_mmio_set_enabled(spapr->xive, true);
|
||||||
}
|
}
|
||||||
@ -372,7 +398,11 @@ static void spapr_irq_set_irq_xive(void *opaque, int srcno, int val)
|
|||||||
{
|
{
|
||||||
SpaprMachineState *spapr = opaque;
|
SpaprMachineState *spapr = opaque;
|
||||||
|
|
||||||
xive_source_set_irq(&spapr->xive->source, srcno, val);
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
kvmppc_xive_source_set_irq(&spapr->xive->source, srcno, val);
|
||||||
|
} else {
|
||||||
|
xive_source_set_irq(&spapr->xive->source, srcno, val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *spapr_irq_get_nodename_xive(SpaprMachineState *spapr)
|
static const char *spapr_irq_get_nodename_xive(SpaprMachineState *spapr)
|
||||||
@ -380,6 +410,18 @@ static const char *spapr_irq_get_nodename_xive(SpaprMachineState *spapr)
|
|||||||
return spapr->xive->nodename;
|
return spapr->xive->nodename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_init_emu_xive(SpaprMachineState *spapr, Error **errp)
|
||||||
|
{
|
||||||
|
spapr_xive_init(spapr->xive, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spapr_irq_init_kvm_xive(SpaprMachineState *spapr, Error **errp)
|
||||||
|
{
|
||||||
|
if (kvm_enabled()) {
|
||||||
|
kvmppc_xive_connect(spapr->xive, errp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XIVE uses the full IRQ number space. Set it to 8K to be compatible
|
* XIVE uses the full IRQ number space. Set it to 8K to be compatible
|
||||||
* with XICS.
|
* with XICS.
|
||||||
@ -404,6 +446,8 @@ SpaprIrq spapr_irq_xive = {
|
|||||||
.reset = spapr_irq_reset_xive,
|
.reset = spapr_irq_reset_xive,
|
||||||
.set_irq = spapr_irq_set_irq_xive,
|
.set_irq = spapr_irq_set_irq_xive,
|
||||||
.get_nodename = spapr_irq_get_nodename_xive,
|
.get_nodename = spapr_irq_get_nodename_xive,
|
||||||
|
.init_emu = spapr_irq_init_emu_xive,
|
||||||
|
.init_kvm = spapr_irq_init_kvm_xive,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -428,14 +472,8 @@ static SpaprIrq *spapr_irq_current(SpaprMachineState *spapr)
|
|||||||
static void spapr_irq_init_dual(SpaprMachineState *spapr, int nr_irqs,
|
static void spapr_irq_init_dual(SpaprMachineState *spapr, int nr_irqs,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
MachineState *machine = MACHINE(spapr);
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) {
|
|
||||||
error_setg(errp, "No KVM support for the 'dual' machine");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spapr_irq_xics.init(spapr, spapr_irq_xics.nr_irqs, &local_err);
|
spapr_irq_xics.init(spapr, spapr_irq_xics.nr_irqs, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@ -514,6 +552,9 @@ static int spapr_irq_post_load_dual(SpaprMachineState *spapr, int version_id)
|
|||||||
* defaults to XICS at startup.
|
* defaults to XICS at startup.
|
||||||
*/
|
*/
|
||||||
if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) {
|
if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) {
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
xics_kvm_disconnect(spapr, &error_fatal);
|
||||||
|
}
|
||||||
spapr_irq_xive.reset(spapr, &error_fatal);
|
spapr_irq_xive.reset(spapr, &error_fatal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,12 +563,30 @@ static int spapr_irq_post_load_dual(SpaprMachineState *spapr, int version_id)
|
|||||||
|
|
||||||
static void spapr_irq_reset_dual(SpaprMachineState *spapr, Error **errp)
|
static void spapr_irq_reset_dual(SpaprMachineState *spapr, Error **errp)
|
||||||
{
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deactivate the XIVE MMIOs. The XIVE backend will reenable them
|
* Deactivate the XIVE MMIOs. The XIVE backend will reenable them
|
||||||
* if selected.
|
* if selected.
|
||||||
*/
|
*/
|
||||||
spapr_xive_mmio_set_enabled(spapr->xive, false);
|
spapr_xive_mmio_set_enabled(spapr->xive, false);
|
||||||
|
|
||||||
|
/* Destroy all KVM devices */
|
||||||
|
if (kvm_irqchip_in_kernel()) {
|
||||||
|
xics_kvm_disconnect(spapr, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
error_prepend(errp, "KVM XICS disconnect failed: ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kvmppc_xive_disconnect(spapr->xive, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
error_prepend(errp, "KVM XIVE disconnect failed: ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spapr_irq_current(spapr)->reset(spapr, errp);
|
spapr_irq_current(spapr)->reset(spapr, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +624,8 @@ SpaprIrq spapr_irq_dual = {
|
|||||||
.reset = spapr_irq_reset_dual,
|
.reset = spapr_irq_reset_dual,
|
||||||
.set_irq = spapr_irq_set_irq_dual,
|
.set_irq = spapr_irq_set_irq_dual,
|
||||||
.get_nodename = spapr_irq_get_nodename_dual,
|
.get_nodename = spapr_irq_get_nodename_dual,
|
||||||
|
.init_emu = NULL, /* should not be used */
|
||||||
|
.init_kvm = NULL, /* should not be used */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -763,6 +824,9 @@ SpaprIrq spapr_irq_xics_legacy = {
|
|||||||
.dt_populate = spapr_dt_xics,
|
.dt_populate = spapr_dt_xics,
|
||||||
.cpu_intc_create = spapr_irq_cpu_intc_create_xics,
|
.cpu_intc_create = spapr_irq_cpu_intc_create_xics,
|
||||||
.post_load = spapr_irq_post_load_xics,
|
.post_load = spapr_irq_post_load_xics,
|
||||||
|
.reset = spapr_irq_reset_xics,
|
||||||
.set_irq = spapr_irq_set_irq_xics,
|
.set_irq = spapr_irq_set_irq_xics,
|
||||||
.get_nodename = spapr_irq_get_nodename_xics,
|
.get_nodename = spapr_irq_get_nodename_xics,
|
||||||
|
.init_emu = spapr_irq_init_emu_xics,
|
||||||
|
.init_kvm = spapr_irq_init_kvm_xics,
|
||||||
};
|
};
|
||||||
|
@ -177,6 +177,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr,
|
|||||||
} else {
|
} else {
|
||||||
lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR);
|
lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR);
|
||||||
}
|
}
|
||||||
|
env->spr[SPR_PSSCR] &= ~PSSCR_EC;
|
||||||
}
|
}
|
||||||
ppc_store_lpcr(newcpu, lpcr);
|
ppc_store_lpcr(newcpu, lpcr);
|
||||||
|
|
||||||
@ -205,8 +206,11 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||||||
|
|
||||||
/* Disable Power-saving mode Exit Cause exceptions for the CPU.
|
/* Disable Power-saving mode Exit Cause exceptions for the CPU.
|
||||||
* This could deliver an interrupt on a dying CPU and crash the
|
* This could deliver an interrupt on a dying CPU and crash the
|
||||||
* guest */
|
* guest.
|
||||||
|
* For the same reason, set PSSCR_EC.
|
||||||
|
*/
|
||||||
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
|
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
|
||||||
|
env->spr[SPR_PSSCR] |= PSSCR_EC;
|
||||||
cs->halted = 1;
|
cs->halted = 1;
|
||||||
kvmppc_set_reg_ppc_online(cpu, 0);
|
kvmppc_set_reg_ppc_online(cpu, 0);
|
||||||
qemu_cpu_kick(cs);
|
qemu_cpu_kick(cs);
|
||||||
|
@ -119,6 +119,7 @@ struct SpaprMachineClass {
|
|||||||
bool pre_2_10_has_unused_icps;
|
bool pre_2_10_has_unused_icps;
|
||||||
bool legacy_irq_allocation;
|
bool legacy_irq_allocation;
|
||||||
bool broken_host_serial_model; /* present real host info to the guest */
|
bool broken_host_serial_model; /* present real host info to the guest */
|
||||||
|
bool pre_4_1_migration; /* don't migrate hpt-max-page-size */
|
||||||
|
|
||||||
void (*phb_placement)(SpaprMachineState *spapr, uint32_t index,
|
void (*phb_placement)(SpaprMachineState *spapr, uint32_t index,
|
||||||
uint64_t *buid, hwaddr *pio,
|
uint64_t *buid, hwaddr *pio,
|
||||||
@ -849,6 +850,7 @@ extern const VMStateDescription vmstate_spapr_cap_dfp;
|
|||||||
extern const VMStateDescription vmstate_spapr_cap_cfpc;
|
extern const VMStateDescription vmstate_spapr_cap_cfpc;
|
||||||
extern const VMStateDescription vmstate_spapr_cap_sbbc;
|
extern const VMStateDescription vmstate_spapr_cap_sbbc;
|
||||||
extern const VMStateDescription vmstate_spapr_cap_ibs;
|
extern const VMStateDescription vmstate_spapr_cap_ibs;
|
||||||
|
extern const VMStateDescription vmstate_spapr_cap_hpt_maxpagesize;
|
||||||
extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv;
|
extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv;
|
||||||
extern const VMStateDescription vmstate_spapr_cap_large_decr;
|
extern const VMStateDescription vmstate_spapr_cap_large_decr;
|
||||||
extern const VMStateDescription vmstate_spapr_cap_ccf_assist;
|
extern const VMStateDescription vmstate_spapr_cap_ccf_assist;
|
||||||
|
@ -48,6 +48,8 @@ typedef struct SpaprIrq {
|
|||||||
void (*reset)(SpaprMachineState *spapr, Error **errp);
|
void (*reset)(SpaprMachineState *spapr, Error **errp);
|
||||||
void (*set_irq)(void *opaque, int srcno, int val);
|
void (*set_irq)(void *opaque, int srcno, int val);
|
||||||
const char *(*get_nodename)(SpaprMachineState *spapr);
|
const char *(*get_nodename)(SpaprMachineState *spapr);
|
||||||
|
void (*init_emu)(SpaprMachineState *spapr, Error **errp);
|
||||||
|
void (*init_kvm)(SpaprMachineState *spapr, Error **errp);
|
||||||
} SpaprIrq;
|
} SpaprIrq;
|
||||||
|
|
||||||
extern SpaprIrq spapr_irq_xics;
|
extern SpaprIrq spapr_irq_xics;
|
||||||
|
@ -38,16 +38,55 @@ typedef struct SpaprXive {
|
|||||||
/* TIMA mapping address */
|
/* TIMA mapping address */
|
||||||
hwaddr tm_base;
|
hwaddr tm_base;
|
||||||
MemoryRegion tm_mmio;
|
MemoryRegion tm_mmio;
|
||||||
|
|
||||||
|
/* KVM support */
|
||||||
|
int fd;
|
||||||
|
void *tm_mmap;
|
||||||
|
VMChangeStateEntry *change;
|
||||||
} SpaprXive;
|
} SpaprXive;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The sPAPR machine has a unique XIVE IC device. Assign a fixed value
|
||||||
|
* to the controller block id value. It can nevertheless be changed
|
||||||
|
* for testing purpose.
|
||||||
|
*/
|
||||||
|
#define SPAPR_XIVE_BLOCK_ID 0x0
|
||||||
|
|
||||||
bool spapr_xive_irq_claim(SpaprXive *xive, uint32_t lisn, bool lsi);
|
bool spapr_xive_irq_claim(SpaprXive *xive, uint32_t lisn, bool lsi);
|
||||||
bool spapr_xive_irq_free(SpaprXive *xive, uint32_t lisn);
|
bool spapr_xive_irq_free(SpaprXive *xive, uint32_t lisn);
|
||||||
void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon);
|
void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon);
|
||||||
|
int spapr_xive_post_load(SpaprXive *xive, int version_id);
|
||||||
|
|
||||||
void spapr_xive_hcall_init(SpaprMachineState *spapr);
|
void spapr_xive_hcall_init(SpaprMachineState *spapr);
|
||||||
void spapr_dt_xive(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt,
|
void spapr_dt_xive(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt,
|
||||||
uint32_t phandle);
|
uint32_t phandle);
|
||||||
void spapr_xive_set_tctx_os_cam(XiveTCTX *tctx);
|
void spapr_xive_set_tctx_os_cam(XiveTCTX *tctx);
|
||||||
void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable);
|
void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable);
|
||||||
|
void spapr_xive_map_mmio(SpaprXive *xive);
|
||||||
|
|
||||||
|
int spapr_xive_end_to_target(uint8_t end_blk, uint32_t end_idx,
|
||||||
|
uint32_t *out_server, uint8_t *out_prio);
|
||||||
|
void spapr_xive_init(SpaprXive *xive, Error **errp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KVM XIVE device helpers
|
||||||
|
*/
|
||||||
|
void kvmppc_xive_connect(SpaprXive *xive, Error **errp);
|
||||||
|
void kvmppc_xive_disconnect(SpaprXive *xive, Error **errp);
|
||||||
|
void kvmppc_xive_reset(SpaprXive *xive, Error **errp);
|
||||||
|
void kvmppc_xive_set_source_config(SpaprXive *xive, uint32_t lisn, XiveEAS *eas,
|
||||||
|
Error **errp);
|
||||||
|
void kvmppc_xive_sync_source(SpaprXive *xive, uint32_t lisn, Error **errp);
|
||||||
|
uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset,
|
||||||
|
uint64_t data, bool write);
|
||||||
|
void kvmppc_xive_set_queue_config(SpaprXive *xive, uint8_t end_blk,
|
||||||
|
uint32_t end_idx, XiveEND *end,
|
||||||
|
Error **errp);
|
||||||
|
void kvmppc_xive_get_queue_config(SpaprXive *xive, uint8_t end_blk,
|
||||||
|
uint32_t end_idx, XiveEND *end,
|
||||||
|
Error **errp);
|
||||||
|
void kvmppc_xive_synchronize_state(SpaprXive *xive, Error **errp);
|
||||||
|
int kvmppc_xive_pre_save(SpaprXive *xive);
|
||||||
|
int kvmppc_xive_post_load(SpaprXive *xive, int version_id);
|
||||||
|
|
||||||
#endif /* PPC_SPAPR_XIVE_H */
|
#endif /* PPC_SPAPR_XIVE_H */
|
||||||
|
@ -119,6 +119,7 @@ struct ICSState {
|
|||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
ICSIRQState *irqs;
|
ICSIRQState *irqs;
|
||||||
XICSFabric *xics;
|
XICSFabric *xics;
|
||||||
|
bool init; /* sPAPR ICS device initialized */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ICS_PROP_XICS "xics"
|
#define ICS_PROP_XICS "xics"
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
void spapr_dt_xics(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt,
|
void spapr_dt_xics(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt,
|
||||||
uint32_t phandle);
|
uint32_t phandle);
|
||||||
int xics_kvm_init(SpaprMachineState *spapr, Error **errp);
|
int xics_kvm_init(SpaprMachineState *spapr, Error **errp);
|
||||||
|
void xics_kvm_disconnect(SpaprMachineState *spapr, Error **errp);
|
||||||
void xics_spapr_init(SpaprMachineState *spapr);
|
void xics_spapr_init(SpaprMachineState *spapr);
|
||||||
|
|
||||||
#endif /* XICS_SPAPR_H */
|
#endif /* XICS_SPAPR_H */
|
||||||
|
@ -140,6 +140,7 @@
|
|||||||
#ifndef PPC_XIVE_H
|
#ifndef PPC_XIVE_H
|
||||||
#define PPC_XIVE_H
|
#define PPC_XIVE_H
|
||||||
|
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
#include "hw/qdev-core.h"
|
#include "hw/qdev-core.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/ppc/xive_regs.h"
|
#include "hw/ppc/xive_regs.h"
|
||||||
@ -194,6 +195,9 @@ typedef struct XiveSource {
|
|||||||
uint32_t esb_shift;
|
uint32_t esb_shift;
|
||||||
MemoryRegion esb_mmio;
|
MemoryRegion esb_mmio;
|
||||||
|
|
||||||
|
/* KVM support */
|
||||||
|
void *esb_mmap;
|
||||||
|
|
||||||
XiveNotifier *xive;
|
XiveNotifier *xive;
|
||||||
} XiveSource;
|
} XiveSource;
|
||||||
|
|
||||||
@ -423,4 +427,14 @@ static inline uint32_t xive_nvt_cam_line(uint8_t nvt_blk, uint32_t nvt_idx)
|
|||||||
return (nvt_blk << 19) | nvt_idx;
|
return (nvt_blk << 19) | nvt_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KVM XIVE device helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
void kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp);
|
||||||
|
void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val);
|
||||||
|
void kvmppc_xive_cpu_connect(XiveTCTX *tctx, Error **errp);
|
||||||
|
void kvmppc_xive_cpu_synchronize_state(XiveTCTX *tctx, Error **errp);
|
||||||
|
void kvmppc_xive_cpu_get_state(XiveTCTX *tctx, Error **errp);
|
||||||
|
|
||||||
#endif /* PPC_XIVE_H */
|
#endif /* PPC_XIVE_H */
|
||||||
|
@ -208,6 +208,12 @@ typedef struct XiveEND {
|
|||||||
#define xive_end_is_backlog(end) (be32_to_cpu((end)->w0) & END_W0_BACKLOG)
|
#define xive_end_is_backlog(end) (be32_to_cpu((end)->w0) & END_W0_BACKLOG)
|
||||||
#define xive_end_is_escalate(end) (be32_to_cpu((end)->w0) & END_W0_ESCALATE_CTL)
|
#define xive_end_is_escalate(end) (be32_to_cpu((end)->w0) & END_W0_ESCALATE_CTL)
|
||||||
|
|
||||||
|
static inline uint64_t xive_end_qaddr(XiveEND *end)
|
||||||
|
{
|
||||||
|
return ((uint64_t) be32_to_cpu(end->w2) & 0x0fffffff) << 32 |
|
||||||
|
be32_to_cpu(end->w3);
|
||||||
|
}
|
||||||
|
|
||||||
/* Notification Virtual Target (NVT) */
|
/* Notification Virtual Target (NVT) */
|
||||||
typedef struct XiveNVT {
|
typedef struct XiveNVT {
|
||||||
uint32_t w0;
|
uint32_t w0;
|
||||||
|
@ -89,6 +89,7 @@ qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n);
|
|||||||
void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr);
|
void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr);
|
||||||
void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
|
void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
|
||||||
int priority);
|
int priority);
|
||||||
|
void sysbus_mmio_unmap(SysBusDevice *dev, int n);
|
||||||
void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
|
void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
|
||||||
MemoryRegion *mem);
|
MemoryRegion *mem);
|
||||||
MemoryRegion *sysbus_address_space(SysBusDevice *dev);
|
MemoryRegion *sysbus_address_space(SysBusDevice *dev);
|
||||||
|
@ -180,18 +180,6 @@ DEF_HELPER_3(vmuloub, void, avr, avr, avr)
|
|||||||
DEF_HELPER_3(vmulouh, void, avr, avr, avr)
|
DEF_HELPER_3(vmulouh, void, avr, avr, avr)
|
||||||
DEF_HELPER_3(vmulouw, void, avr, avr, avr)
|
DEF_HELPER_3(vmulouw, void, avr, avr, avr)
|
||||||
DEF_HELPER_3(vmuluwm, void, avr, avr, avr)
|
DEF_HELPER_3(vmuluwm, void, avr, avr, avr)
|
||||||
DEF_HELPER_3(vsrab, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsrah, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsraw, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsrad, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsrb, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsrh, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsrw, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsrd, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vslb, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vslh, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vslw, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vsld, void, avr, avr, avr)
|
|
||||||
DEF_HELPER_3(vslo, void, avr, avr, avr)
|
DEF_HELPER_3(vslo, void, avr, avr, avr)
|
||||||
DEF_HELPER_3(vsro, void, avr, avr, avr)
|
DEF_HELPER_3(vsro, void, avr, avr, avr)
|
||||||
DEF_HELPER_3(vsrv, void, avr, avr, avr)
|
DEF_HELPER_3(vsrv, void, avr, avr, avr)
|
||||||
|
@ -1791,23 +1791,6 @@ VSHIFT(l, 1)
|
|||||||
VSHIFT(r, 0)
|
VSHIFT(r, 0)
|
||||||
#undef VSHIFT
|
#undef VSHIFT
|
||||||
|
|
||||||
#define VSL(suffix, element, mask) \
|
|
||||||
void helper_vsl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
|
|
||||||
{ \
|
|
||||||
int i; \
|
|
||||||
\
|
|
||||||
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
|
|
||||||
unsigned int shift = b->element[i] & mask; \
|
|
||||||
\
|
|
||||||
r->element[i] = a->element[i] << shift; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
VSL(b, u8, 0x7)
|
|
||||||
VSL(h, u16, 0x0F)
|
|
||||||
VSL(w, u32, 0x1F)
|
|
||||||
VSL(d, u64, 0x3F)
|
|
||||||
#undef VSL
|
|
||||||
|
|
||||||
void helper_vslv(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
void helper_vslv(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -1815,10 +1798,10 @@ void helper_vslv(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
|||||||
|
|
||||||
size = ARRAY_SIZE(r->u8);
|
size = ARRAY_SIZE(r->u8);
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
shift = b->u8[i] & 0x7; /* extract shift value */
|
shift = b->VsrB(i) & 0x7; /* extract shift value */
|
||||||
bytes = (a->u8[i] << 8) + /* extract adjacent bytes */
|
bytes = (a->VsrB(i) << 8) + /* extract adjacent bytes */
|
||||||
(((i + 1) < size) ? a->u8[i + 1] : 0);
|
(((i + 1) < size) ? a->VsrB(i + 1) : 0);
|
||||||
r->u8[i] = (bytes << shift) >> 8; /* shift and store result */
|
r->VsrB(i) = (bytes << shift) >> 8; /* shift and store result */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1833,10 +1816,10 @@ void helper_vsrv(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
|||||||
* order will guarantee that computed result is not fed back.
|
* order will guarantee that computed result is not fed back.
|
||||||
*/
|
*/
|
||||||
for (i = ARRAY_SIZE(r->u8) - 1; i >= 0; i--) {
|
for (i = ARRAY_SIZE(r->u8) - 1; i >= 0; i--) {
|
||||||
shift = b->u8[i] & 0x7; /* extract shift value */
|
shift = b->VsrB(i) & 0x7; /* extract shift value */
|
||||||
bytes = ((i ? a->u8[i - 1] : 0) << 8) + a->u8[i];
|
bytes = ((i ? a->VsrB(i - 1) : 0) << 8) + a->VsrB(i);
|
||||||
/* extract adjacent bytes */
|
/* extract adjacent bytes */
|
||||||
r->u8[i] = (bytes >> shift) & 0xFF; /* shift and store result */
|
r->VsrB(i) = (bytes >> shift) & 0xFF; /* shift and store result */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1980,26 +1963,6 @@ VNEG(vnegw, s32)
|
|||||||
VNEG(vnegd, s64)
|
VNEG(vnegd, s64)
|
||||||
#undef VNEG
|
#undef VNEG
|
||||||
|
|
||||||
#define VSR(suffix, element, mask) \
|
|
||||||
void helper_vsr##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
|
|
||||||
{ \
|
|
||||||
int i; \
|
|
||||||
\
|
|
||||||
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
|
|
||||||
unsigned int shift = b->element[i] & mask; \
|
|
||||||
r->element[i] = a->element[i] >> shift; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
VSR(ab, s8, 0x7)
|
|
||||||
VSR(ah, s16, 0xF)
|
|
||||||
VSR(aw, s32, 0x1F)
|
|
||||||
VSR(ad, s64, 0x3F)
|
|
||||||
VSR(b, u8, 0x7)
|
|
||||||
VSR(h, u16, 0xF)
|
|
||||||
VSR(w, u32, 0x1F)
|
|
||||||
VSR(d, u64, 0x3F)
|
|
||||||
#undef VSR
|
|
||||||
|
|
||||||
void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
||||||
{
|
{
|
||||||
int sh = (b->VsrB(0xf) >> 3) & 0xf;
|
int sh = (b->VsrB(0xf) >> 3) & 0xf;
|
||||||
@ -2053,7 +2016,7 @@ void helper_vsum2sws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
|||||||
for (i = 0; i < ARRAY_SIZE(r->u64); i++) {
|
for (i = 0; i < ARRAY_SIZE(r->u64); i++) {
|
||||||
int64_t t = (int64_t)b->VsrSW(upper + i * 2);
|
int64_t t = (int64_t)b->VsrSW(upper + i * 2);
|
||||||
|
|
||||||
result.VsrW(i) = 0;
|
result.VsrD(i) = 0;
|
||||||
for (j = 0; j < ARRAY_SIZE(r->u64); j++) {
|
for (j = 0; j < ARRAY_SIZE(r->u64); j++) {
|
||||||
t += a->VsrSW(2 * i + j);
|
t += a->VsrSW(2 * i + j);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ static int cap_fixup_hcalls;
|
|||||||
static int cap_htm; /* Hardware transactional memory support */
|
static int cap_htm; /* Hardware transactional memory support */
|
||||||
static int cap_mmu_radix;
|
static int cap_mmu_radix;
|
||||||
static int cap_mmu_hash_v3;
|
static int cap_mmu_hash_v3;
|
||||||
|
static int cap_xive;
|
||||||
static int cap_resize_hpt;
|
static int cap_resize_hpt;
|
||||||
static int cap_ppc_pvr_compat;
|
static int cap_ppc_pvr_compat;
|
||||||
static int cap_ppc_safe_cache;
|
static int cap_ppc_safe_cache;
|
||||||
@ -146,6 +147,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
|||||||
cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM);
|
cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM);
|
||||||
cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX);
|
cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX);
|
||||||
cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3);
|
cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3);
|
||||||
|
cap_xive = kvm_vm_check_extension(s, KVM_CAP_PPC_IRQ_XIVE);
|
||||||
cap_resize_hpt = kvm_vm_check_extension(s, KVM_CAP_SPAPR_RESIZE_HPT);
|
cap_resize_hpt = kvm_vm_check_extension(s, KVM_CAP_SPAPR_RESIZE_HPT);
|
||||||
kvmppc_get_cpu_characteristics(s);
|
kvmppc_get_cpu_characteristics(s);
|
||||||
cap_ppc_nested_kvm_hv = kvm_vm_check_extension(s, KVM_CAP_PPC_NESTED_HV);
|
cap_ppc_nested_kvm_hv = kvm_vm_check_extension(s, KVM_CAP_PPC_NESTED_HV);
|
||||||
@ -1721,7 +1723,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
|||||||
trace_kvm_handle_dcr_write();
|
trace_kvm_handle_dcr_write();
|
||||||
ret = kvmppc_handle_dcr_write(env, run->dcr.dcrn, run->dcr.data);
|
ret = kvmppc_handle_dcr_write(env, run->dcr.dcrn, run->dcr.data);
|
||||||
} else {
|
} else {
|
||||||
trace_kvm_handle_drc_read();
|
trace_kvm_handle_dcr_read();
|
||||||
ret = kvmppc_handle_dcr_read(env, run->dcr.dcrn, &run->dcr.data);
|
ret = kvmppc_handle_dcr_read(env, run->dcr.dcrn, &run->dcr.data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2478,6 +2480,11 @@ static int parse_cap_ppc_count_cache_flush_assist(struct kvm_ppc_cpu_char c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool kvmppc_has_cap_xive(void)
|
||||||
|
{
|
||||||
|
return cap_xive;
|
||||||
|
}
|
||||||
|
|
||||||
static void kvmppc_get_cpu_characteristics(KVMState *s)
|
static void kvmppc_get_cpu_characteristics(KVMState *s)
|
||||||
{
|
{
|
||||||
struct kvm_ppc_cpu_char c;
|
struct kvm_ppc_cpu_char c;
|
||||||
|
@ -60,6 +60,7 @@ bool kvmppc_has_cap_fixup_hcalls(void);
|
|||||||
bool kvmppc_has_cap_htm(void);
|
bool kvmppc_has_cap_htm(void);
|
||||||
bool kvmppc_has_cap_mmu_radix(void);
|
bool kvmppc_has_cap_mmu_radix(void);
|
||||||
bool kvmppc_has_cap_mmu_hash_v3(void);
|
bool kvmppc_has_cap_mmu_hash_v3(void);
|
||||||
|
bool kvmppc_has_cap_xive(void);
|
||||||
int kvmppc_get_cap_safe_cache(void);
|
int kvmppc_get_cap_safe_cache(void);
|
||||||
int kvmppc_get_cap_safe_bounds_check(void);
|
int kvmppc_get_cap_safe_bounds_check(void);
|
||||||
int kvmppc_get_cap_safe_indirect_branch(void);
|
int kvmppc_get_cap_safe_indirect_branch(void);
|
||||||
@ -316,6 +317,11 @@ static inline bool kvmppc_has_cap_mmu_hash_v3(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool kvmppc_has_cap_xive(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int kvmppc_get_cap_safe_cache(void)
|
static inline int kvmppc_get_cap_safe_cache(void)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -22,7 +22,7 @@ kvm_failed_put_vpa(void) "Warning: Unable to set VPA information to KVM"
|
|||||||
kvm_failed_get_vpa(void) "Warning: Unable to get VPA information from KVM"
|
kvm_failed_get_vpa(void) "Warning: Unable to get VPA information from KVM"
|
||||||
kvm_injected_interrupt(int irq) "injected interrupt %d"
|
kvm_injected_interrupt(int irq) "injected interrupt %d"
|
||||||
kvm_handle_dcr_write(void) "handle dcr write"
|
kvm_handle_dcr_write(void) "handle dcr write"
|
||||||
kvm_handle_drc_read(void) "handle dcr read"
|
kvm_handle_dcr_read(void) "handle dcr read"
|
||||||
kvm_handle_halt(void) "handle halt"
|
kvm_handle_halt(void) "handle halt"
|
||||||
kvm_handle_papr_hcall(void) "handle PAPR hypercall"
|
kvm_handle_papr_hcall(void) "handle PAPR hypercall"
|
||||||
kvm_handle_epr(void) "handle epr"
|
kvm_handle_epr(void) "handle epr"
|
||||||
|
@ -530,21 +530,21 @@ GEN_VXFORM(vmuleuw, 4, 10);
|
|||||||
GEN_VXFORM(vmulesb, 4, 12);
|
GEN_VXFORM(vmulesb, 4, 12);
|
||||||
GEN_VXFORM(vmulesh, 4, 13);
|
GEN_VXFORM(vmulesh, 4, 13);
|
||||||
GEN_VXFORM(vmulesw, 4, 14);
|
GEN_VXFORM(vmulesw, 4, 14);
|
||||||
GEN_VXFORM(vslb, 2, 4);
|
GEN_VXFORM_V(vslb, MO_8, tcg_gen_gvec_shlv, 2, 4);
|
||||||
GEN_VXFORM(vslh, 2, 5);
|
GEN_VXFORM_V(vslh, MO_16, tcg_gen_gvec_shlv, 2, 5);
|
||||||
GEN_VXFORM(vslw, 2, 6);
|
GEN_VXFORM_V(vslw, MO_32, tcg_gen_gvec_shlv, 2, 6);
|
||||||
GEN_VXFORM(vrlwnm, 2, 6);
|
GEN_VXFORM(vrlwnm, 2, 6);
|
||||||
GEN_VXFORM_DUAL(vslw, PPC_ALTIVEC, PPC_NONE, \
|
GEN_VXFORM_DUAL(vslw, PPC_ALTIVEC, PPC_NONE, \
|
||||||
vrlwnm, PPC_NONE, PPC2_ISA300)
|
vrlwnm, PPC_NONE, PPC2_ISA300)
|
||||||
GEN_VXFORM(vsld, 2, 23);
|
GEN_VXFORM_V(vsld, MO_64, tcg_gen_gvec_shlv, 2, 23);
|
||||||
GEN_VXFORM(vsrb, 2, 8);
|
GEN_VXFORM_V(vsrb, MO_8, tcg_gen_gvec_shrv, 2, 8);
|
||||||
GEN_VXFORM(vsrh, 2, 9);
|
GEN_VXFORM_V(vsrh, MO_16, tcg_gen_gvec_shrv, 2, 9);
|
||||||
GEN_VXFORM(vsrw, 2, 10);
|
GEN_VXFORM_V(vsrw, MO_32, tcg_gen_gvec_shrv, 2, 10);
|
||||||
GEN_VXFORM(vsrd, 2, 27);
|
GEN_VXFORM_V(vsrd, MO_64, tcg_gen_gvec_shrv, 2, 27);
|
||||||
GEN_VXFORM(vsrab, 2, 12);
|
GEN_VXFORM_V(vsrab, MO_8, tcg_gen_gvec_sarv, 2, 12);
|
||||||
GEN_VXFORM(vsrah, 2, 13);
|
GEN_VXFORM_V(vsrah, MO_16, tcg_gen_gvec_sarv, 2, 13);
|
||||||
GEN_VXFORM(vsraw, 2, 14);
|
GEN_VXFORM_V(vsraw, MO_32, tcg_gen_gvec_sarv, 2, 14);
|
||||||
GEN_VXFORM(vsrad, 2, 15);
|
GEN_VXFORM_V(vsrad, MO_64, tcg_gen_gvec_sarv, 2, 15);
|
||||||
GEN_VXFORM(vsrv, 2, 28);
|
GEN_VXFORM(vsrv, 2, 28);
|
||||||
GEN_VXFORM(vslv, 2, 29);
|
GEN_VXFORM(vslv, 2, 29);
|
||||||
GEN_VXFORM(vslo, 6, 16);
|
GEN_VXFORM(vslo, 6, 16);
|
||||||
|
@ -227,7 +227,62 @@ static void gen_lxvb16x(DisasContext *ctx)
|
|||||||
tcg_temp_free_i64(xtl);
|
tcg_temp_free_i64(xtl);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VSX_VECTOR_LOAD_STORE(name, op, indexed) \
|
#define VSX_VECTOR_LOAD(name, op, indexed) \
|
||||||
|
static void gen_##name(DisasContext *ctx) \
|
||||||
|
{ \
|
||||||
|
int xt; \
|
||||||
|
TCGv EA; \
|
||||||
|
TCGv_i64 xth; \
|
||||||
|
TCGv_i64 xtl; \
|
||||||
|
\
|
||||||
|
if (indexed) { \
|
||||||
|
xt = xT(ctx->opcode); \
|
||||||
|
} else { \
|
||||||
|
xt = DQxT(ctx->opcode); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (xt < 32) { \
|
||||||
|
if (unlikely(!ctx->vsx_enabled)) { \
|
||||||
|
gen_exception(ctx, POWERPC_EXCP_VSXU); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
if (unlikely(!ctx->altivec_enabled)) { \
|
||||||
|
gen_exception(ctx, POWERPC_EXCP_VPU); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
xth = tcg_temp_new_i64(); \
|
||||||
|
xtl = tcg_temp_new_i64(); \
|
||||||
|
gen_set_access_type(ctx, ACCESS_INT); \
|
||||||
|
EA = tcg_temp_new(); \
|
||||||
|
if (indexed) { \
|
||||||
|
gen_addr_reg_index(ctx, EA); \
|
||||||
|
} else { \
|
||||||
|
gen_addr_imm_index(ctx, EA, 0x0F); \
|
||||||
|
} \
|
||||||
|
if (ctx->le_mode) { \
|
||||||
|
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \
|
||||||
|
set_cpu_vsrl(xt, xtl); \
|
||||||
|
tcg_gen_addi_tl(EA, EA, 8); \
|
||||||
|
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \
|
||||||
|
set_cpu_vsrh(xt, xth); \
|
||||||
|
} else { \
|
||||||
|
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \
|
||||||
|
set_cpu_vsrh(xt, xth); \
|
||||||
|
tcg_gen_addi_tl(EA, EA, 8); \
|
||||||
|
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \
|
||||||
|
set_cpu_vsrl(xt, xtl); \
|
||||||
|
} \
|
||||||
|
tcg_temp_free(EA); \
|
||||||
|
tcg_temp_free_i64(xth); \
|
||||||
|
tcg_temp_free_i64(xtl); \
|
||||||
|
}
|
||||||
|
|
||||||
|
VSX_VECTOR_LOAD(lxv, ld_i64, 0)
|
||||||
|
VSX_VECTOR_LOAD(lxvx, ld_i64, 1)
|
||||||
|
|
||||||
|
#define VSX_VECTOR_STORE(name, op, indexed) \
|
||||||
static void gen_##name(DisasContext *ctx) \
|
static void gen_##name(DisasContext *ctx) \
|
||||||
{ \
|
{ \
|
||||||
int xt; \
|
int xt; \
|
||||||
@ -265,26 +320,20 @@ static void gen_##name(DisasContext *ctx) \
|
|||||||
} \
|
} \
|
||||||
if (ctx->le_mode) { \
|
if (ctx->le_mode) { \
|
||||||
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \
|
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \
|
||||||
set_cpu_vsrl(xt, xtl); \
|
|
||||||
tcg_gen_addi_tl(EA, EA, 8); \
|
tcg_gen_addi_tl(EA, EA, 8); \
|
||||||
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \
|
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \
|
||||||
set_cpu_vsrh(xt, xth); \
|
|
||||||
} else { \
|
} else { \
|
||||||
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \
|
tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \
|
||||||
set_cpu_vsrh(xt, xth); \
|
|
||||||
tcg_gen_addi_tl(EA, EA, 8); \
|
tcg_gen_addi_tl(EA, EA, 8); \
|
||||||
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \
|
tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \
|
||||||
set_cpu_vsrl(xt, xtl); \
|
|
||||||
} \
|
} \
|
||||||
tcg_temp_free(EA); \
|
tcg_temp_free(EA); \
|
||||||
tcg_temp_free_i64(xth); \
|
tcg_temp_free_i64(xth); \
|
||||||
tcg_temp_free_i64(xtl); \
|
tcg_temp_free_i64(xtl); \
|
||||||
}
|
}
|
||||||
|
|
||||||
VSX_VECTOR_LOAD_STORE(lxv, ld_i64, 0)
|
VSX_VECTOR_STORE(stxv, st_i64, 0)
|
||||||
VSX_VECTOR_LOAD_STORE(stxv, st_i64, 0)
|
VSX_VECTOR_STORE(stxvx, st_i64, 1)
|
||||||
VSX_VECTOR_LOAD_STORE(lxvx, ld_i64, 1)
|
|
||||||
VSX_VECTOR_LOAD_STORE(stxvx, st_i64, 1)
|
|
||||||
|
|
||||||
#ifdef TARGET_PPC64
|
#ifdef TARGET_PPC64
|
||||||
#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \
|
#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \
|
||||||
@ -329,7 +378,6 @@ static void gen_##name(DisasContext *ctx) \
|
|||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
xth = tcg_temp_new_i64(); \
|
xth = tcg_temp_new_i64(); \
|
||||||
get_cpu_vsrh(xth, rD(ctx->opcode) + 32); \
|
|
||||||
gen_set_access_type(ctx, ACCESS_INT); \
|
gen_set_access_type(ctx, ACCESS_INT); \
|
||||||
EA = tcg_temp_new(); \
|
EA = tcg_temp_new(); \
|
||||||
gen_addr_imm_index(ctx, EA, 0x03); \
|
gen_addr_imm_index(ctx, EA, 0x03); \
|
||||||
@ -513,8 +561,8 @@ static void gen_##name(DisasContext *ctx) \
|
|||||||
tcg_temp_free_i64(xth); \
|
tcg_temp_free_i64(xth); \
|
||||||
}
|
}
|
||||||
|
|
||||||
VSX_LOAD_SCALAR_DS(stxsd, st64_i64)
|
VSX_STORE_SCALAR_DS(stxsd, st64_i64)
|
||||||
VSX_LOAD_SCALAR_DS(stxssp, st32fs)
|
VSX_STORE_SCALAR_DS(stxssp, st32fs)
|
||||||
|
|
||||||
static void gen_mfvsrwz(DisasContext *ctx)
|
static void gen_mfvsrwz(DisasContext *ctx)
|
||||||
{
|
{
|
||||||
@ -858,8 +906,8 @@ static void glue(gen_, name)(DisasContext *ctx) \
|
|||||||
xbh = tcg_temp_new_i64(); \
|
xbh = tcg_temp_new_i64(); \
|
||||||
xbl = tcg_temp_new_i64(); \
|
xbl = tcg_temp_new_i64(); \
|
||||||
sgm = tcg_temp_new_i64(); \
|
sgm = tcg_temp_new_i64(); \
|
||||||
set_cpu_vsrh(xB(ctx->opcode), xbh); \
|
get_cpu_vsrh(xbh, xB(ctx->opcode)); \
|
||||||
set_cpu_vsrl(xB(ctx->opcode), xbl); \
|
get_cpu_vsrl(xbl, xB(ctx->opcode)); \
|
||||||
tcg_gen_movi_i64(sgm, sgn_mask); \
|
tcg_gen_movi_i64(sgm, sgn_mask); \
|
||||||
switch (op) { \
|
switch (op) { \
|
||||||
case OP_ABS: { \
|
case OP_ABS: { \
|
||||||
@ -1192,7 +1240,7 @@ static void gen_xxbrq(DisasContext *ctx)
|
|||||||
tcg_gen_bswap64_i64(xtl, xbh);
|
tcg_gen_bswap64_i64(xtl, xbh);
|
||||||
set_cpu_vsrl(xT(ctx->opcode), xtl);
|
set_cpu_vsrl(xT(ctx->opcode), xtl);
|
||||||
tcg_gen_mov_i64(xth, t0);
|
tcg_gen_mov_i64(xth, t0);
|
||||||
set_cpu_vsrl(xT(ctx->opcode), xth);
|
set_cpu_vsrh(xT(ctx->opcode), xth);
|
||||||
|
|
||||||
tcg_temp_free_i64(t0);
|
tcg_temp_free_i64(t0);
|
||||||
tcg_temp_free_i64(xth);
|
tcg_temp_free_i64(xth);
|
||||||
@ -1220,7 +1268,7 @@ static void gen_xxbrw(DisasContext *ctx)
|
|||||||
get_cpu_vsrl(xbl, xB(ctx->opcode));
|
get_cpu_vsrl(xbl, xB(ctx->opcode));
|
||||||
|
|
||||||
gen_bswap32x4(xth, xtl, xbh, xbl);
|
gen_bswap32x4(xth, xtl, xbh, xbl);
|
||||||
set_cpu_vsrl(xT(ctx->opcode), xth);
|
set_cpu_vsrh(xT(ctx->opcode), xth);
|
||||||
set_cpu_vsrl(xT(ctx->opcode), xtl);
|
set_cpu_vsrl(xT(ctx->opcode), xtl);
|
||||||
|
|
||||||
tcg_temp_free_i64(xth);
|
tcg_temp_free_i64(xth);
|
||||||
@ -1355,13 +1403,13 @@ static void gen_xxspltib(DisasContext *ctx)
|
|||||||
int rt = xT(ctx->opcode);
|
int rt = xT(ctx->opcode);
|
||||||
|
|
||||||
if (rt < 32) {
|
if (rt < 32) {
|
||||||
if (unlikely(!ctx->altivec_enabled)) {
|
if (unlikely(!ctx->vsx_enabled)) {
|
||||||
gen_exception(ctx, POWERPC_EXCP_VPU);
|
gen_exception(ctx, POWERPC_EXCP_VSXU);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (unlikely(!ctx->vsx_enabled)) {
|
if (unlikely(!ctx->altivec_enabled)) {
|
||||||
gen_exception(ctx, POWERPC_EXCP_VSXU);
|
gen_exception(ctx, POWERPC_EXCP_VPU);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1820,7 +1868,7 @@ static void gen_xvxsigdp(DisasContext *ctx)
|
|||||||
tcg_gen_movi_i64(t0, 0x0010000000000000);
|
tcg_gen_movi_i64(t0, 0x0010000000000000);
|
||||||
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
|
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0);
|
||||||
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
|
tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0);
|
||||||
tcg_gen_deposit_i64(xth, t0, xbl, 0, 52);
|
tcg_gen_deposit_i64(xtl, t0, xbl, 0, 52);
|
||||||
set_cpu_vsrl(xT(ctx->opcode), xtl);
|
set_cpu_vsrl(xT(ctx->opcode), xtl);
|
||||||
|
|
||||||
tcg_temp_free_i64(t0);
|
tcg_temp_free_i64(t0);
|
||||||
|
@ -107,6 +107,7 @@ docker-image-debian-sparc64-cross: docker-image-debian-sid
|
|||||||
docker-image-debian-mips64-cross: docker-image-debian-sid
|
docker-image-debian-mips64-cross: docker-image-debian-sid
|
||||||
docker-image-debian-riscv64-cross: docker-image-debian-sid
|
docker-image-debian-riscv64-cross: docker-image-debian-sid
|
||||||
docker-image-debian-powerpc-cross: docker-image-debian-sid
|
docker-image-debian-powerpc-cross: docker-image-debian-sid
|
||||||
|
docker-image-debian-ppc64-cross: docker-image-debian-sid
|
||||||
docker-image-travis: NOUSER=1
|
docker-image-travis: NOUSER=1
|
||||||
|
|
||||||
# Specialist build images, sometimes very limited tools
|
# Specialist build images, sometimes very limited tools
|
||||||
|
11
tests/docker/dockerfiles/debian-ppc64-cross.docker
Normal file
11
tests/docker/dockerfiles/debian-ppc64-cross.docker
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#
|
||||||
|
# Docker ppc64 cross-compiler target
|
||||||
|
#
|
||||||
|
# This docker target builds on the debian sid base image which
|
||||||
|
# contains cross compilers for Debian "ports" targets.
|
||||||
|
FROM qemu:debian-sid
|
||||||
|
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
gcc-powerpc64-linux-gnu \
|
||||||
|
libc6-dev-ppc64-cross || { echo "Failed to build - see debian-sid.docker notes"; exit 1; }
|
@ -1,6 +1,9 @@
|
|||||||
ifeq ($(TARGET_NAME),ppc)
|
ifeq ($(TARGET_NAME),ppc)
|
||||||
DOCKER_IMAGE=debian-powerpc-cross
|
DOCKER_IMAGE=debian-powerpc-cross
|
||||||
DOCKER_CROSS_COMPILER=powerpc-linux-gnu-gcc
|
DOCKER_CROSS_COMPILER=powerpc-linux-gnu-gcc
|
||||||
|
else ifeq ($(TARGET_NAME),ppc64)
|
||||||
|
DOCKER_IMAGE=debian-ppc64-cross
|
||||||
|
DOCKER_CROSS_COMPILER=powerpc64-linux-gnu-gcc
|
||||||
else ifeq ($(TARGET_NAME),ppc64le)
|
else ifeq ($(TARGET_NAME),ppc64le)
|
||||||
DOCKER_IMAGE=debian-ppc64el-cross
|
DOCKER_IMAGE=debian-ppc64el-cross
|
||||||
DOCKER_CROSS_COMPILER=powerpc64le-linux-gnu-gcc
|
DOCKER_CROSS_COMPILER=powerpc64le-linux-gnu-gcc
|
||||||
|
Loading…
Reference in New Issue
Block a user