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:
Richard Henderson
2021-12-17 09:55:14 -08:00
59 changed files with 2513 additions and 1646 deletions
+9
View File
@@ -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
---------------
+100
View File
@@ -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.
-78
View File
@@ -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
View File
@@ -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>
+226
View File
@@ -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
View File
@@ -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;
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+2 -1
View File
@@ -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(),
};
+11
View File
@@ -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,
+1
View File
@@ -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);
+66 -9
View File
@@ -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[] = {
-3
View File
@@ -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"
+2 -1
View File
@@ -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;
+2 -1
View File
@@ -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
View File
@@ -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);
}
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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