mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-ppc-20211217' of https://github.com/legoater/qemu into staging
ppc 7.0 queue: * General cleanup for Mac machines (Peter) * Fixes for FPU exceptions (Lucas) * Support for new ISA31 instructions (Matheus) * Fixes for ivshmem (Daniel) * Cleanups for PowerNV PHB (Christophe and Cedric) * Updates of PowerNV and pSeries documentation (Leonardo and Daniel) * Fixes for PowerNV (Daniel) * Large cleanup of FPU implementation (Richard) * Removal of SoftTLBs support for PPC74x CPUs (Fabiano) * Fixes for exception models in MPCx and 60x CPUs (Fabiano) * Removal of 401/403 CPUs (Cedric) * Deprecation of taihu machine (Thomas) * Large rework of PPC405 machine (Cedric) * Fixes for VSX instructions (Victor and Matheus) * Fix for e6500 CPU (Fabiano) * Initial support for PMU (Daniel) # gpg: Signature made Fri 17 Dec 2021 09:20:31 AM PST # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-ppc-20211217' of https://github.com/legoater/qemu: (101 commits) ppc/pnv: Use QOM hierarchy to scan PEC PHB4 devices ppc/pnv: Move realize of PEC stacks under the PEC model ppc/pnv: Remove "system-memory" property from PHB4 PEC ppc/pnv: Compute the PHB index from the PHB4 PEC model ppc/pnv: Introduce a num_stack class attribute ppc/pnv: Introduce a "chip" property under the PHB4 model ppc/pnv: Introduce version and device_id class atributes for PHB4 devices ppc/pnv: Introduce a num_pecs class attribute for PHB4 PEC devices ppc/pnv: Use QOM hierarchy to scan PHB3 devices ppc/pnv: Move mapping of the PHB3 CQ regions under pnv_pbcq_realize() ppc/pnv: Drop the "num-phbs" property ppc/pnv: Use the chip class to check the index of PHB3 devices ppc/pnv: Introduce a "chip" property under PHB3 PPC64/TCG: Implement 'rfebb' instruction target/ppc/power8-pmu.c: add PM_RUN_INST_CMPL (0xFA) event target/ppc: enable PMU instruction count target/ppc: enable PMU counter overflow with cycle events target/ppc: PMU: update counters on MMCR1 write target/ppc: PMU: update counters on PMCs r/w target/ppc: PMU basic cycle count for pseries TCG ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -315,6 +315,15 @@ This machine is deprecated because we have enough AST2500 based OpenPOWER
|
||||
machines. It can be easily replaced by the ``witherspoon-bmc`` or the
|
||||
``romulus-bmc`` machines.
|
||||
|
||||
PPC 405 ``taihu`` machine (since 7.0)
|
||||
'''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The PPC 405 CPU is a system-on-a-chip, so all 405 machines are very similar,
|
||||
except for some external periphery. However, the periphery of the ``taihu``
|
||||
machine is hardly emulated at all (e.g. neither the LCD nor the USB part had
|
||||
been implemented), so there is not much value added by this board. Use the
|
||||
``ref405ep`` machine instead.
|
||||
|
||||
Backend options
|
||||
---------------
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
sPAPR hypervisor calls
|
||||
----------------------
|
||||
|
||||
When used with the ``pseries`` machine type, ``qemu-system-ppc64`` implements
|
||||
a set of hypervisor calls (a.k.a. hcalls) defined in the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://cdn.openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
This document is a subset of the Power Architecture Platform Reference (PAPR+)
|
||||
specification (IBM internal only), which is what PowerVM, the IBM proprietary
|
||||
hypervisor, adheres to.
|
||||
|
||||
The subset in LoPAR is selected based on the requirements of Linux as a guest.
|
||||
|
||||
In addition to those calls, we have added our own private hypervisor
|
||||
calls which are mostly used as a private interface between the firmware
|
||||
running in the guest and QEMU.
|
||||
|
||||
All those hypercalls start at hcall number 0xf000 which correspond
|
||||
to an implementation specific range in PAPR.
|
||||
|
||||
H_RTAS (0xf000)
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
RTAS stands for Run-Time Abstraction Sercies and is a set of runtime services
|
||||
generally provided by the firmware inside the guest to the operating system. It
|
||||
predates the existence of hypervisors (it was originally an extension to Open
|
||||
Firmware) and is still used by PAPR and LoPAR to provide various services that
|
||||
are not performance sensitive.
|
||||
|
||||
We currently implement the RTAS services in QEMU itself. The actual RTAS
|
||||
"firmware" blob in the guest is a small stub of a few instructions which
|
||||
calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU.
|
||||
|
||||
Arguments:
|
||||
|
||||
``r3``: ``H_RTAS (0xf000)``
|
||||
|
||||
``r4``: Guest physical address of RTAS parameter block.
|
||||
|
||||
Returns:
|
||||
|
||||
``H_SUCCESS``: Successfully called the RTAS function (RTAS result will have
|
||||
been stored in the parameter block).
|
||||
|
||||
``H_PARAMETER``: Unknown token.
|
||||
|
||||
H_LOGICAL_MEMOP (0xf001)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When the guest runs in "real mode" (in powerpc terminology this means with MMU
|
||||
disabled, i.e. guest effective address equals to guest physical address), it
|
||||
only has access to a subset of memory and no I/Os.
|
||||
|
||||
PAPR and LoPAR provides a set of hypervisor calls to perform cacheable or
|
||||
non-cacheable accesses to any guest physical addresses that the
|
||||
guest can use in order to access IO devices while in real mode.
|
||||
|
||||
This is typically used by the firmware running in the guest.
|
||||
|
||||
However, doing a hypercall for each access is extremely inefficient
|
||||
(even more so when running KVM) when accessing the frame buffer. In
|
||||
that case, things like scrolling become unusably slow.
|
||||
|
||||
This hypercall allows the guest to request a "memory op" to be applied
|
||||
to memory. The supported memory ops at this point are to copy a range
|
||||
of memory (supports overlap of source and destination) and XOR which
|
||||
is used by our SLOF firmware to invert the screen.
|
||||
|
||||
Arguments:
|
||||
|
||||
``r3 ``: ``H_LOGICAL_MEMOP (0xf001)``
|
||||
|
||||
``r4``: Guest physical address of destination.
|
||||
|
||||
``r5``: Guest physical address of source.
|
||||
|
||||
``r6``: Individual element size, defined by the binary logarithm of the
|
||||
desired size. Supported values are:
|
||||
|
||||
``0`` = 1 byte
|
||||
|
||||
``1`` = 2 bytes
|
||||
|
||||
``2`` = 4 bytes
|
||||
|
||||
``3`` = 8 bytes
|
||||
|
||||
``r7``: Number of elements.
|
||||
|
||||
``r8``: Operation. Supported values are:
|
||||
|
||||
``0``: copy
|
||||
|
||||
``1``: xor
|
||||
|
||||
Returns:
|
||||
|
||||
``H_SUCCESS``: Success.
|
||||
|
||||
``H_PARAMETER``: Invalid argument.
|
||||
@@ -1,78 +0,0 @@
|
||||
When used with the "pseries" machine type, QEMU-system-ppc64 implements
|
||||
a set of hypervisor calls using a subset of the server "PAPR" specification
|
||||
(IBM internal at this point), which is also what IBM's proprietary hypervisor
|
||||
adheres too.
|
||||
|
||||
The subset is selected based on the requirements of Linux as a guest.
|
||||
|
||||
In addition to those calls, we have added our own private hypervisor
|
||||
calls which are mostly used as a private interface between the firmware
|
||||
running in the guest and QEMU.
|
||||
|
||||
All those hypercalls start at hcall number 0xf000 which correspond
|
||||
to an implementation specific range in PAPR.
|
||||
|
||||
- H_RTAS (0xf000)
|
||||
|
||||
RTAS is a set of runtime services generally provided by the firmware
|
||||
inside the guest to the operating system. It predates the existence
|
||||
of hypervisors (it was originally an extension to Open Firmware) and
|
||||
is still used by PAPR to provide various services that aren't performance
|
||||
sensitive.
|
||||
|
||||
We currently implement the RTAS services in QEMU itself. The actual RTAS
|
||||
"firmware" blob in the guest is a small stub of a few instructions which
|
||||
calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU.
|
||||
|
||||
Arguments:
|
||||
|
||||
r3 : H_RTAS (0xf000)
|
||||
r4 : Guest physical address of RTAS parameter block
|
||||
|
||||
Returns:
|
||||
|
||||
H_SUCCESS : Successfully called the RTAS function (RTAS result
|
||||
will have been stored in the parameter block)
|
||||
H_PARAMETER : Unknown token
|
||||
|
||||
- H_LOGICAL_MEMOP (0xf001)
|
||||
|
||||
When the guest runs in "real mode" (in powerpc lingua this means
|
||||
with MMU disabled, ie guest effective == guest physical), it only
|
||||
has access to a subset of memory and no IOs.
|
||||
|
||||
PAPR provides a set of hypervisor calls to perform cacheable or
|
||||
non-cacheable accesses to any guest physical addresses that the
|
||||
guest can use in order to access IO devices while in real mode.
|
||||
|
||||
This is typically used by the firmware running in the guest.
|
||||
|
||||
However, doing a hypercall for each access is extremely inefficient
|
||||
(even more so when running KVM) when accessing the frame buffer. In
|
||||
that case, things like scrolling become unusably slow.
|
||||
|
||||
This hypercall allows the guest to request a "memory op" to be applied
|
||||
to memory. The supported memory ops at this point are to copy a range
|
||||
of memory (supports overlap of source and destination) and XOR which
|
||||
is used by our SLOF firmware to invert the screen.
|
||||
|
||||
Arguments:
|
||||
|
||||
r3: H_LOGICAL_MEMOP (0xf001)
|
||||
r4: Guest physical address of destination
|
||||
r5: Guest physical address of source
|
||||
r6: Individual element size
|
||||
0 = 1 byte
|
||||
1 = 2 bytes
|
||||
2 = 4 bytes
|
||||
3 = 8 bytes
|
||||
r7: Number of elements
|
||||
r8: Operation
|
||||
0 = copy
|
||||
1 = xor
|
||||
|
||||
Returns:
|
||||
|
||||
H_SUCCESS : Success
|
||||
H_PARAMETER : Invalid argument
|
||||
|
||||
+41
-27
@@ -1,7 +1,7 @@
|
||||
PowerNV family boards (``powernv8``, ``powernv9``)
|
||||
PowerNV family boards (``powernv8``, ``powernv9``, ``powernv10``)
|
||||
==================================================================
|
||||
|
||||
PowerNV (as Non-Virtualized) is the "baremetal" platform using the
|
||||
PowerNV (as Non-Virtualized) is the "bare metal" platform using the
|
||||
OPAL firmware. It runs Linux on IBM and OpenPOWER systems and it can
|
||||
be used as an hypervisor OS, running KVM guests, or simply as a host
|
||||
OS.
|
||||
@@ -16,16 +16,14 @@ Supported devices
|
||||
-----------------
|
||||
|
||||
* Multi processor support for POWER8, POWER8NVL and POWER9.
|
||||
* XSCOM, serial communication sideband bus to configure chiplets
|
||||
* Simple LPC Controller
|
||||
* Processor Service Interface (PSI) Controller
|
||||
* Interrupt Controller, XICS (POWER8) and XIVE (POWER9)
|
||||
* POWER8 PHB3 PCIe Host bridge and POWER9 PHB4 PCIe Host bridge
|
||||
* Simple OCC is an on-chip microcontroller used for power management
|
||||
tasks
|
||||
* iBT device to handle BMC communication, with the internal BMC
|
||||
simulator provided by QEMU or an external BMC such as an Aspeed
|
||||
QEMU machine.
|
||||
* XSCOM, serial communication sideband bus to configure chiplets.
|
||||
* Simple LPC Controller.
|
||||
* Processor Service Interface (PSI) Controller.
|
||||
* Interrupt Controller, XICS (POWER8) and XIVE (POWER9) and XIVE2 (Power10).
|
||||
* POWER8 PHB3 PCIe Host bridge and POWER9 PHB4 PCIe Host bridge.
|
||||
* Simple OCC is an on-chip micro-controller used for power management tasks.
|
||||
* iBT device to handle BMC communication, with the internal BMC simulator
|
||||
provided by QEMU or an external BMC such as an Aspeed QEMU machine.
|
||||
* PNOR containing the different firmware partitions.
|
||||
|
||||
Missing devices
|
||||
@@ -33,31 +31,42 @@ Missing devices
|
||||
|
||||
A lot is missing, among which :
|
||||
|
||||
* POWER10 processor
|
||||
* XIVE2 (POWER10) interrupt controller
|
||||
* I2C controllers (yet to be merged)
|
||||
* NPU/NPU2/NPU3 controllers
|
||||
* EEH support for PCIe Host bridge controllers
|
||||
* NX controller
|
||||
* VAS controller
|
||||
* chipTOD (Time Of Day)
|
||||
* I2C controllers (yet to be merged).
|
||||
* NPU/NPU2/NPU3 controllers.
|
||||
* EEH support for PCIe Host bridge controllers.
|
||||
* NX controller.
|
||||
* VAS controller.
|
||||
* chipTOD (Time Of Day).
|
||||
* Self Boot Engine (SBE).
|
||||
* FSI bus
|
||||
* FSI bus.
|
||||
|
||||
Firmware
|
||||
--------
|
||||
|
||||
The OPAL firmware (OpenPower Abstraction Layer) for OpenPower systems
|
||||
includes the runtime services ``skiboot`` and the bootloader kernel and
|
||||
initramfs ``skiroot``. Source code can be found on GitHub:
|
||||
initramfs ``skiroot``. Source code can be found on the `OpenPOWER account at
|
||||
GitHub <https://github.com/open-power>`_.
|
||||
|
||||
https://github.com/open-power.
|
||||
|
||||
Prebuilt images of ``skiboot`` and ``skiroot`` are made available on the `OpenPOWER <https://github.com/open-power/op-build/releases/>`__ site.
|
||||
Prebuilt images of ``skiboot`` and ``skiroot`` are made available on the
|
||||
`OpenPOWER <https://github.com/open-power/op-build/releases/>`__ site.
|
||||
|
||||
QEMU includes a prebuilt image of ``skiboot`` which is updated when a
|
||||
more recent version is required by the models.
|
||||
|
||||
Current acceleration status
|
||||
---------------------------
|
||||
|
||||
KVM acceleration in Linux Power hosts is provided by the kvm-hv and
|
||||
kvm-pr modules. kvm-hv is adherent to PAPR and it's not compliant with
|
||||
powernv. kvm-pr in theory could be used as a valid accel option but
|
||||
this isn't supported by kvm-pr at this moment.
|
||||
|
||||
To spare users from dealing with not so informative errors when attempting
|
||||
to use accel=kvm, the powernv machine will throw an error informing that
|
||||
KVM is not supported. This can be revisited in the future if kvm-pr (or
|
||||
any other KVM alternative) is usable as KVM accel for this machine.
|
||||
|
||||
Boot options
|
||||
------------
|
||||
|
||||
@@ -83,6 +92,7 @@ and a SATA disk :
|
||||
|
||||
Complex PCIe configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Six PHBs are defined per chip (POWER9) but no default PCI layout is
|
||||
provided (to be compatible with libvirt). One PCI device can be added
|
||||
on any of the available PCIe slots using command line options such as:
|
||||
@@ -157,7 +167,7 @@ one on the command line :
|
||||
The files `palmetto-SDR.bin <http://www.kaod.org/qemu/powernv/palmetto-SDR.bin>`__
|
||||
and `palmetto-FRU.bin <http://www.kaod.org/qemu/powernv/palmetto-FRU.bin>`__
|
||||
define a Sensor Data Record repository and a Field Replaceable Unit
|
||||
inventory for a palmetto BMC. They can be used to extend the QEMU BMC
|
||||
inventory for a Palmetto BMC. They can be used to extend the QEMU BMC
|
||||
simulator.
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -189,4 +199,8 @@ CAVEATS
|
||||
-------
|
||||
|
||||
* No support for multiple HW threads (SMT=1). Same as pseries.
|
||||
* CPU can hang when doing intensive I/Os. Use ``-append powersave=off`` in that case.
|
||||
|
||||
Maintainer contact information
|
||||
------------------------------
|
||||
|
||||
Cédric Le Goater <clg@kaod.org>
|
||||
|
||||
@@ -1,12 +1,238 @@
|
||||
pSeries family boards (``pseries``)
|
||||
===================================
|
||||
|
||||
The Power machine para-virtualized environment described by the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_
|
||||
is called pSeries. This environment is also known as sPAPR, System p guests, or
|
||||
simply Power Linux guests (although it is capable of running other operating
|
||||
systems, such as AIX).
|
||||
|
||||
Even though pSeries is designed to behave as a guest environment, it is also
|
||||
capable of acting as a hypervisor OS, providing, on that role, nested
|
||||
virtualization capabilities.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
|
||||
* Multi processor support for many Power processors generations: POWER7,
|
||||
POWER7+, POWER8, POWER8NVL, POWER9, and Power10. Support for POWER5+ exists,
|
||||
but its state is unknown.
|
||||
* Interrupt Controller, XICS (POWER8) and XIVE (POWER9 and Power10)
|
||||
* vPHB PCIe Host bridge.
|
||||
* vscsi and vnet devices, compatible with the same devices available on a
|
||||
PowerVM hypervisor with VIOS managing LPARs.
|
||||
* Virtio based devices.
|
||||
* PCIe device pass through.
|
||||
|
||||
Missing devices
|
||||
---------------
|
||||
|
||||
* SPICE support.
|
||||
|
||||
Firmware
|
||||
--------
|
||||
|
||||
`SLOF <https://github.com/aik/SLOF>`_ (Slimline Open Firmware) is an
|
||||
implementation of the `IEEE 1275-1994, Standard for Boot (Initialization
|
||||
Configuration) Firmware: Core Requirements and Practices
|
||||
<https://standards.ieee.org/standard/1275-1994.html>`_.
|
||||
|
||||
QEMU includes a prebuilt image of SLOF which is updated when a more recent
|
||||
version is required.
|
||||
|
||||
Build directions
|
||||
----------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./configure --target-list=ppc64-softmmu && make
|
||||
|
||||
Running instructions
|
||||
--------------------
|
||||
|
||||
Someone can select the pSeries machine type by running QEMU with the following
|
||||
options:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
qemu-system-ppc64 -M pseries <other QEMU arguments>
|
||||
|
||||
sPAPR devices
|
||||
-------------
|
||||
|
||||
The sPAPR specification defines a set of para-virtualized devices, which are
|
||||
also supported by the pSeries machine in QEMU and can be instantiated with the
|
||||
``-device`` option:
|
||||
|
||||
* ``spapr-vlan`` : a virtual network interface.
|
||||
* ``spapr-vscsi`` : a virtual SCSI disk interface.
|
||||
* ``spapr-rng`` : a pseudo-device for passing random number generator data to the
|
||||
guest (see the `H_RANDOM hypercall feature
|
||||
<https://wiki.qemu.org/Features/HRandomHypercall>`_ for details).
|
||||
* ``spapr-vty``: a virtual teletype.
|
||||
* ``spapr-pci-host-bridge``: a PCI host bridge.
|
||||
* ``tpm-spapr``: a Trusted Platform Module (TPM).
|
||||
* ``spapr-tpm-proxy``: a TPM proxy.
|
||||
|
||||
These are compatible with the devices historically available for use when
|
||||
running the IBM PowerVM hypervisor with LPARs.
|
||||
|
||||
However, since these devices have originally been specified with another
|
||||
hypervisor and non-Linux guests in mind, you should use the virtio counterparts
|
||||
(virtio-net, virtio-blk/scsi and virtio-rng for instance) if possible instead,
|
||||
since they will most probably give you better performance with Linux guests in a
|
||||
QEMU environment.
|
||||
|
||||
The pSeries machine in QEMU is always instantiated with the following devices:
|
||||
|
||||
* A NVRAM device (``spapr-nvram``).
|
||||
* A virtual teletype (``spapr-vty``).
|
||||
* A PCI host bridge (``spapr-pci-host-bridge``).
|
||||
|
||||
Hence, it is not needed to add them manually, unless you use the ``-nodefaults``
|
||||
command line option in QEMU.
|
||||
|
||||
In the case of the default ``spapr-nvram`` device, if someone wants to make the
|
||||
contents of the NVRAM device persistent, they will need to specify a PFLASH
|
||||
device when starting QEMU, i.e. either use
|
||||
``-drive if=pflash,file=<filename>,format=raw`` to set the default PFLASH
|
||||
device, or specify one with an ID
|
||||
(``-drive if=none,file=<filename>,format=raw,id=pfid``) and pass that ID to the
|
||||
NVRAM device with ``-global spapr-nvram.drive=pfid``.
|
||||
|
||||
sPAPR specification
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The main source of documentation on the sPAPR standard is the `Linux on Power
|
||||
Architecture Reference document (LoPAR)
|
||||
<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
|
||||
However, documentation specific to QEMU's implementation of the specification
|
||||
can also be found in QEMU documentation:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../../specs/ppc-spapr-hcalls.rst
|
||||
../../specs/ppc-spapr-numa.rst
|
||||
../../specs/ppc-spapr-xive.rst
|
||||
|
||||
Other documentation available in QEMU docs directory:
|
||||
|
||||
* Hot plug (``/docs/specs/ppc-spapr-hotplug.txt``).
|
||||
* Hypervisor calls needed by the Ultravisor
|
||||
(``/docs/specs/ppc-spapr-uv-hcalls.txt``).
|
||||
|
||||
Switching between the KVM-PR and KVM-HV kernel module
|
||||
-----------------------------------------------------
|
||||
|
||||
Currently, there are two implementations of KVM on Power, ``kvm_hv.ko`` and
|
||||
``kvm_pr.ko``.
|
||||
|
||||
|
||||
If a host supports both KVM modes, and both KVM kernel modules are loaded, it is
|
||||
possible to switch between the two modes with the ``kvm-type`` parameter:
|
||||
|
||||
* Use ``qemu-system-ppc64 -M pseries,accel=kvm,kvm-type=PR`` to use the
|
||||
``kvm_pr.ko`` kernel module.
|
||||
* Use ``qemu-system-ppc64 -M pseries,accel=kvm,kvm-type=HV`` to use ``kvm_hv.ko``
|
||||
instead.
|
||||
|
||||
KVM-PR
|
||||
^^^^^^
|
||||
|
||||
KVM-PR uses the so-called **PR**\ oblem state of the PPC CPUs to run the guests,
|
||||
i.e. the virtual machine is run in user mode and all privileged instructions
|
||||
trap and have to be emulated by the host. That means you can run KVM-PR inside
|
||||
a pSeries guest (or a PowerVM LPAR for that matter), and that is where it has
|
||||
originated, as historically (prior to POWER7) it was not possible to run Linux
|
||||
on hypervisor mode on a Power processor (this function was restricted to
|
||||
PowerVM, the IBM proprietary hypervisor).
|
||||
|
||||
Because all privileged instructions are trapped, guests that use a lot of
|
||||
privileged instructions run quite slow with KVM-PR. On the other hand, because
|
||||
of that, this kernel module can run on pretty much every PPC hardware, and is
|
||||
able to emulate a lot of guests CPUs. This module can even be used to run other
|
||||
PowerPC guests like an emulated PowerMac.
|
||||
|
||||
As KVM-PR can be run inside a pSeries guest, it can also provide nested
|
||||
virtualization capabilities (i.e. running a guest from within a guest).
|
||||
|
||||
It is important to notice that, as KVM-HV provides a much better execution
|
||||
performance, maintenance work has been much more focused on it in the past
|
||||
years. Maintenance for KVM-PR has been minimal.
|
||||
|
||||
In order to run KVM-PR guests with POWER9 processors, someone will need to start
|
||||
QEMU with ``kernel_irqchip=off`` command line option.
|
||||
|
||||
KVM-HV
|
||||
^^^^^^
|
||||
|
||||
KVM-HV uses the hypervisor mode of more recent Power processors, that allow
|
||||
access to the bare metal hardware directly. Although POWER7 had this capability,
|
||||
it was only starting with POWER8 that this was officially supported by IBM.
|
||||
|
||||
Originally, KVM-HV was only available when running on a PowerNV platform (a.k.a.
|
||||
Power bare metal). Although it runs on a PowerNV platform, it can only be used
|
||||
to start pSeries guests. As the pSeries guest doesn't have access to the
|
||||
hypervisor mode of the Power CPU, it wasn't possible to run KVM-HV on a guest.
|
||||
This limitation has been lifted, and now it is possible to run KVM-HV inside
|
||||
pSeries guests as well, making nested virtualization possible with KVM-HV.
|
||||
|
||||
As KVM-HV has access to privileged instructions, guests that use a lot of these
|
||||
can run much faster than with KVM-PR. On the other hand, the guest CPU has to be
|
||||
of the same type as the host CPU this way, e.g. it is not possible to specify an
|
||||
embedded PPC CPU for the guest with KVM-HV. However, there is at least the
|
||||
possibility to run the guest in a backward-compatibility mode of the previous
|
||||
CPUs generations, e.g. you can run a POWER7 guest on a POWER8 host by using
|
||||
``-cpu POWER8,compat=power7`` as parameter to QEMU.
|
||||
|
||||
Modules support
|
||||
---------------
|
||||
|
||||
As noticed in the sections above, each module can run in a different
|
||||
environment. The following table shows with which environment each module can
|
||||
run. As long as you are in a supported environment, you can run KVM-PR or KVM-HV
|
||||
nested. Combinations not shown in the table are not available.
|
||||
|
||||
+--------------+------------+------+-------------------+----------+--------+
|
||||
| Platform | Host type | Bits | Page table format | KVM-HV | KVM-PR |
|
||||
+==============+============+======+===================+==========+========+
|
||||
| PowerNV | bare metal | 32 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | N/A | N/A |
|
||||
| | +------+-------------------+----------+--------+
|
||||
| | | 64 | hash | yes | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | yes | no |
|
||||
+--------------+------------+------+-------------------+----------+--------+
|
||||
| pSeries [1]_ | PowerNV | 32 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | N/A | N/A |
|
||||
| | +------+-------------------+----------+--------+
|
||||
| | | 64 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | yes [2]_ | no |
|
||||
| +------------+------+-------------------+----------+--------+
|
||||
| | PowerVM | 32 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix | N/A | N/A |
|
||||
| | +------+-------------------+----------+--------+
|
||||
| | | 64 | hash | no | yes |
|
||||
| | | +-------------------+----------+--------+
|
||||
| | | | radix [3]_ | no | yes |
|
||||
+--------------+------------+------+-------------------+----------+--------+
|
||||
|
||||
.. [1] On POWER9 DD2.1 processors, the page table format on the host and guest
|
||||
must be the same.
|
||||
|
||||
.. [2] KVM-HV cannot run nested on POWER8 machines.
|
||||
|
||||
.. [3] Introduced on Power10 machines.
|
||||
|
||||
Maintainer contact information
|
||||
------------------------------
|
||||
|
||||
Cédric Le Goater <clg@kaod.org>
|
||||
|
||||
Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
|
||||
+35
-22
@@ -19,7 +19,7 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s)
|
||||
{
|
||||
switch (a->cls) {
|
||||
case float_class_snan:
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
if (s->default_nan_mode) {
|
||||
parts_default_nan(a, s);
|
||||
} else {
|
||||
@@ -40,7 +40,7 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b,
|
||||
float_status *s)
|
||||
{
|
||||
if (is_snan(a->cls) || is_snan(b->cls)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
}
|
||||
|
||||
if (s->default_nan_mode) {
|
||||
@@ -68,7 +68,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
|
||||
int which;
|
||||
|
||||
if (unlikely(abc_mask & float_cmask_snan)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
}
|
||||
|
||||
which = pickNaNMulAdd(a->cls, b->cls, c->cls,
|
||||
@@ -354,7 +354,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b,
|
||||
return a;
|
||||
}
|
||||
/* Inf - Inf */
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_isi, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
@@ -423,7 +423,7 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b,
|
||||
|
||||
/* Inf * Zero == NaN */
|
||||
if (unlikely(ab_mask == float_cmask_infzero)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
@@ -489,11 +489,13 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
|
||||
|
||||
if (unlikely(ab_mask != float_cmask_normal)) {
|
||||
if (unlikely(ab_mask == float_cmask_infzero)) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, s);
|
||||
goto d_nan;
|
||||
}
|
||||
|
||||
if (ab_mask & float_cmask_inf) {
|
||||
if (c->cls == float_class_inf && a->sign != c->sign) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_isi, s);
|
||||
goto d_nan;
|
||||
}
|
||||
goto return_inf;
|
||||
@@ -566,7 +568,6 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
|
||||
goto finish_sign;
|
||||
|
||||
d_nan:
|
||||
float_raise(float_flag_invalid, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
@@ -589,11 +590,13 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b,
|
||||
}
|
||||
|
||||
/* 0/0 or Inf/Inf => NaN */
|
||||
if (unlikely(ab_mask == float_cmask_zero) ||
|
||||
unlikely(ab_mask == float_cmask_inf)) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
if (unlikely(ab_mask == float_cmask_zero)) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_zdz, s);
|
||||
goto d_nan;
|
||||
}
|
||||
if (unlikely(ab_mask == float_cmask_inf)) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_idi, s);
|
||||
goto d_nan;
|
||||
}
|
||||
|
||||
/* All the NaN cases */
|
||||
@@ -624,6 +627,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b,
|
||||
float_raise(float_flag_divbyzero, s);
|
||||
a->cls = float_class_inf;
|
||||
return a;
|
||||
|
||||
d_nan:
|
||||
parts_default_nan(a, s);
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -862,7 +869,7 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status,
|
||||
return;
|
||||
|
||||
d_nan:
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_sqrt, status);
|
||||
parts_default_nan(a, status);
|
||||
}
|
||||
|
||||
@@ -1042,13 +1049,15 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode,
|
||||
|
||||
switch (p->cls) {
|
||||
case float_class_snan:
|
||||
flags |= float_flag_invalid_snan;
|
||||
/* fall through */
|
||||
case float_class_qnan:
|
||||
flags = float_flag_invalid;
|
||||
flags |= float_flag_invalid;
|
||||
r = max;
|
||||
break;
|
||||
|
||||
case float_class_inf:
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = p->sign ? min : max;
|
||||
break;
|
||||
|
||||
@@ -1070,11 +1079,11 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode,
|
||||
if (r <= -(uint64_t)min) {
|
||||
r = -r;
|
||||
} else {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = min;
|
||||
}
|
||||
} else if (r > max) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = max;
|
||||
}
|
||||
break;
|
||||
@@ -1107,13 +1116,15 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode,
|
||||
|
||||
switch (p->cls) {
|
||||
case float_class_snan:
|
||||
flags |= float_flag_invalid_snan;
|
||||
/* fall through */
|
||||
case float_class_qnan:
|
||||
flags = float_flag_invalid;
|
||||
flags |= float_flag_invalid;
|
||||
r = max;
|
||||
break;
|
||||
|
||||
case float_class_inf:
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = p->sign ? 0 : max;
|
||||
break;
|
||||
|
||||
@@ -1131,15 +1142,15 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode,
|
||||
}
|
||||
|
||||
if (p->sign) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = 0;
|
||||
} else if (p->exp > DECOMPOSED_BINARY_POINT) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = max;
|
||||
} else {
|
||||
r = p->frac_hi >> (DECOMPOSED_BINARY_POINT - p->exp);
|
||||
if (r > max) {
|
||||
flags = float_flag_invalid;
|
||||
flags = float_flag_invalid | float_flag_invalid_cvti;
|
||||
r = max;
|
||||
}
|
||||
}
|
||||
@@ -1334,7 +1345,9 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b,
|
||||
}
|
||||
|
||||
if (unlikely(ab_mask & float_cmask_anynan)) {
|
||||
if (!is_quiet || (ab_mask & float_cmask_snan)) {
|
||||
if (ab_mask & float_cmask_snan) {
|
||||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
} else if (!is_quiet) {
|
||||
float_raise(float_flag_invalid, s);
|
||||
}
|
||||
return float_relation_unordered;
|
||||
|
||||
@@ -506,7 +506,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
* the default NaN
|
||||
*/
|
||||
if (infzero && is_qnan(c_cls)) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
* case sets InvalidOp and returns the default NaN
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 3;
|
||||
}
|
||||
/* Prefer sNaN over qNaN, in the a, b, c order. */
|
||||
@@ -556,7 +556,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
* case sets InvalidOp and returns the input value 'c'
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 2;
|
||||
}
|
||||
/* Prefer sNaN over qNaN, in the c, a, b order. */
|
||||
@@ -580,7 +580,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
* a default NaN
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -597,7 +597,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
#elif defined(TARGET_RISCV)
|
||||
/* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
}
|
||||
return 3; /* default NaN */
|
||||
#elif defined(TARGET_XTENSA)
|
||||
@@ -606,7 +606,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
* an input NaN if we have one (ie c).
|
||||
*/
|
||||
if (infzero) {
|
||||
float_raise(float_flag_invalid, status);
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, status);
|
||||
return 2;
|
||||
}
|
||||
if (status->use_first_nan) {
|
||||
|
||||
+113
-1
@@ -1693,6 +1693,50 @@ static float64 float64_round_pack_canonical(FloatParts64 *p,
|
||||
return float64_pack_raw(p);
|
||||
}
|
||||
|
||||
static float64 float64r32_round_pack_canonical(FloatParts64 *p,
|
||||
float_status *s)
|
||||
{
|
||||
parts_uncanon(p, s, &float32_params);
|
||||
|
||||
/*
|
||||
* In parts_uncanon, we placed the fraction for float32 at the lsb.
|
||||
* We need to adjust the fraction higher so that the least N bits are
|
||||
* zero, and the fraction is adjacent to the float64 implicit bit.
|
||||
*/
|
||||
switch (p->cls) {
|
||||
case float_class_normal:
|
||||
if (unlikely(p->exp == 0)) {
|
||||
/*
|
||||
* The result is denormal for float32, but can be represented
|
||||
* in normalized form for float64. Adjust, per canonicalize.
|
||||
*/
|
||||
int shift = frac_normalize(p);
|
||||
p->exp = (float32_params.frac_shift -
|
||||
float32_params.exp_bias - shift + 1 +
|
||||
float64_params.exp_bias);
|
||||
frac_shr(p, float64_params.frac_shift);
|
||||
} else {
|
||||
frac_shl(p, float32_params.frac_shift - float64_params.frac_shift);
|
||||
p->exp += float64_params.exp_bias - float32_params.exp_bias;
|
||||
}
|
||||
break;
|
||||
case float_class_snan:
|
||||
case float_class_qnan:
|
||||
frac_shl(p, float32_params.frac_shift - float64_params.frac_shift);
|
||||
p->exp = float64_params.exp_max;
|
||||
break;
|
||||
case float_class_inf:
|
||||
p->exp = float64_params.exp_max;
|
||||
break;
|
||||
case float_class_zero:
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return float64_pack_raw(p);
|
||||
}
|
||||
|
||||
static void float128_unpack_canonical(FloatParts128 *p, float128 f,
|
||||
float_status *s)
|
||||
{
|
||||
@@ -1938,6 +1982,28 @@ float64_sub(float64 a, float64 b, float_status *s)
|
||||
return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub);
|
||||
}
|
||||
|
||||
static float64 float64r32_addsub(float64 a, float64 b, float_status *status,
|
||||
bool subtract)
|
||||
{
|
||||
FloatParts64 pa, pb, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
pr = parts_addsub(&pa, &pb, status, subtract);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
float64 float64r32_add(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
return float64r32_addsub(a, b, status, false);
|
||||
}
|
||||
|
||||
float64 float64r32_sub(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
return float64r32_addsub(a, b, status, true);
|
||||
}
|
||||
|
||||
static bfloat16 QEMU_FLATTEN
|
||||
bfloat16_addsub(bfloat16 a, bfloat16 b, float_status *status, bool subtract)
|
||||
{
|
||||
@@ -2069,6 +2135,17 @@ float64_mul(float64 a, float64 b, float_status *s)
|
||||
f64_is_zon2, f64_addsubmul_post);
|
||||
}
|
||||
|
||||
float64 float64r32_mul(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
FloatParts64 pa, pb, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
pr = parts_mul(&pa, &pb, status);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN
|
||||
bfloat16_mul(bfloat16 a, bfloat16 b, float_status *status)
|
||||
{
|
||||
@@ -2296,6 +2373,19 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s)
|
||||
return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s);
|
||||
}
|
||||
|
||||
float64 float64r32_muladd(float64 a, float64 b, float64 c,
|
||||
int flags, float_status *status)
|
||||
{
|
||||
FloatParts64 pa, pb, pc, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
float64_unpack_canonical(&pc, c, status);
|
||||
pr = parts_muladd(&pa, &pb, &pc, flags, status);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c,
|
||||
int flags, float_status *status)
|
||||
{
|
||||
@@ -2419,6 +2509,17 @@ float64_div(float64 a, float64 b, float_status *s)
|
||||
f64_div_pre, f64_div_post);
|
||||
}
|
||||
|
||||
float64 float64r32_div(float64 a, float64 b, float_status *status)
|
||||
{
|
||||
FloatParts64 pa, pb, *pr;
|
||||
|
||||
float64_unpack_canonical(&pa, a, status);
|
||||
float64_unpack_canonical(&pb, b, status);
|
||||
pr = parts_div(&pa, &pb, status);
|
||||
|
||||
return float64r32_round_pack_canonical(pr, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN
|
||||
bfloat16_div(bfloat16 a, bfloat16 b, float_status *status)
|
||||
{
|
||||
@@ -2543,8 +2644,10 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status)
|
||||
static void parts_float_to_ahp(FloatParts64 *a, float_status *s)
|
||||
{
|
||||
switch (a->cls) {
|
||||
case float_class_qnan:
|
||||
case float_class_snan:
|
||||
float_raise(float_flag_invalid_snan, s);
|
||||
/* fall through */
|
||||
case float_class_qnan:
|
||||
/*
|
||||
* There is no NaN in the destination format. Raise Invalid
|
||||
* and return a zero with the sign of the input NaN.
|
||||
@@ -4283,6 +4386,15 @@ float64 QEMU_FLATTEN float64_sqrt(float64 xa, float_status *s)
|
||||
return soft_f64_sqrt(ua.s, s);
|
||||
}
|
||||
|
||||
float64 float64r32_sqrt(float64 a, float_status *status)
|
||||
{
|
||||
FloatParts64 p;
|
||||
|
||||
float64_unpack_canonical(&p, a, status);
|
||||
parts_sqrt(&p, status, &float64_params);
|
||||
return float64r32_round_pack_canonical(&p, status);
|
||||
}
|
||||
|
||||
bfloat16 QEMU_FLATTEN bfloat16_sqrt(bfloat16 a, float_status *status)
|
||||
{
|
||||
FloatParts64 p;
|
||||
|
||||
+1
-1
@@ -243,7 +243,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
|
||||
static const MemoryRegionOps ivshmem_mmio_ops = {
|
||||
.read = ivshmem_io_read,
|
||||
.write = ivshmem_io_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
|
||||
@@ -993,7 +993,7 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
int i;
|
||||
|
||||
if (phb->phb_id >= PNV8_CHIP_PHB3_MAX) {
|
||||
if (phb->phb_id >= PNV_CHIP_GET_CLASS(phb->chip)->num_phbs) {
|
||||
error_setg(errp, "invalid PHB index: %d", phb->phb_id);
|
||||
return;
|
||||
}
|
||||
@@ -1092,6 +1092,7 @@ static const char *pnv_phb3_root_bus_path(PCIHostState *host_bridge,
|
||||
static Property pnv_phb3_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0),
|
||||
DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -284,6 +284,17 @@ static void pnv_pbcq_realize(DeviceState *dev, Error **errp)
|
||||
pnv_xscom_region_init(&pbcq->xscom_spci_regs, OBJECT(dev),
|
||||
&pnv_pbcq_spci_xscom_ops, pbcq, name,
|
||||
PNV_XSCOM_PBCQ_SPCI_SIZE);
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(phb->chip,
|
||||
PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_nest_regs);
|
||||
pnv_xscom_add_subregion(phb->chip,
|
||||
PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_pci_regs);
|
||||
pnv_xscom_add_subregion(phb->chip,
|
||||
PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id,
|
||||
&pbcq->xscom_spci_regs);
|
||||
}
|
||||
|
||||
static int pnv_pbcq_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
|
||||
@@ -1205,6 +1205,7 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB4_ROOT_BUS);
|
||||
pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
|
||||
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
|
||||
|
||||
/* Add a single Root port */
|
||||
qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id);
|
||||
|
||||
@@ -124,7 +124,7 @@ static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
|
||||
static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
|
||||
{
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
MemoryRegion *sysmem = pec->system_memory;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
|
||||
uint64_t bar, mask, size;
|
||||
char name[64];
|
||||
@@ -374,20 +374,41 @@ static void pnv_pec_instance_init(Object *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static int pnv_pec_phb_offset(PnvPhb4PecState *pec)
|
||||
{
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
int index = pec->index;
|
||||
int offset = 0;
|
||||
|
||||
while (index--) {
|
||||
offset += pecc->num_stacks[index];
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
char name[64];
|
||||
int i;
|
||||
|
||||
assert(pec->system_memory);
|
||||
if (pec->index >= PNV_CHIP_GET_CLASS(pec->chip)->num_pecs) {
|
||||
error_setg(errp, "invalid PEC index: %d", pec->index);
|
||||
return;
|
||||
}
|
||||
|
||||
pec->num_stacks = pecc->num_stacks[pec->index];
|
||||
|
||||
/* Create stacks */
|
||||
for (i = 0; i < pec->num_stacks; i++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[i];
|
||||
Object *stk_obj = OBJECT(stack);
|
||||
int phb_id = pnv_pec_phb_offset(pec) + i;
|
||||
|
||||
object_property_set_int(stk_obj, "stack-no", i, &error_abort);
|
||||
object_property_set_int(stk_obj, "phb-id", phb_id, &error_abort);
|
||||
object_property_set_link(stk_obj, "pec", OBJECT(pec), &error_abort);
|
||||
if (!qdev_realize(DEVICE(stk_obj), NULL, errp)) {
|
||||
return;
|
||||
@@ -460,10 +481,9 @@ static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
|
||||
static Property pnv_pec_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0),
|
||||
DEFINE_PROP_UINT32("num-stacks", PnvPhb4PecState, num_stacks, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0),
|
||||
DEFINE_PROP_LINK("system-memory", PnvPhb4PecState, system_memory,
|
||||
TYPE_MEMORY_REGION, MemoryRegion *),
|
||||
DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP,
|
||||
PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@@ -477,6 +497,13 @@ static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec)
|
||||
return PNV9_XSCOM_PEC_NEST_BASE + 0x400 * pec->index;
|
||||
}
|
||||
|
||||
/*
|
||||
* PEC0 -> 1 stack
|
||||
* PEC1 -> 2 stacks
|
||||
* PEC2 -> 3 stacks
|
||||
*/
|
||||
static const uint32_t pnv_pec_num_stacks[] = { 1, 2, 3 };
|
||||
|
||||
static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@@ -499,6 +526,9 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
||||
pecc->compat_size = sizeof(compat);
|
||||
pecc->stk_compat = stk_compat;
|
||||
pecc->stk_compat_size = sizeof(stk_compat);
|
||||
pecc->version = PNV_PHB4_VERSION;
|
||||
pecc->device_id = PNV_PHB4_DEVICE_ID;
|
||||
pecc->num_stacks = pnv_pec_num_stacks;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pec_type_info = {
|
||||
@@ -519,12 +549,17 @@ static void pnv_pec_stk_instance_init(Object *obj)
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj);
|
||||
|
||||
object_initialize_child(obj, "phb", &stack->phb, TYPE_PNV_PHB4);
|
||||
object_property_add_alias(obj, "phb-id", OBJECT(&stack->phb), "index");
|
||||
}
|
||||
|
||||
static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev);
|
||||
PnvPhb4PecState *pec = stack->pec;
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
PnvChip *chip = pec->chip;
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
char name[64];
|
||||
|
||||
assert(pec);
|
||||
@@ -548,10 +583,32 @@ static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
||||
pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(&stack->phb),
|
||||
&pnv_phb4_xscom_ops, &stack->phb, name, 0x40);
|
||||
|
||||
/*
|
||||
* Let the machine/chip realize the PHB object to customize more
|
||||
* easily some fields
|
||||
*/
|
||||
object_property_set_int(OBJECT(&stack->phb), "chip-id", pec->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "version", pecc->version,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "device-id", pecc->device_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(&stack->phb), "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&stack->phb), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_nest_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->pci_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
|
||||
0x40 * stack->stack_no,
|
||||
&stack->phb_regs_mr);
|
||||
}
|
||||
|
||||
static Property pnv_pec_stk_properties[] = {
|
||||
|
||||
@@ -36,9 +36,6 @@
|
||||
#include "hw/pci-host/uninorth.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
/* SMP is not enabled, for now */
|
||||
#define MAX_CPUS 1
|
||||
|
||||
#define NVRAM_SIZE 0x2000
|
||||
#define PROM_FILENAME "openbios-ppc"
|
||||
|
||||
|
||||
@@ -581,7 +581,8 @@ static void core99_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->desc = "Mac99 based PowerMAC";
|
||||
mc->init = ppc_core99_init;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->max_cpus = MAX_CPUS;
|
||||
/* SMP is not supported currently */
|
||||
mc->max_cpus = 1;
|
||||
mc->default_boot_order = "cd";
|
||||
mc->default_display = "std";
|
||||
mc->kvm_type = core99_kvm_type;
|
||||
|
||||
@@ -423,7 +423,8 @@ static void heathrow_class_init(ObjectClass *oc, void *data)
|
||||
mc->desc = "Heathrow based PowerMAC";
|
||||
mc->init = ppc_heathrow_init;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->max_cpus = MAX_CPUS;
|
||||
/* SMP is not supported currently */
|
||||
mc->max_cpus = 1;
|
||||
#ifndef TARGET_PPC64
|
||||
mc->is_default = true;
|
||||
#endif
|
||||
|
||||
+86
-91
@@ -522,7 +522,7 @@ static void *pnv_dt_create(MachineState *machine)
|
||||
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
|
||||
_FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
|
||||
if (qemu_uuid_set) {
|
||||
_FDT((fdt_property_string(fdt, "system-id", buf)));
|
||||
_FDT((fdt_setprop_string(fdt, 0, "system-id", buf)));
|
||||
}
|
||||
g_free(buf);
|
||||
|
||||
@@ -638,32 +638,47 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp)
|
||||
return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp);
|
||||
}
|
||||
|
||||
static int pnv_chip_power8_pic_print_info_child(Object *child, void *opaque)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
|
||||
if (phb3) {
|
||||
pnv_phb3_msi_pic_print_info(&phb3->msis, mon);
|
||||
ics_pic_print_info(&phb3->lsis, mon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon)
|
||||
{
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(chip);
|
||||
int i;
|
||||
|
||||
ics_pic_print_info(&chip8->psi.ics, mon);
|
||||
for (i = 0; i < chip->num_phbs; i++) {
|
||||
pnv_phb3_msi_pic_print_info(&chip8->phbs[i].msis, mon);
|
||||
ics_pic_print_info(&chip8->phbs[i].lsis, mon);
|
||||
object_child_foreach(OBJECT(chip),
|
||||
pnv_chip_power8_pic_print_info_child, mon);
|
||||
}
|
||||
|
||||
static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
PnvPHB4 *phb4 = (PnvPHB4 *) object_dynamic_cast(child, TYPE_PNV_PHB4);
|
||||
|
||||
if (phb4) {
|
||||
pnv_phb4_pic_print_info(phb4, mon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon)
|
||||
{
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
int i, j;
|
||||
|
||||
pnv_xive_pic_print_info(&chip9->xive, mon);
|
||||
pnv_psi_pic_print_info(&chip9->psi, mon);
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
PnvPhb4PecState *pec = &chip9->pecs[i];
|
||||
for (j = 0; j < pec->num_stacks; j++) {
|
||||
pnv_phb4_pic_print_info(&pec->stacks[j].phb, mon);
|
||||
}
|
||||
}
|
||||
object_child_foreach_recursive(OBJECT(chip),
|
||||
pnv_chip_power9_pic_print_info_child, mon);
|
||||
}
|
||||
|
||||
static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip,
|
||||
@@ -742,6 +757,11 @@ static void pnv_init(MachineState *machine)
|
||||
DriveInfo *pnor = drive_get(IF_MTD, 0, 0);
|
||||
DeviceState *dev;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
error_report("The powernv machine does not work with KVM acceleration");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* allocate RAM */
|
||||
if (machine->ram_size < mc->default_ram_size) {
|
||||
char *sz = size_to_str(mc->default_ram_size);
|
||||
@@ -1221,25 +1241,15 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
/* PHB3 controllers */
|
||||
for (i = 0; i < chip->num_phbs; i++) {
|
||||
PnvPHB3 *phb = &chip8->phbs[i];
|
||||
PnvPBCQState *pbcq = &phb->pbcq;
|
||||
|
||||
object_property_set_int(OBJECT(phb), "index", i, &error_fatal);
|
||||
object_property_set_int(OBJECT(phb), "chip-id", chip->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(phb), "chip", OBJECT(chip),
|
||||
&error_fatal);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_nest_regs);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id,
|
||||
&pbcq->xscom_pci_regs);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id,
|
||||
&pbcq->xscom_spci_regs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1340,15 +1350,13 @@ static void pnv_chip_power9_instance_init(Object *obj)
|
||||
|
||||
object_initialize_child(obj, "homer", &chip9->homer, TYPE_PNV9_HOMER);
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
/* Number of PECs is the chip default */
|
||||
chip->num_pecs = pcc->num_pecs;
|
||||
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
object_initialize_child(obj, "pec[*]", &chip9->pecs[i],
|
||||
TYPE_PNV_PHB4_PEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Number of PHBs is the chip default
|
||||
*/
|
||||
chip->num_phbs = pcc->num_phbs;
|
||||
}
|
||||
|
||||
static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
||||
@@ -1378,30 +1386,22 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_chip_power9_phb_realize(PnvChip *chip, Error **errp)
|
||||
static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp)
|
||||
{
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
int i, j;
|
||||
int phb_id = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) {
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
PnvPhb4PecState *pec = &chip9->pecs[i];
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
|
||||
object_property_set_int(OBJECT(pec), "index", i, &error_fatal);
|
||||
/*
|
||||
* PEC0 -> 1 stack
|
||||
* PEC1 -> 2 stacks
|
||||
* PEC2 -> 3 stacks
|
||||
*/
|
||||
object_property_set_int(OBJECT(pec), "num-stacks", i + 1,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(pec), "chip-id", chip->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(pec), "system-memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
object_property_set_link(OBJECT(pec), "chip", OBJECT(chip),
|
||||
&error_fatal);
|
||||
if (!qdev_realize(DEVICE(pec), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
@@ -1411,37 +1411,6 @@ static void pnv_chip_power9_phb_realize(PnvChip *chip, Error **errp)
|
||||
|
||||
pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr);
|
||||
|
||||
for (j = 0; j < pec->num_stacks && phb_id < chip->num_phbs;
|
||||
j++, phb_id++) {
|
||||
PnvPhb4PecStack *stack = &pec->stacks[j];
|
||||
Object *obj = OBJECT(&stack->phb);
|
||||
|
||||
object_property_set_int(obj, "index", phb_id, &error_fatal);
|
||||
object_property_set_int(obj, "chip-id", chip->chip_id,
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, "version", PNV_PHB4_VERSION,
|
||||
&error_fatal);
|
||||
object_property_set_int(obj, "device-id", PNV_PHB4_DEVICE_ID,
|
||||
&error_fatal);
|
||||
object_property_set_link(obj, "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(obj), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Populate the XSCOM address space. */
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_nest_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + 0x40 * (stack->stack_no + 1),
|
||||
&stack->pci_regs_mr);
|
||||
pnv_xscom_add_subregion(chip,
|
||||
pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
|
||||
0x40 * stack->stack_no,
|
||||
&stack->phb_regs_mr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1537,8 +1506,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip),
|
||||
&chip9->homer.regs);
|
||||
|
||||
/* PHBs */
|
||||
pnv_chip_power9_phb_realize(chip, &local_err);
|
||||
/* PEC PHBs */
|
||||
pnv_chip_power9_pec_realize(chip, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -1569,7 +1538,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
|
||||
k->xscom_core_base = pnv_chip_power9_xscom_core_base;
|
||||
k->xscom_pcba = pnv_chip_power9_xscom_pcba;
|
||||
dc->desc = "PowerNV Chip POWER9";
|
||||
k->num_phbs = 6;
|
||||
k->num_pecs = PNV9_CHIP_MAX_PEC;
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_chip_power9_realize,
|
||||
&k->parent_realize);
|
||||
@@ -1764,7 +1733,6 @@ static Property pnv_chip_properties[] = {
|
||||
DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1),
|
||||
DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0),
|
||||
DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1),
|
||||
DEFINE_PROP_UINT32("num-phbs", PnvChip, num_phbs, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@@ -1795,10 +1763,32 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct ForeachPhb3Args {
|
||||
int irq;
|
||||
ICSState *ics;
|
||||
} ForeachPhb3Args;
|
||||
|
||||
static int pnv_ics_get_child(Object *child, void *opaque)
|
||||
{
|
||||
ForeachPhb3Args *args = opaque;
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
|
||||
if (phb3) {
|
||||
if (ics_valid_irq(&phb3->lsis, args->irq)) {
|
||||
args->ics = &phb3->lsis;
|
||||
}
|
||||
if (ics_valid_irq(ICS(&phb3->msis), args->irq)) {
|
||||
args->ics = ICS(&phb3->msis);
|
||||
}
|
||||
}
|
||||
return args->ics ? 1 : 0;
|
||||
}
|
||||
|
||||
static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
int i, j;
|
||||
ForeachPhb3Args args = { irq, NULL };
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
@@ -1807,32 +1797,37 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
||||
if (ics_valid_irq(&chip8->psi.ics, irq)) {
|
||||
return &chip8->psi.ics;
|
||||
}
|
||||
for (j = 0; j < chip->num_phbs; j++) {
|
||||
if (ics_valid_irq(&chip8->phbs[j].lsis, irq)) {
|
||||
return &chip8->phbs[j].lsis;
|
||||
}
|
||||
if (ics_valid_irq(ICS(&chip8->phbs[j].msis), irq)) {
|
||||
return ICS(&chip8->phbs[j].msis);
|
||||
}
|
||||
|
||||
object_child_foreach(OBJECT(chip), pnv_ics_get_child, &args);
|
||||
if (args.ics) {
|
||||
return args.ics;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pnv_ics_resend_child(Object *child, void *opaque)
|
||||
{
|
||||
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
|
||||
|
||||
if (phb3) {
|
||||
ics_resend(&phb3->lsis);
|
||||
ics_resend(ICS(&phb3->msis));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_ics_resend(XICSFabric *xi)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xi);
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
PnvChip *chip = pnv->chips[i];
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]);
|
||||
|
||||
ics_resend(&chip8->psi.ics);
|
||||
for (j = 0; j < chip->num_phbs; j++) {
|
||||
ics_resend(&chip8->phbs[j].lsis);
|
||||
ics_resend(ICS(&chip8->phbs[j].msis));
|
||||
}
|
||||
object_child_foreach(OBJECT(chip), pnv_ics_resend_child, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1367,6 +1367,7 @@ int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
|
||||
if (dcr->dcr_read == NULL)
|
||||
goto error;
|
||||
*valp = (*dcr->dcr_read)(dcr->opaque, dcrn);
|
||||
trace_ppc_dcr_read(dcrn, *valp);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1386,6 +1387,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
|
||||
dcr = &dcr_env->dcrn[dcrn];
|
||||
if (dcr->dcr_write == NULL)
|
||||
goto error;
|
||||
trace_ppc_dcr_write(dcrn, val);
|
||||
(*dcr->dcr_write)(dcr->opaque, dcrn, val);
|
||||
|
||||
return 0;
|
||||
|
||||
+10
-4
@@ -27,6 +27,13 @@
|
||||
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
|
||||
#define PPC405EP_SDRAM_BASE 0x00000000
|
||||
#define PPC405EP_NVRAM_BASE 0xF0000000
|
||||
#define PPC405EP_FPGA_BASE 0xF0300000
|
||||
#define PPC405EP_SRAM_BASE 0xFFF00000
|
||||
#define PPC405EP_SRAM_SIZE (512 * KiB)
|
||||
#define PPC405EP_FLASH_BASE 0xFFF80000
|
||||
|
||||
/* Bootinfo as set-up by u-boot */
|
||||
typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t;
|
||||
struct ppc4xx_bd_info_t {
|
||||
@@ -50,19 +57,18 @@ struct ppc4xx_bd_info_t {
|
||||
uint32_t bi_plb_busfreq;
|
||||
uint32_t bi_pci_busfreq;
|
||||
uint8_t bi_pci_enetaddr[6];
|
||||
uint32_t bi_pci_enetaddr2[6];
|
||||
uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */
|
||||
uint32_t bi_opbfreq;
|
||||
uint32_t bi_iic_fast[2];
|
||||
};
|
||||
|
||||
/* PowerPC 405 core */
|
||||
ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd,
|
||||
uint32_t flags);
|
||||
ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size);
|
||||
|
||||
void ppc4xx_plb_init(CPUPPCState *env);
|
||||
void ppc405_ebc_init(CPUPPCState *env);
|
||||
|
||||
CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
MemoryRegion ram_memories[2],
|
||||
hwaddr ram_bases[2],
|
||||
hwaddr ram_sizes[2],
|
||||
|
||||
+132
-113
@@ -41,11 +41,12 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/loader.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "elf.h"
|
||||
|
||||
#define BIOS_FILENAME "ppc405_rom.bin"
|
||||
#define BIOS_SIZE (2 * MiB)
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x00000000
|
||||
#define KERNEL_LOAD_ADDR 0x01000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
#define USE_FLASH_BIOS
|
||||
@@ -136,32 +137,101 @@ static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base)
|
||||
qemu_register_reset(&ref405ep_fpga_reset, fpga);
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU reset handler when booting directly from a loaded kernel
|
||||
*/
|
||||
static struct boot_info {
|
||||
uint32_t entry;
|
||||
uint32_t bdloc;
|
||||
uint32_t initrd_base;
|
||||
uint32_t initrd_size;
|
||||
uint32_t cmdline_base;
|
||||
uint32_t cmdline_size;
|
||||
} boot_info;
|
||||
|
||||
static void main_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
struct boot_info *bi = env->load_info;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
|
||||
/* stack: top of sram */
|
||||
env->gpr[1] = PPC405EP_SRAM_BASE + PPC405EP_SRAM_SIZE - 8;
|
||||
|
||||
/* Tune our boot state */
|
||||
env->gpr[3] = bi->bdloc;
|
||||
env->gpr[4] = bi->initrd_base;
|
||||
env->gpr[5] = bi->initrd_base + bi->initrd_size;
|
||||
env->gpr[6] = bi->cmdline_base;
|
||||
env->gpr[7] = bi->cmdline_size;
|
||||
|
||||
env->nip = bi->entry;
|
||||
}
|
||||
|
||||
static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
hwaddr boot_entry;
|
||||
hwaddr kernel_base;
|
||||
int kernel_size;
|
||||
hwaddr initrd_base;
|
||||
int initrd_size;
|
||||
ram_addr_t bdloc;
|
||||
int len;
|
||||
|
||||
bdloc = ppc405_set_bootinfo(env, machine->ram_size);
|
||||
boot_info.bdloc = bdloc;
|
||||
|
||||
kernel_size = load_elf(machine->kernel_filename, NULL, NULL, NULL,
|
||||
&boot_entry, &kernel_base, NULL, NULL,
|
||||
1, PPC_ELF_MACHINE, 0, 0);
|
||||
if (kernel_size < 0) {
|
||||
error_report("Could not load kernel '%s' : %s",
|
||||
machine->kernel_filename, load_elf_strerror(kernel_size));
|
||||
exit(1);
|
||||
}
|
||||
boot_info.entry = boot_entry;
|
||||
|
||||
/* load initrd */
|
||||
if (machine->initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(machine->initrd_filename, initrd_base,
|
||||
machine->ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
machine->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
boot_info.initrd_base = initrd_base;
|
||||
boot_info.initrd_size = initrd_size;
|
||||
}
|
||||
|
||||
if (machine->kernel_cmdline) {
|
||||
len = strlen(machine->kernel_cmdline);
|
||||
bdloc -= ((len + 255) & ~255);
|
||||
cpu_physical_memory_write(bdloc, machine->kernel_cmdline, len + 1);
|
||||
boot_info.cmdline_base = bdloc;
|
||||
boot_info.cmdline_size = bdloc + len;
|
||||
}
|
||||
|
||||
/* Install our custom reset handler to start from Linux */
|
||||
qemu_register_reset(main_cpu_reset, cpu);
|
||||
env->load_info = &boot_info;
|
||||
}
|
||||
|
||||
static void ref405ep_init(MachineState *machine)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const char *bios_name = machine->firmware ?: BIOS_FILENAME;
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
char *filename;
|
||||
ppc4xx_bd_info_t bd;
|
||||
CPUPPCState *env;
|
||||
PowerPCCPU *cpu;
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
MemoryRegion *bios;
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
ram_addr_t bdloc;
|
||||
MemoryRegion *ram_memories = g_new(MemoryRegion, 2);
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
target_ulong sram_size;
|
||||
long bios_size;
|
||||
//int phy_addr = 0;
|
||||
//static int phy_addr = 1;
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
int linux_boot;
|
||||
int len;
|
||||
DriveInfo *dinfo;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
DeviceState *uicdev;
|
||||
|
||||
@@ -180,132 +250,80 @@ static void ref405ep_init(MachineState *machine)
|
||||
memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0);
|
||||
ram_bases[1] = 0x00000000;
|
||||
ram_sizes[1] = 0x00000000;
|
||||
env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
|
||||
cpu = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
33333333, &uicdev, kernel_filename == NULL ? 0 : 1);
|
||||
|
||||
/* allocate SRAM */
|
||||
sram_size = 512 * KiB;
|
||||
memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size,
|
||||
memory_region_init_ram(sram, NULL, "ef405ep.sram", PPC405EP_SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(sysmem, 0xFFF00000, sram);
|
||||
memory_region_add_subregion(sysmem, PPC405EP_SRAM_BASE, sram);
|
||||
|
||||
/* allocate and load BIOS */
|
||||
#ifdef USE_FLASH_BIOS
|
||||
dinfo = drive_get(IF_PFLASH, 0, 0);
|
||||
if (dinfo) {
|
||||
bios_size = 8 * MiB;
|
||||
pflash_cfi02_register((uint32_t)(-bios_size),
|
||||
"ef405ep.bios", bios_size,
|
||||
blk_by_legacy_dinfo(dinfo),
|
||||
64 * KiB, 1,
|
||||
2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
bios = g_new(MemoryRegion, 1);
|
||||
if (machine->firmware) {
|
||||
MemoryRegion *bios = g_new(MemoryRegion, 1);
|
||||
g_autofree char *filename;
|
||||
long bios_size;
|
||||
|
||||
memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE,
|
||||
&error_fatal);
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (filename) {
|
||||
bios_size = load_image_size(filename,
|
||||
memory_region_get_ram_ptr(bios),
|
||||
BIOS_SIZE);
|
||||
g_free(filename);
|
||||
if (bios_size < 0) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
} else if (!qtest_enabled() || kernel_filename != NULL) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", bios_name);
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware);
|
||||
if (!filename) {
|
||||
error_report("Could not find firmware '%s'", machine->firmware);
|
||||
exit(1);
|
||||
} else {
|
||||
/* Avoid an uninitialized variable warning */
|
||||
bios_size = -1;
|
||||
}
|
||||
|
||||
bios_size = load_image_size(filename,
|
||||
memory_region_get_ram_ptr(bios),
|
||||
BIOS_SIZE);
|
||||
if (bios_size < 0) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", machine->firmware);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
}
|
||||
|
||||
/* Register FPGA */
|
||||
ref405ep_fpga_init(sysmem, 0xF0300000);
|
||||
ref405ep_fpga_init(sysmem, PPC405EP_FPGA_BASE);
|
||||
/* Register NVRAM */
|
||||
dev = qdev_new("sysbus-m48t08");
|
||||
qdev_prop_set_int32(dev, "base-year", 1968);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(s, &error_fatal);
|
||||
sysbus_mmio_map(s, 0, 0xF0000000);
|
||||
/* Load kernel */
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
if (linux_boot) {
|
||||
memset(&bd, 0, sizeof(bd));
|
||||
bd.bi_memstart = 0x00000000;
|
||||
bd.bi_memsize = machine->ram_size;
|
||||
bd.bi_flashstart = -bios_size;
|
||||
bd.bi_flashsize = -bios_size;
|
||||
bd.bi_flashoffset = 0;
|
||||
bd.bi_sramstart = 0xFFF00000;
|
||||
bd.bi_sramsize = sram_size;
|
||||
bd.bi_bootflags = 0;
|
||||
bd.bi_intfreq = 133333333;
|
||||
bd.bi_busfreq = 33333333;
|
||||
bd.bi_baudrate = 115200;
|
||||
bd.bi_s_version[0] = 'Q';
|
||||
bd.bi_s_version[1] = 'M';
|
||||
bd.bi_s_version[2] = 'U';
|
||||
bd.bi_s_version[3] = '\0';
|
||||
bd.bi_r_version[0] = 'Q';
|
||||
bd.bi_r_version[1] = 'E';
|
||||
bd.bi_r_version[2] = 'M';
|
||||
bd.bi_r_version[3] = 'U';
|
||||
bd.bi_r_version[4] = '\0';
|
||||
bd.bi_procfreq = 133333333;
|
||||
bd.bi_plb_busfreq = 33333333;
|
||||
bd.bi_pci_busfreq = 33333333;
|
||||
bd.bi_opbfreq = 33333333;
|
||||
bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001);
|
||||
env->gpr[3] = bdloc;
|
||||
sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE);
|
||||
|
||||
/* Load kernel and initrd using U-Boot images */
|
||||
if (kernel_filename && machine->firmware) {
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image_targphys(kernel_filename, kernel_base,
|
||||
machine->ram_size - kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
printf("Load kernel size %ld at " TARGET_FMT_lx,
|
||||
kernel_size, kernel_base);
|
||||
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
if (machine->initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
initrd_size = load_image_targphys(machine->initrd_filename,
|
||||
initrd_base,
|
||||
machine->ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
initrd_filename);
|
||||
machine->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
env->gpr[4] = initrd_base;
|
||||
env->gpr[5] = initrd_size;
|
||||
if (kernel_cmdline != NULL) {
|
||||
len = strlen(kernel_cmdline);
|
||||
bdloc -= ((len + 255) & ~255);
|
||||
cpu_physical_memory_write(bdloc, kernel_cmdline, len + 1);
|
||||
env->gpr[6] = bdloc;
|
||||
env->gpr[7] = bdloc + len;
|
||||
} else {
|
||||
env->gpr[6] = 0;
|
||||
env->gpr[7] = 0;
|
||||
}
|
||||
env->nip = KERNEL_LOAD_ADDR;
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
bdloc = 0;
|
||||
|
||||
/* Load ELF kernel and rootfs.cpio */
|
||||
} else if (kernel_filename && !machine->firmware) {
|
||||
boot_from_kernel(machine, cpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,6 +565,7 @@ static void taihu_class_init(ObjectClass *oc, void *data)
|
||||
mc->init = taihu_405ep_init;
|
||||
mc->default_ram_size = 0x08000000;
|
||||
mc->default_ram_id = "taihu_405ep.ram";
|
||||
mc->deprecation_reason = "incomplete, use 'ref405ep' instead";
|
||||
}
|
||||
|
||||
static const TypeInfo taihu_type = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user