ppc patch queue for 2021-03-10
Next batch of patches for the ppc target and machine types. Includes: * Several cleanups for sm501 from Peter Maydell * An update to the SLOF guest firmware * Improved handling of hotplug failures in spapr, associated cleanups to the hotplug handling code * Several etsec fixes and cleanups from Bin Meng * Assorted other fixes and cleanups -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAmBIRlUACgkQbDjKyiDZ s5JGlxAApWKpxdtMwrxvQ7EX95XtDWY0v2Jpl3ZKLhYgWJ28pt1SfsDUlA9KhlDd syXITpyspECe9kjOAKEim4J0y5sMVlTw8KjzIVPMik4uyoLTOBwE+nRmwPnmnWEy 9ZH0J+QOonQYh3jCp7JbTGU2ZW5pJ9s/sv8bPbzXfrR07HbAJ2+MjUkTVxkSVJAq QUvo/jMntu+a1HFU8Eiw8VyyIcIOAQyS469xzUiHHzKFlR8XodE56Vj+oh6ZFtaA cB2h4U51uzGfpz+GISm3lZUHSVnWQSFwLAc4x66aRsnLiQ66iAu8N0jRh8lsoW0y FHF+uGp3AFUARHOiCRk0r7+s29gbu+lX2jogfddj+qj7mGIZXd2tMfrrG3eWsB2C HvNby4xzyyDaguHK7N0/C42B8OX5dy2pxOP5lvdzL20ip97AKRGXngyM7LhYH8yw 4uzdebYVFu0KkLri4Qzxjm/GxgzrCbWIe5ImsDIlnmY1cJ7NKQYPzFX56xqq147y 6USFQu7RM9E03vj3c9UIkmK0KhL8GQvYxX4dMWIUjtjeLGJuN5seKBkl5mH2OSEJ D9svKOanXmsZYS0A25VX9FRX263zbJ1HIkDmGzpLi7HULdRy78e89rJk6490WNDr mnLogO+ttBvhEaLUsIVrWwLd21JW/A2NHuEz0+KELr9ZOQMYRj8= =/uyx -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.0-20210310' into staging ppc patch queue for 2021-03-10 Next batch of patches for the ppc target and machine types. Includes: * Several cleanups for sm501 from Peter Maydell * An update to the SLOF guest firmware * Improved handling of hotplug failures in spapr, associated cleanups to the hotplug handling code * Several etsec fixes and cleanups from Bin Meng * Assorted other fixes and cleanups # gpg: Signature made Wed 10 Mar 2021 04:08:53 GMT # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dg-gitlab/tags/ppc-for-6.0-20210310: spapr.c: send QAPI event when memory hotunplug fails spapr.c: remove duplicated assert in spapr_memory_unplug_request() target/ppc: fix icount support on Book-e vms accessing SPRs qemu_timer.c: add timer_deadline_ms() helper spapr_pci.c: add 'unplug already in progress' message for PCI unplug spapr.c: add 'unplug already in progress' message for PHB unplug hw/ppc: e500: Add missing <ranges> in the eTSEC node hw/net: fsl_etsec: Fix build error when HEX_DUMP is on spapr_drc.c: use DRC reconfiguration to cleanup DIMM unplug state spapr_drc.c: add hotunplug timeout for CPUs spapr_drc.c: introduce unplug_timeout_timer target/ppc: Fix bcdsub. emulation when result overflows docs/system: Extend PPC section spapr: rename spapr_drc_detach() to spapr_drc_unplug_request() spapr_drc.c: use spapr_drc_release() in isolate_physical/set_unusable pseries: Update SLOF firmware image spapr_drc.c: do not call spapr_drc_detach() in drc_isolate_logical() hw/display/sm501: Inline template header into C file hw/display/sm501: Expand out macros in template header hw/display/sm501: Remove dead code for non-32-bit RGB surfaces Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1941858448
10
docs/system/ppc/embedded.rst
Normal file
10
docs/system/ppc/embedded.rst
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Embedded family boards
|
||||||
|
======================
|
||||||
|
|
||||||
|
- ``bamboo`` bamboo
|
||||||
|
- ``mpc8544ds`` mpc8544ds
|
||||||
|
- ``ppce500`` generic paravirt e500 platform
|
||||||
|
- ``ref405ep`` ref405ep
|
||||||
|
- ``sam460ex`` aCube Sam460ex
|
||||||
|
- ``taihu`` taihu
|
||||||
|
- ``virtex-ml507`` Xilinx Virtex ML507 reference design
|
34
docs/system/ppc/powermac.rst
Normal file
34
docs/system/ppc/powermac.rst
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
PowerMac family boards (``g3beige``, ``mac99``)
|
||||||
|
==================================================================
|
||||||
|
|
||||||
|
Use the executable ``qemu-system-ppc`` to simulate a complete PowerMac
|
||||||
|
PowerPC system.
|
||||||
|
|
||||||
|
- ``g3beige`` Heathrow based PowerMAC
|
||||||
|
- ``mac99`` Mac99 based PowerMAC
|
||||||
|
|
||||||
|
Supported devices
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
QEMU emulates the following PowerMac peripherals:
|
||||||
|
|
||||||
|
* UniNorth or Grackle PCI Bridge
|
||||||
|
* PCI VGA compatible card with VESA Bochs Extensions
|
||||||
|
* 2 PMAC IDE interfaces with hard disk and CD-ROM support
|
||||||
|
* NE2000 PCI adapters
|
||||||
|
* Non Volatile RAM
|
||||||
|
* VIA-CUDA with ADB keyboard and mouse.
|
||||||
|
|
||||||
|
|
||||||
|
Missing devices
|
||||||
|
---------------
|
||||||
|
|
||||||
|
* To be identified
|
||||||
|
|
||||||
|
Firmware
|
||||||
|
--------
|
||||||
|
|
||||||
|
Since version 0.9.1, QEMU uses OpenBIOS https://www.openbios.org/ for
|
||||||
|
the g3beige and mac99 PowerMac and the 40p machines. OpenBIOS is a free
|
||||||
|
(GPL v2) portable firmware implementation. The goal is to implement a
|
||||||
|
100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware.
|
193
docs/system/ppc/powernv.rst
Normal file
193
docs/system/ppc/powernv.rst
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
PowerNV family boards (``powernv8``, ``powernv9``)
|
||||||
|
==================================================================
|
||||||
|
|
||||||
|
PowerNV (as Non-Virtualized) is the "baremetal" 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.
|
||||||
|
|
||||||
|
The PowerNV QEMU machine tries to emulate a PowerNV system at the
|
||||||
|
level of the skiboot firmware, which loads the OS and provides some
|
||||||
|
runtime services. Power Systems have a lower firmware (HostBoot) that
|
||||||
|
does low level system initialization, like DRAM training. This is
|
||||||
|
beyond the scope of what QEMU addresses today.
|
||||||
|
|
||||||
|
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.
|
||||||
|
* PNOR containing the different firmware partitions.
|
||||||
|
|
||||||
|
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)
|
||||||
|
* Self Boot Engine (SBE).
|
||||||
|
* 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:
|
||||||
|
|
||||||
|
https://github.com/open-power.
|
||||||
|
|
||||||
|
Prebuilt images of `skiboot` and `skiboot` are made available on the `OpenPOWER <https://openpower.xyz/job/openpower/job/openpower-op-build/>`__ site. To boot a POWER9 machine, use the `witherspoon <https://openpower.xyz/job/openpower/job/openpower-op-build/label=slave,target=witherspoon/lastSuccessfulBuild/>`__ images. For POWER8, use
|
||||||
|
the `palmetto <https://openpower.xyz/job/openpower/job/openpower-op-build/label=slave,target=palmetto/lastSuccessfulBuild/>`__ images.
|
||||||
|
|
||||||
|
QEMU includes a prebuilt image of `skiboot` which is updated when a
|
||||||
|
more recent version is required by the models.
|
||||||
|
|
||||||
|
Boot options
|
||||||
|
------------
|
||||||
|
|
||||||
|
Here is a simple setup with one e1000e NIC :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ qemu-system-ppc64 -m 2G -machine powernv9 -smp 2,cores=2,threads=1 \
|
||||||
|
-accel tcg,thread=single \
|
||||||
|
-device e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0 \
|
||||||
|
-netdev user,id=net0,hostfwd=::20022-:22,hostname=pnv \
|
||||||
|
-kernel ./zImage.epapr \
|
||||||
|
-initrd ./rootfs.cpio.xz \
|
||||||
|
-nographic
|
||||||
|
|
||||||
|
and a SATA disk :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-device ich9-ahci,id=sata0,bus=pcie.1,addr=0x0 \
|
||||||
|
-drive file=./ubuntu-ppc64le.qcow2,if=none,id=drive0,format=qcow2,cache=none \
|
||||||
|
-device ide-hd,bus=sata0.0,unit=0,drive=drive0,id=ide,bootindex=1 \
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-device e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0
|
||||||
|
-netdev bridge,id=net0,helper=/usr/libexec/qemu-bridge-helper,br=virbr0,id=hostnet0
|
||||||
|
|
||||||
|
-device megasas,id=scsi0,bus=pcie.0,addr=0x0
|
||||||
|
-drive file=./ubuntu-ppc64le.qcow2,if=none,id=drive-scsi0-0-0-0,format=qcow2,cache=none
|
||||||
|
-device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=2
|
||||||
|
|
||||||
|
Here is a full example with two different storage controllers on
|
||||||
|
different PHBs, each with a disk, the second PHB is empty :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ qemu-system-ppc64 -m 2G -machine powernv9 -smp 2,cores=2,threads=1 -accel tcg,thread=single \
|
||||||
|
-kernel ./zImage.epapr -initrd ./rootfs.cpio.xz -bios ./skiboot.lid \
|
||||||
|
\
|
||||||
|
-device megasas,id=scsi0,bus=pcie.0,addr=0x0 \
|
||||||
|
-drive file=./rhel7-ppc64le.qcow2,if=none,id=drive-scsi0-0-0-0,format=qcow2,cache=none \
|
||||||
|
-device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=2 \
|
||||||
|
\
|
||||||
|
-device pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0 \
|
||||||
|
\
|
||||||
|
-device ich9-ahci,id=sata0,bus=bridge1,addr=0x1 \
|
||||||
|
-drive file=./ubuntu-ppc64le.qcow2,if=none,id=drive0,format=qcow2,cache=none \
|
||||||
|
-device ide-hd,bus=sata0.0,unit=0,drive=drive0,id=ide,bootindex=1 \
|
||||||
|
-device e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=bridge1,addr=0x2 \
|
||||||
|
-netdev bridge,helper=/usr/libexec/qemu-bridge-helper,br=virbr0,id=net0 \
|
||||||
|
-device nec-usb-xhci,bus=bridge1,addr=0x7 \
|
||||||
|
\
|
||||||
|
-serial mon:stdio -nographic
|
||||||
|
|
||||||
|
You can also use VIRTIO devices :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-drive file=./fedora-ppc64le.qcow2,if=none,snapshot=on,id=drive0 \
|
||||||
|
-device virtio-blk-pci,drive=drive0,id=blk0,bus=pcie.0 \
|
||||||
|
\
|
||||||
|
-netdev tap,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0,id=netdev0 \
|
||||||
|
-device virtio-net-pci,netdev=netdev0,id=net0,bus=pcie.1 \
|
||||||
|
\
|
||||||
|
-fsdev local,id=fsdev0,path=$HOME,security_model=passthrough \
|
||||||
|
-device virtio-9p-pci,fsdev=fsdev0,mount_tag=host,bus=pcie.2
|
||||||
|
|
||||||
|
Multi sockets
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The number of sockets is deduced from the number of CPUs and the
|
||||||
|
number of cores. ``-smp 2,cores=1`` will define a machine with 2
|
||||||
|
sockets of 1 core, whereas ``-smp 2,cores=2`` will define a machine
|
||||||
|
with 1 socket of 2 cores. ``-smp 8,cores=2``, 4 sockets of 2 cores.
|
||||||
|
|
||||||
|
BMC configuration
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
OpenPOWER systems negotiate the shutdown and reboot with their
|
||||||
|
BMC. The QEMU PowerNV machine embeds an IPMI BMC simulator using the
|
||||||
|
iBT interface and should offer the same power features.
|
||||||
|
|
||||||
|
If you want to define your own BMC, use ``-nodefaults`` and specify
|
||||||
|
one on the command line :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10
|
||||||
|
|
||||||
|
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
|
||||||
|
simulator.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-device ipmi-bmc-sim,sdrfile=./palmetto-SDR.bin,fruareasize=256,frudatafile=./palmetto-FRU.bin,id=bmc0 \
|
||||||
|
-device isa-ipmi-bt,bmc=bmc0,irq=10
|
||||||
|
|
||||||
|
The PowerNV machine can also be run with an external IPMI BMC device
|
||||||
|
connected to a remote QEMU machine acting as BMC, using these options
|
||||||
|
:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-chardev socket,id=ipmi0,host=localhost,port=9002,reconnect=10 \
|
||||||
|
-device ipmi-bmc-extern,id=bmc0,chardev=ipmi0 \
|
||||||
|
-device isa-ipmi-bt,bmc=bmc0,irq=10 \
|
||||||
|
-nodefaults
|
||||||
|
|
||||||
|
NVRAM
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
Use a MTD drive to add a PNOR to the machine, and get a NVRAM :
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
-drive file=./witherspoon.pnor,format=raw,if=mtd
|
||||||
|
|
||||||
|
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.
|
18
docs/system/ppc/prep.rst
Normal file
18
docs/system/ppc/prep.rst
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Prep machine (``40p``)
|
||||||
|
==================================================================
|
||||||
|
|
||||||
|
Use the executable ``qemu-system-ppc`` to simulate a complete 40P (PREP)
|
||||||
|
|
||||||
|
Supported devices
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
QEMU emulates the following 40P (PREP) peripherals:
|
||||||
|
|
||||||
|
* PCI Bridge
|
||||||
|
* PCI VGA compatible card with VESA Bochs Extensions
|
||||||
|
* 2 IDE interfaces with hard disk and CD-ROM support
|
||||||
|
* Floppy disk
|
||||||
|
* PCnet network adapters
|
||||||
|
* Serial port
|
||||||
|
* PREP Non Volatile RAM
|
||||||
|
* PC compatible keyboard and mouse.
|
12
docs/system/ppc/pseries.rst
Normal file
12
docs/system/ppc/pseries.rst
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pSeries family boards (``pseries``)
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Supported devices
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Missing devices
|
||||||
|
---------------
|
||||||
|
|
||||||
|
|
||||||
|
Firmware
|
||||||
|
--------
|
@ -3,45 +3,22 @@
|
|||||||
PowerPC System emulator
|
PowerPC System emulator
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Use the executable ``qemu-system-ppc`` to simulate a complete 40P (PREP)
|
Board-specific documentation
|
||||||
or PowerMac PowerPC system.
|
============================
|
||||||
|
|
||||||
QEMU emulates the following PowerMac peripherals:
|
You can get a complete list by running ``qemu-system-ppc64 --machine
|
||||||
|
help``.
|
||||||
|
|
||||||
- UniNorth or Grackle PCI Bridge
|
..
|
||||||
|
This table of contents should be kept sorted alphabetically
|
||||||
|
by the title text of each file, which isn't the same ordering
|
||||||
|
as an alphabetical sort by filename.
|
||||||
|
|
||||||
- PCI VGA compatible card with VESA Bochs Extensions
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
- 2 PMAC IDE interfaces with hard disk and CD-ROM support
|
ppc/embedded
|
||||||
|
ppc/powermac
|
||||||
- NE2000 PCI adapters
|
ppc/powernv
|
||||||
|
ppc/prep
|
||||||
- Non Volatile RAM
|
ppc/pseries
|
||||||
|
|
||||||
- VIA-CUDA with ADB keyboard and mouse.
|
|
||||||
|
|
||||||
QEMU emulates the following 40P (PREP) peripherals:
|
|
||||||
|
|
||||||
- PCI Bridge
|
|
||||||
|
|
||||||
- PCI VGA compatible card with VESA Bochs Extensions
|
|
||||||
|
|
||||||
- 2 IDE interfaces with hard disk and CD-ROM support
|
|
||||||
|
|
||||||
- Floppy disk
|
|
||||||
|
|
||||||
- PCnet network adapters
|
|
||||||
|
|
||||||
- Serial port
|
|
||||||
|
|
||||||
- PREP Non Volatile RAM
|
|
||||||
|
|
||||||
- PC compatible keyboard and mouse.
|
|
||||||
|
|
||||||
Since version 0.9.1, QEMU uses OpenBIOS https://www.openbios.org/ for
|
|
||||||
the g3beige and mac99 PowerMac and the 40p machines. OpenBIOS is a free
|
|
||||||
(GPL v2) portable firmware implementation. The goal is to implement a
|
|
||||||
100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware.
|
|
||||||
|
|
||||||
More information is available at
|
|
||||||
http://perso.magic.fr/l_indien/qemu-ppc/.
|
|
||||||
|
@ -1558,86 +1558,85 @@ typedef void draw_hwc_line_func(uint8_t *d, const uint8_t *s,
|
|||||||
int width, const uint8_t *palette,
|
int width, const uint8_t *palette,
|
||||||
int c_x, int c_y);
|
int c_x, int c_y);
|
||||||
|
|
||||||
#define DEPTH 8
|
static void draw_line8_32(uint8_t *d, const uint8_t *s, int width,
|
||||||
#include "sm501_template.h"
|
const uint32_t *pal)
|
||||||
|
|
||||||
#define DEPTH 15
|
|
||||||
#include "sm501_template.h"
|
|
||||||
|
|
||||||
#define BGR_FORMAT
|
|
||||||
#define DEPTH 15
|
|
||||||
#include "sm501_template.h"
|
|
||||||
|
|
||||||
#define DEPTH 16
|
|
||||||
#include "sm501_template.h"
|
|
||||||
|
|
||||||
#define BGR_FORMAT
|
|
||||||
#define DEPTH 16
|
|
||||||
#include "sm501_template.h"
|
|
||||||
|
|
||||||
#define DEPTH 32
|
|
||||||
#include "sm501_template.h"
|
|
||||||
|
|
||||||
#define BGR_FORMAT
|
|
||||||
#define DEPTH 32
|
|
||||||
#include "sm501_template.h"
|
|
||||||
|
|
||||||
static draw_line_func *draw_line8_funcs[] = {
|
|
||||||
draw_line8_8,
|
|
||||||
draw_line8_15,
|
|
||||||
draw_line8_16,
|
|
||||||
draw_line8_32,
|
|
||||||
draw_line8_32bgr,
|
|
||||||
draw_line8_15bgr,
|
|
||||||
draw_line8_16bgr,
|
|
||||||
};
|
|
||||||
|
|
||||||
static draw_line_func *draw_line16_funcs[] = {
|
|
||||||
draw_line16_8,
|
|
||||||
draw_line16_15,
|
|
||||||
draw_line16_16,
|
|
||||||
draw_line16_32,
|
|
||||||
draw_line16_32bgr,
|
|
||||||
draw_line16_15bgr,
|
|
||||||
draw_line16_16bgr,
|
|
||||||
};
|
|
||||||
|
|
||||||
static draw_line_func *draw_line32_funcs[] = {
|
|
||||||
draw_line32_8,
|
|
||||||
draw_line32_15,
|
|
||||||
draw_line32_16,
|
|
||||||
draw_line32_32,
|
|
||||||
draw_line32_32bgr,
|
|
||||||
draw_line32_15bgr,
|
|
||||||
draw_line32_16bgr,
|
|
||||||
};
|
|
||||||
|
|
||||||
static draw_hwc_line_func *draw_hwc_line_funcs[] = {
|
|
||||||
draw_hwc_line_8,
|
|
||||||
draw_hwc_line_15,
|
|
||||||
draw_hwc_line_16,
|
|
||||||
draw_hwc_line_32,
|
|
||||||
draw_hwc_line_32bgr,
|
|
||||||
draw_hwc_line_15bgr,
|
|
||||||
draw_hwc_line_16bgr,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int get_depth_index(DisplaySurface *surface)
|
|
||||||
{
|
{
|
||||||
switch (surface_bits_per_pixel(surface)) {
|
uint8_t v, r, g, b;
|
||||||
default:
|
do {
|
||||||
case 8:
|
v = ldub_p(s);
|
||||||
return 0;
|
r = (pal[v] >> 16) & 0xff;
|
||||||
case 15:
|
g = (pal[v] >> 8) & 0xff;
|
||||||
return 1;
|
b = (pal[v] >> 0) & 0xff;
|
||||||
case 16:
|
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
|
||||||
return 2;
|
s++;
|
||||||
case 32:
|
d += 4;
|
||||||
if (is_surface_bgr(surface)) {
|
} while (--width != 0);
|
||||||
return 4;
|
}
|
||||||
} else {
|
|
||||||
return 3;
|
static void draw_line16_32(uint8_t *d, const uint8_t *s, int width,
|
||||||
|
const uint32_t *pal)
|
||||||
|
{
|
||||||
|
uint16_t rgb565;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rgb565 = lduw_le_p(s);
|
||||||
|
r = (rgb565 >> 8) & 0xf8;
|
||||||
|
g = (rgb565 >> 3) & 0xfc;
|
||||||
|
b = (rgb565 << 3) & 0xf8;
|
||||||
|
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
|
||||||
|
s += 2;
|
||||||
|
d += 4;
|
||||||
|
} while (--width != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_line32_32(uint8_t *d, const uint8_t *s, int width,
|
||||||
|
const uint32_t *pal)
|
||||||
|
{
|
||||||
|
uint8_t r, g, b;
|
||||||
|
|
||||||
|
do {
|
||||||
|
r = s[2];
|
||||||
|
g = s[1];
|
||||||
|
b = s[0];
|
||||||
|
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
|
||||||
|
s += 4;
|
||||||
|
d += 4;
|
||||||
|
} while (--width != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw hardware cursor image on the given line.
|
||||||
|
*/
|
||||||
|
static void draw_hwc_line_32(uint8_t *d, const uint8_t *s, int width,
|
||||||
|
const uint8_t *palette, int c_x, int c_y)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint8_t r, g, b, v, bitset = 0;
|
||||||
|
|
||||||
|
/* get cursor position */
|
||||||
|
assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
|
||||||
|
s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
|
||||||
|
d += c_x * 4;
|
||||||
|
|
||||||
|
for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) {
|
||||||
|
/* get pixel value */
|
||||||
|
if (i % 4 == 0) {
|
||||||
|
bitset = ldub_p(s);
|
||||||
|
s++;
|
||||||
}
|
}
|
||||||
|
v = bitset & 3;
|
||||||
|
bitset >>= 2;
|
||||||
|
|
||||||
|
/* write pixel */
|
||||||
|
if (v) {
|
||||||
|
v--;
|
||||||
|
r = palette[v * 3 + 0];
|
||||||
|
g = palette[v * 3 + 1];
|
||||||
|
b = palette[v * 3 + 2];
|
||||||
|
*(uint32_t *)d = rgb_to_pixel32(r, g, b);
|
||||||
|
}
|
||||||
|
d += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1652,7 +1651,6 @@ static void sm501_update_display(void *opaque)
|
|||||||
int height = get_height(s, crt);
|
int height = get_height(s, crt);
|
||||||
int src_bpp = get_bpp(s, crt);
|
int src_bpp = get_bpp(s, crt);
|
||||||
int dst_bpp = surface_bytes_per_pixel(surface);
|
int dst_bpp = surface_bytes_per_pixel(surface);
|
||||||
int dst_depth_index = get_depth_index(surface);
|
|
||||||
draw_line_func *draw_line = NULL;
|
draw_line_func *draw_line = NULL;
|
||||||
draw_hwc_line_func *draw_hwc_line = NULL;
|
draw_hwc_line_func *draw_hwc_line = NULL;
|
||||||
int full_update = 0;
|
int full_update = 0;
|
||||||
@ -1662,6 +1660,8 @@ static void sm501_update_display(void *opaque)
|
|||||||
uint8_t hwc_palette[3 * 3];
|
uint8_t hwc_palette[3 * 3];
|
||||||
uint8_t *hwc_src = NULL;
|
uint8_t *hwc_src = NULL;
|
||||||
|
|
||||||
|
assert(dst_bpp == 4); /* Output is always 32-bit RGB */
|
||||||
|
|
||||||
if (!((crt ? s->dc_crt_control : s->dc_panel_control)
|
if (!((crt ? s->dc_crt_control : s->dc_panel_control)
|
||||||
& SM501_DC_CRT_CONTROL_ENABLE)) {
|
& SM501_DC_CRT_CONTROL_ENABLE)) {
|
||||||
return;
|
return;
|
||||||
@ -1674,13 +1674,13 @@ static void sm501_update_display(void *opaque)
|
|||||||
/* choose draw_line function */
|
/* choose draw_line function */
|
||||||
switch (src_bpp) {
|
switch (src_bpp) {
|
||||||
case 1:
|
case 1:
|
||||||
draw_line = draw_line8_funcs[dst_depth_index];
|
draw_line = draw_line8_32;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
draw_line = draw_line16_funcs[dst_depth_index];
|
draw_line = draw_line16_32;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
draw_line = draw_line32_funcs[dst_depth_index];
|
draw_line = draw_line32_32;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display"
|
qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display"
|
||||||
@ -1691,7 +1691,7 @@ static void sm501_update_display(void *opaque)
|
|||||||
/* set up to draw hardware cursor */
|
/* set up to draw hardware cursor */
|
||||||
if (is_hwc_enabled(s, crt)) {
|
if (is_hwc_enabled(s, crt)) {
|
||||||
/* choose cursor draw line function */
|
/* choose cursor draw line function */
|
||||||
draw_hwc_line = draw_hwc_line_funcs[dst_depth_index];
|
draw_hwc_line = draw_hwc_line_32;
|
||||||
hwc_src = get_hwc_address(s, crt);
|
hwc_src = get_hwc_address(s, crt);
|
||||||
c_x = get_hwc_x(s, crt);
|
c_x = get_hwc_x(s, crt);
|
||||||
c_y = get_hwc_y(s, crt);
|
c_y = get_hwc_y(s, crt);
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pixel drawing function templates for QEMU SM501 Device
|
|
||||||
*
|
|
||||||
* Copyright (c) 2008 Shin-ichiro KAWASAKI
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if DEPTH == 8
|
|
||||||
#define BPP 1
|
|
||||||
#define PIXEL_TYPE uint8_t
|
|
||||||
#elif DEPTH == 15 || DEPTH == 16
|
|
||||||
#define BPP 2
|
|
||||||
#define PIXEL_TYPE uint16_t
|
|
||||||
#elif DEPTH == 32
|
|
||||||
#define BPP 4
|
|
||||||
#define PIXEL_TYPE uint32_t
|
|
||||||
#else
|
|
||||||
#error unsupport depth
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BGR_FORMAT
|
|
||||||
#define PIXEL_NAME glue(DEPTH, bgr)
|
|
||||||
#else
|
|
||||||
#define PIXEL_NAME DEPTH
|
|
||||||
#endif /* BGR_FORMAT */
|
|
||||||
|
|
||||||
|
|
||||||
static void glue(draw_line8_, PIXEL_NAME)(
|
|
||||||
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
|
||||||
{
|
|
||||||
uint8_t v, r, g, b;
|
|
||||||
do {
|
|
||||||
v = ldub_p(s);
|
|
||||||
r = (pal[v] >> 16) & 0xff;
|
|
||||||
g = (pal[v] >> 8) & 0xff;
|
|
||||||
b = (pal[v] >> 0) & 0xff;
|
|
||||||
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
|
||||||
s++;
|
|
||||||
d += BPP;
|
|
||||||
} while (--width != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void glue(draw_line16_, PIXEL_NAME)(
|
|
||||||
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
|
||||||
{
|
|
||||||
uint16_t rgb565;
|
|
||||||
uint8_t r, g, b;
|
|
||||||
|
|
||||||
do {
|
|
||||||
rgb565 = lduw_le_p(s);
|
|
||||||
r = (rgb565 >> 8) & 0xf8;
|
|
||||||
g = (rgb565 >> 3) & 0xfc;
|
|
||||||
b = (rgb565 << 3) & 0xf8;
|
|
||||||
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
|
||||||
s += 2;
|
|
||||||
d += BPP;
|
|
||||||
} while (--width != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void glue(draw_line32_, PIXEL_NAME)(
|
|
||||||
uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
|
|
||||||
{
|
|
||||||
uint8_t r, g, b;
|
|
||||||
|
|
||||||
do {
|
|
||||||
r = s[2];
|
|
||||||
g = s[1];
|
|
||||||
b = s[0];
|
|
||||||
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
|
||||||
s += 4;
|
|
||||||
d += BPP;
|
|
||||||
} while (--width != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw hardware cursor image on the given line.
|
|
||||||
*/
|
|
||||||
static void glue(draw_hwc_line_, PIXEL_NAME)(uint8_t *d, const uint8_t *s,
|
|
||||||
int width, const uint8_t *palette, int c_x, int c_y)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint8_t r, g, b, v, bitset = 0;
|
|
||||||
|
|
||||||
/* get cursor position */
|
|
||||||
assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
|
|
||||||
s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
|
|
||||||
d += c_x * BPP;
|
|
||||||
|
|
||||||
for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) {
|
|
||||||
/* get pixel value */
|
|
||||||
if (i % 4 == 0) {
|
|
||||||
bitset = ldub_p(s);
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
v = bitset & 3;
|
|
||||||
bitset >>= 2;
|
|
||||||
|
|
||||||
/* write pixel */
|
|
||||||
if (v) {
|
|
||||||
v--;
|
|
||||||
r = palette[v * 3 + 0];
|
|
||||||
g = palette[v * 3 + 1];
|
|
||||||
b = palette[v * 3 + 2];
|
|
||||||
*(PIXEL_TYPE *)d = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
|
|
||||||
}
|
|
||||||
d += BPP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef DEPTH
|
|
||||||
#undef BPP
|
|
||||||
#undef PIXEL_TYPE
|
|
||||||
#undef PIXEL_NAME
|
|
||||||
#undef BGR_FORMAT
|
|
@ -27,6 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
#include "hw/ptimer.h"
|
#include "hw/ptimer.h"
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
#include "net/checksum.h"
|
#include "net/checksum.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "etsec.h"
|
#include "etsec.h"
|
||||||
|
@ -231,6 +231,7 @@ static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data)
|
|||||||
assert(irq2 >= 0);
|
assert(irq2 >= 0);
|
||||||
|
|
||||||
qemu_fdt_add_subnode(fdt, node);
|
qemu_fdt_add_subnode(fdt, node);
|
||||||
|
qemu_fdt_setprop(fdt, node, "ranges", NULL, 0);
|
||||||
qemu_fdt_setprop_string(fdt, node, "device_type", "network");
|
qemu_fdt_setprop_string(fdt, node, "device_type", "network");
|
||||||
qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2");
|
qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2");
|
||||||
qemu_fdt_setprop_string(fdt, node, "model", "eTSEC");
|
qemu_fdt_setprop_string(fdt, node, "model", "eTSEC");
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/datadir.h"
|
#include "qemu/datadir.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qapi-events-machine.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "sysemu/hostmem.h"
|
#include "sysemu/hostmem.h"
|
||||||
@ -3575,6 +3576,57 @@ static SpaprDimmState *spapr_recover_pending_dimm_state(SpaprMachineState *ms,
|
|||||||
return spapr_pending_dimm_unplugs_add(ms, avail_lmbs, dimm);
|
return spapr_pending_dimm_unplugs_add(ms, avail_lmbs, dimm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev)
|
||||||
|
{
|
||||||
|
SpaprDimmState *ds;
|
||||||
|
PCDIMMDevice *dimm;
|
||||||
|
SpaprDrc *drc;
|
||||||
|
uint32_t nr_lmbs;
|
||||||
|
uint64_t size, addr_start, addr;
|
||||||
|
g_autofree char *qapi_error = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimm = PC_DIMM(dev);
|
||||||
|
ds = spapr_pending_dimm_unplugs_find(spapr, dimm);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 'ds == NULL' would mean that the DIMM doesn't have a pending
|
||||||
|
* unplug state, but one of its DRC is marked as unplug_requested.
|
||||||
|
* This is bad and weird enough to g_assert() out.
|
||||||
|
*/
|
||||||
|
g_assert(ds);
|
||||||
|
|
||||||
|
spapr_pending_dimm_unplugs_remove(spapr, ds);
|
||||||
|
|
||||||
|
size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &error_abort);
|
||||||
|
nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE;
|
||||||
|
|
||||||
|
addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
addr = addr_start;
|
||||||
|
for (i = 0; i < nr_lmbs; i++) {
|
||||||
|
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
|
||||||
|
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
||||||
|
g_assert(drc);
|
||||||
|
|
||||||
|
drc->unplug_requested = false;
|
||||||
|
addr += SPAPR_MEMORY_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tell QAPI that something happened and the memory
|
||||||
|
* hotunplug wasn't successful.
|
||||||
|
*/
|
||||||
|
qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest "
|
||||||
|
"for device %s", dev->id);
|
||||||
|
qapi_event_send_mem_unplug_error(dev->id, qapi_error);
|
||||||
|
}
|
||||||
|
|
||||||
/* Callback to be called during DRC release. */
|
/* Callback to be called during DRC release. */
|
||||||
void spapr_lmb_release(DeviceState *dev)
|
void spapr_lmb_release(DeviceState *dev)
|
||||||
{
|
{
|
||||||
@ -3654,13 +3706,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
|
|||||||
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
||||||
g_assert(drc);
|
g_assert(drc);
|
||||||
|
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_unplug_request(drc);
|
||||||
addr += SPAPR_MEMORY_BLOCK_SIZE;
|
addr += SPAPR_MEMORY_BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
|
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
|
||||||
addr_start / SPAPR_MEMORY_BLOCK_SIZE);
|
addr_start / SPAPR_MEMORY_BLOCK_SIZE);
|
||||||
g_assert(drc);
|
|
||||||
spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
|
spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
|
||||||
nr_lmbs, spapr_drc_index(drc));
|
nr_lmbs, spapr_drc_index(drc));
|
||||||
}
|
}
|
||||||
@ -3722,8 +3773,12 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
g_assert(drc);
|
g_assert(drc);
|
||||||
|
|
||||||
if (!spapr_drc_unplug_requested(drc)) {
|
if (!spapr_drc_unplug_requested(drc)) {
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_unplug_request(drc);
|
||||||
spapr_hotplug_req_remove_by_index(drc);
|
spapr_hotplug_req_remove_by_index(drc);
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "core-id %d unplug is still pending, %d seconds "
|
||||||
|
"timeout remaining",
|
||||||
|
cc->core_id, spapr_drc_unplug_timeout_remaining_sec(drc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3985,8 +4040,12 @@ static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev,
|
|||||||
assert(drc);
|
assert(drc);
|
||||||
|
|
||||||
if (!spapr_drc_unplug_requested(drc)) {
|
if (!spapr_drc_unplug_requested(drc)) {
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_unplug_request(drc);
|
||||||
spapr_hotplug_req_remove_by_index(drc);
|
spapr_hotplug_req_remove_by_index(drc);
|
||||||
|
} else {
|
||||||
|
error_setg(errp,
|
||||||
|
"PCI Host Bridge unplug already in progress for device %s",
|
||||||
|
dev->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,22 @@ uint32_t spapr_drc_index(SpaprDrc *drc)
|
|||||||
| (drc->id & DRC_INDEX_ID_MASK);
|
| (drc->id & DRC_INDEX_ID_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spapr_drc_release(SpaprDrc *drc)
|
||||||
|
{
|
||||||
|
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||||
|
|
||||||
|
drck->release(drc->dev);
|
||||||
|
|
||||||
|
drc->unplug_requested = false;
|
||||||
|
timer_del(drc->unplug_timeout_timer);
|
||||||
|
|
||||||
|
g_free(drc->fdt);
|
||||||
|
drc->fdt = NULL;
|
||||||
|
drc->fdt_start_offset = 0;
|
||||||
|
object_property_del(OBJECT(drc), "device");
|
||||||
|
drc->dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t drc_isolate_physical(SpaprDrc *drc)
|
static uint32_t drc_isolate_physical(SpaprDrc *drc)
|
||||||
{
|
{
|
||||||
switch (drc->state) {
|
switch (drc->state) {
|
||||||
@ -68,7 +84,7 @@ static uint32_t drc_isolate_physical(SpaprDrc *drc)
|
|||||||
if (drc->unplug_requested) {
|
if (drc->unplug_requested) {
|
||||||
uint32_t drc_index = spapr_drc_index(drc);
|
uint32_t drc_index = spapr_drc_index(drc);
|
||||||
trace_spapr_drc_set_isolation_state_finalizing(drc_index);
|
trace_spapr_drc_set_isolation_state_finalizing(drc_index);
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_release(drc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RTAS_OUT_SUCCESS;
|
return RTAS_OUT_SUCCESS;
|
||||||
@ -132,19 +148,6 @@ static uint32_t drc_isolate_logical(SpaprDrc *drc)
|
|||||||
|
|
||||||
drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE;
|
drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE;
|
||||||
|
|
||||||
/* if we're awaiting release, but still in an unconfigured state,
|
|
||||||
* it's likely the guest is still in the process of configuring
|
|
||||||
* the device and is transitioning the devices to an ISOLATED
|
|
||||||
* state as a part of that process. so we only complete the
|
|
||||||
* removal when this transition happens for a device in a
|
|
||||||
* configured state, as suggested by the state diagram from PAPR+
|
|
||||||
* 2.7, 13.4
|
|
||||||
*/
|
|
||||||
if (drc->unplug_requested) {
|
|
||||||
uint32_t drc_index = spapr_drc_index(drc);
|
|
||||||
trace_spapr_drc_set_isolation_state_finalizing(drc_index);
|
|
||||||
spapr_drc_detach(drc);
|
|
||||||
}
|
|
||||||
return RTAS_OUT_SUCCESS;
|
return RTAS_OUT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +225,7 @@ static uint32_t drc_set_unusable(SpaprDrc *drc)
|
|||||||
if (drc->unplug_requested) {
|
if (drc->unplug_requested) {
|
||||||
uint32_t drc_index = spapr_drc_index(drc);
|
uint32_t drc_index = spapr_drc_index(drc);
|
||||||
trace_spapr_drc_set_allocation_state_finalizing(drc_index);
|
trace_spapr_drc_set_allocation_state_finalizing(drc_index);
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_release(drc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RTAS_OUT_SUCCESS;
|
return RTAS_OUT_SUCCESS;
|
||||||
@ -369,6 +372,17 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
|
|||||||
} while (fdt_depth != 0);
|
} while (fdt_depth != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spapr_drc_start_unplug_timeout_timer(SpaprDrc *drc)
|
||||||
|
{
|
||||||
|
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||||
|
|
||||||
|
if (drck->unplug_timeout_seconds != 0) {
|
||||||
|
timer_mod(drc->unplug_timeout_timer,
|
||||||
|
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
|
||||||
|
drck->unplug_timeout_seconds * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void spapr_drc_attach(SpaprDrc *drc, DeviceState *d)
|
void spapr_drc_attach(SpaprDrc *drc, DeviceState *d)
|
||||||
{
|
{
|
||||||
trace_spapr_drc_attach(spapr_drc_index(drc));
|
trace_spapr_drc_attach(spapr_drc_index(drc));
|
||||||
@ -385,30 +399,18 @@ void spapr_drc_attach(SpaprDrc *drc, DeviceState *d)
|
|||||||
NULL, 0);
|
NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_drc_release(SpaprDrc *drc)
|
void spapr_drc_unplug_request(SpaprDrc *drc)
|
||||||
{
|
{
|
||||||
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||||
|
|
||||||
drck->release(drc->dev);
|
trace_spapr_drc_unplug_request(spapr_drc_index(drc));
|
||||||
|
|
||||||
drc->unplug_requested = false;
|
|
||||||
g_free(drc->fdt);
|
|
||||||
drc->fdt = NULL;
|
|
||||||
drc->fdt_start_offset = 0;
|
|
||||||
object_property_del(OBJECT(drc), "device");
|
|
||||||
drc->dev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void spapr_drc_detach(SpaprDrc *drc)
|
|
||||||
{
|
|
||||||
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
|
||||||
|
|
||||||
trace_spapr_drc_detach(spapr_drc_index(drc));
|
|
||||||
|
|
||||||
g_assert(drc->dev);
|
g_assert(drc->dev);
|
||||||
|
|
||||||
drc->unplug_requested = true;
|
drc->unplug_requested = true;
|
||||||
|
|
||||||
|
spapr_drc_start_unplug_timeout_timer(drc);
|
||||||
|
|
||||||
if (drc->state != drck->empty_state) {
|
if (drc->state != drck->empty_state) {
|
||||||
trace_spapr_drc_awaiting_quiesce(spapr_drc_index(drc));
|
trace_spapr_drc_awaiting_quiesce(spapr_drc_index(drc));
|
||||||
return;
|
return;
|
||||||
@ -417,6 +419,15 @@ void spapr_drc_detach(SpaprDrc *drc)
|
|||||||
spapr_drc_release(drc);
|
spapr_drc_release(drc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int spapr_drc_unplug_timeout_remaining_sec(SpaprDrc *drc)
|
||||||
|
{
|
||||||
|
if (drc->unplug_requested) {
|
||||||
|
return timer_deadline_ms(drc->unplug_timeout_timer) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool spapr_drc_reset(SpaprDrc *drc)
|
bool spapr_drc_reset(SpaprDrc *drc)
|
||||||
{
|
{
|
||||||
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||||
@ -488,11 +499,23 @@ static bool spapr_drc_needed(void *opaque)
|
|||||||
spapr_drc_unplug_requested(drc);
|
spapr_drc_unplug_requested(drc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spapr_drc_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
SpaprDrc *drc = opaque;
|
||||||
|
|
||||||
|
if (drc->unplug_requested) {
|
||||||
|
spapr_drc_start_unplug_timeout_timer(drc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_spapr_drc = {
|
static const VMStateDescription vmstate_spapr_drc = {
|
||||||
.name = "spapr_drc",
|
.name = "spapr_drc",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.needed = spapr_drc_needed,
|
.needed = spapr_drc_needed,
|
||||||
|
.post_load = spapr_drc_post_load,
|
||||||
.fields = (VMStateField []) {
|
.fields = (VMStateField []) {
|
||||||
VMSTATE_UINT32(state, SpaprDrc),
|
VMSTATE_UINT32(state, SpaprDrc),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
@ -503,6 +526,15 @@ static const VMStateDescription vmstate_spapr_drc = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void drc_unplug_timeout_cb(void *opaque)
|
||||||
|
{
|
||||||
|
SpaprDrc *drc = opaque;
|
||||||
|
|
||||||
|
if (drc->unplug_requested) {
|
||||||
|
drc->unplug_requested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void drc_realize(DeviceState *d, Error **errp)
|
static void drc_realize(DeviceState *d, Error **errp)
|
||||||
{
|
{
|
||||||
SpaprDrc *drc = SPAPR_DR_CONNECTOR(d);
|
SpaprDrc *drc = SPAPR_DR_CONNECTOR(d);
|
||||||
@ -525,6 +557,11 @@ static void drc_realize(DeviceState *d, Error **errp)
|
|||||||
object_property_add_alias(root_container, link_name,
|
object_property_add_alias(root_container, link_name,
|
||||||
drc->owner, child_name);
|
drc->owner, child_name);
|
||||||
g_free(link_name);
|
g_free(link_name);
|
||||||
|
|
||||||
|
drc->unplug_timeout_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
|
||||||
|
drc_unplug_timeout_cb,
|
||||||
|
drc);
|
||||||
|
|
||||||
vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc,
|
vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc,
|
||||||
drc);
|
drc);
|
||||||
trace_spapr_drc_realize_complete(spapr_drc_index(drc));
|
trace_spapr_drc_realize_complete(spapr_drc_index(drc));
|
||||||
@ -542,6 +579,7 @@ static void drc_unrealize(DeviceState *d)
|
|||||||
name = g_strdup_printf("%x", spapr_drc_index(drc));
|
name = g_strdup_printf("%x", spapr_drc_index(drc));
|
||||||
object_property_del(root_container, name);
|
object_property_del(root_container, name);
|
||||||
g_free(name);
|
g_free(name);
|
||||||
|
timer_free(drc->unplug_timeout_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type,
|
SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type,
|
||||||
@ -683,6 +721,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data)
|
|||||||
drck->drc_name_prefix = "CPU ";
|
drck->drc_name_prefix = "CPU ";
|
||||||
drck->release = spapr_core_release;
|
drck->release = spapr_core_release;
|
||||||
drck->dt_populate = spapr_core_dt_populate;
|
drck->dt_populate = spapr_core_dt_populate;
|
||||||
|
drck->unplug_timeout_seconds = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
|
static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
|
||||||
@ -1190,6 +1229,15 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
|
|||||||
|
|
||||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This indicates that the kernel is reconfiguring a LMB due to
|
||||||
|
* a failed hotunplug. Rollback the DIMM unplug process.
|
||||||
|
*/
|
||||||
|
if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_LMB &&
|
||||||
|
drc->unplug_requested) {
|
||||||
|
spapr_memory_unplug_rollback(spapr, drc->dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (!drc->fdt) {
|
if (!drc->fdt) {
|
||||||
void *fdt;
|
void *fdt;
|
||||||
int fdt_size;
|
int fdt_size;
|
||||||
|
@ -1723,12 +1723,12 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
|
|||||||
* functions, even if their unplug weren't requested
|
* functions, even if their unplug weren't requested
|
||||||
* beforehand.
|
* beforehand.
|
||||||
*/
|
*/
|
||||||
spapr_drc_detach(func_drc);
|
spapr_drc_unplug_request(func_drc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_unplug_request(drc);
|
||||||
|
|
||||||
/* if this isn't func 0, defer unplug event. otherwise signal removal
|
/* if this isn't func 0, defer unplug event. otherwise signal removal
|
||||||
* for all present functions
|
* for all present functions
|
||||||
@ -1743,6 +1743,10 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
error_setg(errp,
|
||||||
|
"PCI device unplug already in progress for device %s",
|
||||||
|
drc->dev->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ spapr_drc_set_allocation_state(uint32_t index, int state) "drc: 0x%"PRIx32", sta
|
|||||||
spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32
|
||||||
spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32
|
||||||
spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32
|
||||||
spapr_drc_detach(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_unplug_request(uint32_t index) "drc: 0x%"PRIx32
|
||||||
spapr_drc_awaiting_quiesce(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_awaiting_quiesce(uint32_t index) "drc: 0x%"PRIx32
|
||||||
spapr_drc_reset(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_reset(uint32_t index) "drc: 0x%"PRIx32
|
||||||
spapr_drc_realize(uint32_t index) "drc: 0x%"PRIx32
|
spapr_drc_realize(uint32_t index) "drc: 0x%"PRIx32
|
||||||
|
@ -847,6 +847,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize);
|
|||||||
int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp);
|
int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp);
|
||||||
void spapr_clear_pending_events(SpaprMachineState *spapr);
|
void spapr_clear_pending_events(SpaprMachineState *spapr);
|
||||||
void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr);
|
void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr);
|
||||||
|
void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev);
|
||||||
int spapr_max_server_number(SpaprMachineState *spapr);
|
int spapr_max_server_number(SpaprMachineState *spapr);
|
||||||
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
||||||
uint64_t pte0, uint64_t pte1);
|
uint64_t pte0, uint64_t pte1);
|
||||||
|
@ -187,6 +187,8 @@ typedef struct SpaprDrc {
|
|||||||
bool unplug_requested;
|
bool unplug_requested;
|
||||||
void *fdt;
|
void *fdt;
|
||||||
int fdt_start_offset;
|
int fdt_start_offset;
|
||||||
|
|
||||||
|
QEMUTimer *unplug_timeout_timer;
|
||||||
} SpaprDrc;
|
} SpaprDrc;
|
||||||
|
|
||||||
struct SpaprMachineState;
|
struct SpaprMachineState;
|
||||||
@ -209,6 +211,8 @@ typedef struct SpaprDrcClass {
|
|||||||
|
|
||||||
int (*dt_populate)(SpaprDrc *drc, struct SpaprMachineState *spapr,
|
int (*dt_populate)(SpaprDrc *drc, struct SpaprMachineState *spapr,
|
||||||
void *fdt, int *fdt_start_offset, Error **errp);
|
void *fdt, int *fdt_start_offset, Error **errp);
|
||||||
|
|
||||||
|
int unplug_timeout_seconds;
|
||||||
} SpaprDrcClass;
|
} SpaprDrcClass;
|
||||||
|
|
||||||
typedef struct SpaprDrcPhysical {
|
typedef struct SpaprDrcPhysical {
|
||||||
@ -243,7 +247,8 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask);
|
|||||||
* beforehand (eg. check drc->dev at pre-plug).
|
* beforehand (eg. check drc->dev at pre-plug).
|
||||||
*/
|
*/
|
||||||
void spapr_drc_attach(SpaprDrc *drc, DeviceState *d);
|
void spapr_drc_attach(SpaprDrc *drc, DeviceState *d);
|
||||||
void spapr_drc_detach(SpaprDrc *drc);
|
void spapr_drc_unplug_request(SpaprDrc *drc);
|
||||||
|
int spapr_drc_unplug_timeout_remaining_sec(SpaprDrc *drc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset all DRCs, causing pending hot-plug/unplug requests to complete.
|
* Reset all DRCs, causing pending hot-plug/unplug requests to complete.
|
||||||
|
@ -795,6 +795,14 @@ static inline int64_t get_max_clock_jump(void)
|
|||||||
return 60 * NANOSECONDS_PER_SECOND;
|
return 60 * NANOSECONDS_PER_SECOND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* timer_deadline_ms:
|
||||||
|
*
|
||||||
|
* Returns the remaining miliseconds for @timer to expire, or zero
|
||||||
|
* if the timer is no longer pending.
|
||||||
|
*/
|
||||||
|
int64_t timer_deadline_ms(QEMUTimer *timer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Low level clock functions
|
* Low level clock functions
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||||
implementation for certain IBM POWER hardware. The sources are at
|
implementation for certain IBM POWER hardware. The sources are at
|
||||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
https://github.com/aik/SLOF, and the image currently in qemu is
|
||||||
built from git tag qemu-slof-20200717.
|
built from git tag qemu-slof-20210217.
|
||||||
|
|
||||||
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
|
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
|
||||||
legacy x86 software to communicate with an attached serial console as
|
legacy x86 software to communicate with an attached serial console as
|
||||||
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
@ -1 +1 @@
|
|||||||
Subproject commit e18ddad8516ff2cfe36ec130200318f7251aa78c
|
Subproject commit 33a7322de13e9dca4b38851a345a58d37e7a441d
|
@ -2175,14 +2175,17 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
||||||
int *overflow)
|
int *overflow)
|
||||||
{
|
{
|
||||||
int carry = 0;
|
int carry = 0;
|
||||||
int i;
|
int i;
|
||||||
|
int is_zero = 1;
|
||||||
|
|
||||||
for (i = 1; i <= 31; i++) {
|
for (i = 1; i <= 31; i++) {
|
||||||
uint8_t digit = bcd_get_digit(a, i, invalid) +
|
uint8_t digit = bcd_get_digit(a, i, invalid) +
|
||||||
bcd_get_digit(b, i, invalid) + carry;
|
bcd_get_digit(b, i, invalid) + carry;
|
||||||
|
is_zero &= (digit == 0);
|
||||||
if (digit > 9) {
|
if (digit > 9) {
|
||||||
carry = 1;
|
carry = 1;
|
||||||
digit -= 10;
|
digit -= 10;
|
||||||
@ -2194,6 +2197,7 @@ static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*overflow = carry;
|
*overflow = carry;
|
||||||
|
return is_zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid,
|
||||||
@ -2225,14 +2229,15 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
|
|||||||
int sgnb = bcd_get_sgn(b);
|
int sgnb = bcd_get_sgn(b);
|
||||||
int invalid = (sgna == 0) || (sgnb == 0);
|
int invalid = (sgna == 0) || (sgnb == 0);
|
||||||
int overflow = 0;
|
int overflow = 0;
|
||||||
|
int zero = 0;
|
||||||
uint32_t cr = 0;
|
uint32_t cr = 0;
|
||||||
ppc_avr_t result = { .u64 = { 0, 0 } };
|
ppc_avr_t result = { .u64 = { 0, 0 } };
|
||||||
|
|
||||||
if (!invalid) {
|
if (!invalid) {
|
||||||
if (sgna == sgnb) {
|
if (sgna == sgnb) {
|
||||||
result.VsrB(BCD_DIG_BYTE(0)) = bcd_preferred_sgn(sgna, ps);
|
result.VsrB(BCD_DIG_BYTE(0)) = bcd_preferred_sgn(sgna, ps);
|
||||||
bcd_add_mag(&result, a, b, &invalid, &overflow);
|
zero = bcd_add_mag(&result, a, b, &invalid, &overflow);
|
||||||
cr = bcd_cmp_zero(&result);
|
cr = (sgna > 0) ? CRF_GT : CRF_LT;
|
||||||
} else {
|
} else {
|
||||||
int magnitude = bcd_cmp_mag(a, b);
|
int magnitude = bcd_cmp_mag(a, b);
|
||||||
if (magnitude > 0) {
|
if (magnitude > 0) {
|
||||||
@ -2255,6 +2260,8 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps)
|
|||||||
cr = CRF_SO;
|
cr = CRF_SO;
|
||||||
} else if (overflow) {
|
} else if (overflow) {
|
||||||
cr |= CRF_SO;
|
cr |= CRF_SO;
|
||||||
|
} else if (zero) {
|
||||||
|
cr |= CRF_EQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
*r = result;
|
*r = result;
|
||||||
|
@ -566,35 +566,71 @@ static void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn)
|
|||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
static void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn)
|
static void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn)
|
||||||
{
|
{
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_io_start();
|
||||||
|
}
|
||||||
gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env);
|
gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env);
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_stop_exception(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn)
|
static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn)
|
||||||
{
|
{
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_io_start();
|
||||||
|
}
|
||||||
gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]);
|
gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]);
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_stop_exception(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn)
|
static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn)
|
||||||
{
|
{
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_io_start();
|
||||||
|
}
|
||||||
gen_store_spr(sprn, cpu_gpr[gprn]);
|
gen_store_spr(sprn, cpu_gpr[gprn]);
|
||||||
gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]);
|
gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]);
|
||||||
/* We must stop translation as we may have rebooted */
|
/* We must stop translation as we may have rebooted */
|
||||||
gen_stop_exception(ctx);
|
gen_stop_exception(ctx);
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_stop_exception(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn)
|
static void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn)
|
||||||
{
|
{
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_io_start();
|
||||||
|
}
|
||||||
gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]);
|
gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]);
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_stop_exception(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn)
|
static void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn)
|
||||||
{
|
{
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_io_start();
|
||||||
|
}
|
||||||
gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]);
|
gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]);
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_stop_exception(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn)
|
static void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn)
|
||||||
{
|
{
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_io_start();
|
||||||
|
}
|
||||||
gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]);
|
gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]);
|
||||||
|
if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
|
||||||
|
gen_stop_exception(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -251,6 +251,12 @@ for target in $target_list; do
|
|||||||
echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak
|
echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
ppc*)
|
||||||
|
if do_compiler "$target_compiler" $target_compiler_cflags \
|
||||||
|
-mpower8-vector -o $TMPE $TMPC; then
|
||||||
|
echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
enabled_cross_compilers="$enabled_cross_compilers $target_compiler"
|
enabled_cross_compilers="$enabled_cross_compilers $target_compiler"
|
||||||
|
13
tests/tcg/ppc64/Makefile.target
Normal file
13
tests/tcg/ppc64/Makefile.target
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# -*- Mode: makefile -*-
|
||||||
|
#
|
||||||
|
# ppc64 specific tweaks
|
||||||
|
|
||||||
|
VPATH += $(SRC_PATH)/tests/tcg/ppc64
|
||||||
|
VPATH += $(SRC_PATH)/tests/tcg/ppc64le
|
||||||
|
|
||||||
|
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),)
|
||||||
|
PPC64_TESTS=bcdsub
|
||||||
|
endif
|
||||||
|
bcdsub: CFLAGS += -mpower8-vector
|
||||||
|
|
||||||
|
TESTS += $(PPC64_TESTS)
|
12
tests/tcg/ppc64le/Makefile.target
Normal file
12
tests/tcg/ppc64le/Makefile.target
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# -*- Mode: makefile -*-
|
||||||
|
#
|
||||||
|
# ppc64le specific tweaks
|
||||||
|
|
||||||
|
VPATH += $(SRC_PATH)/tests/tcg/ppc64le
|
||||||
|
|
||||||
|
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),)
|
||||||
|
PPC64LE_TESTS=bcdsub
|
||||||
|
endif
|
||||||
|
bcdsub: CFLAGS += -mpower8-vector
|
||||||
|
|
||||||
|
TESTS += $(PPC64LE_TESTS)
|
130
tests/tcg/ppc64le/bcdsub.c
Normal file
130
tests/tcg/ppc64le/bcdsub.c
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#define CRF_LT (1 << 3)
|
||||||
|
#define CRF_GT (1 << 2)
|
||||||
|
#define CRF_EQ (1 << 1)
|
||||||
|
#define CRF_SO (1 << 0)
|
||||||
|
#define UNDEF 0
|
||||||
|
|
||||||
|
#define BCDSUB(vra, vrb, ps) \
|
||||||
|
asm ("bcdsub. %1,%2,%3,%4;" \
|
||||||
|
"mfocrf %0,0b10;" \
|
||||||
|
: "=r" (cr), "=v" (vrt) \
|
||||||
|
: "v" (vra), "v" (vrb), "i" (ps) \
|
||||||
|
: );
|
||||||
|
|
||||||
|
#define TEST(vra, vrb, ps, exp_res, exp_cr6) \
|
||||||
|
do { \
|
||||||
|
__int128 vrt = 0; \
|
||||||
|
int cr = 0; \
|
||||||
|
BCDSUB(vra, vrb, ps); \
|
||||||
|
if (exp_res) \
|
||||||
|
assert(vrt == exp_res); \
|
||||||
|
assert((cr >> 4) == exp_cr6); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unbounded result is equal to zero:
|
||||||
|
* sign = (PS) ? 0b1111 : 0b1100
|
||||||
|
* CR6 = 0b0010
|
||||||
|
*/
|
||||||
|
void test_bcdsub_eq(void)
|
||||||
|
{
|
||||||
|
__int128 a, b;
|
||||||
|
|
||||||
|
/* maximum positive BCD value */
|
||||||
|
a = b = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c);
|
||||||
|
|
||||||
|
TEST(a, b, 0, 0xc, CRF_EQ);
|
||||||
|
TEST(a, b, 1, 0xf, CRF_EQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unbounded result is greater than zero:
|
||||||
|
* sign = (PS) ? 0b1111 : 0b1100
|
||||||
|
* CR6 = (overflow) ? 0b0101 : 0b0100
|
||||||
|
*/
|
||||||
|
void test_bcdsub_gt(void)
|
||||||
|
{
|
||||||
|
__int128 a, b, c;
|
||||||
|
|
||||||
|
/* maximum positive BCD value */
|
||||||
|
a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c);
|
||||||
|
|
||||||
|
/* negative one BCD value */
|
||||||
|
b = (__int128) 0x1d;
|
||||||
|
|
||||||
|
TEST(a, b, 0, 0xc, (CRF_GT | CRF_SO));
|
||||||
|
TEST(a, b, 1, 0xf, (CRF_GT | CRF_SO));
|
||||||
|
|
||||||
|
c = (((__int128) 0x9999999999999999) << 64 | 0x999999999999998c);
|
||||||
|
|
||||||
|
TEST(c, b, 0, a, CRF_GT);
|
||||||
|
TEST(c, b, 1, (a | 0x3), CRF_GT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unbounded result is less than zero:
|
||||||
|
* sign = 0b1101
|
||||||
|
* CR6 = (overflow) ? 0b1001 : 0b1000
|
||||||
|
*/
|
||||||
|
void test_bcdsub_lt(void)
|
||||||
|
{
|
||||||
|
__int128 a, b;
|
||||||
|
|
||||||
|
/* positive zero BCD value */
|
||||||
|
a = (__int128) 0xc;
|
||||||
|
|
||||||
|
/* positive one BCD value */
|
||||||
|
b = (__int128) 0x1c;
|
||||||
|
|
||||||
|
TEST(a, b, 0, 0x1d, CRF_LT);
|
||||||
|
TEST(a, b, 1, 0x1d, CRF_LT);
|
||||||
|
|
||||||
|
/* maximum negative BCD value */
|
||||||
|
a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999d);
|
||||||
|
|
||||||
|
/* positive one BCD value */
|
||||||
|
b = (__int128) 0x1c;
|
||||||
|
|
||||||
|
TEST(a, b, 0, 0xd, (CRF_LT | CRF_SO));
|
||||||
|
TEST(a, b, 1, 0xd, (CRF_LT | CRF_SO));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_bcdsub_invalid(void)
|
||||||
|
{
|
||||||
|
__int128 a, b;
|
||||||
|
|
||||||
|
/* positive one BCD value */
|
||||||
|
a = (__int128) 0x1c;
|
||||||
|
b = 0xf00;
|
||||||
|
|
||||||
|
TEST(a, b, 0, UNDEF, CRF_SO);
|
||||||
|
TEST(a, b, 1, UNDEF, CRF_SO);
|
||||||
|
|
||||||
|
TEST(b, a, 0, UNDEF, CRF_SO);
|
||||||
|
TEST(b, a, 1, UNDEF, CRF_SO);
|
||||||
|
|
||||||
|
a = 0xbad;
|
||||||
|
|
||||||
|
TEST(a, b, 0, UNDEF, CRF_SO);
|
||||||
|
TEST(a, b, 1, UNDEF, CRF_SO);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct sigaction action;
|
||||||
|
|
||||||
|
action.sa_handler = _exit;
|
||||||
|
sigaction(SIGABRT, &action, NULL);
|
||||||
|
|
||||||
|
test_bcdsub_eq();
|
||||||
|
test_bcdsub_gt();
|
||||||
|
test_bcdsub_lt();
|
||||||
|
test_bcdsub_invalid();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -242,6 +242,19 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list)
|
|||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the time remaining for the deadline, in ms.
|
||||||
|
*/
|
||||||
|
int64_t timer_deadline_ms(QEMUTimer *timer)
|
||||||
|
{
|
||||||
|
if (timer_pending(timer)) {
|
||||||
|
return qemu_timeout_ns_to_ms(timer->expire_time) -
|
||||||
|
qemu_clock_get_ms(timer->timer_list->clock->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the soonest deadline across all timerlists attached
|
/* Calculate the soonest deadline across all timerlists attached
|
||||||
* to the clock. This is used for the icount timeout so we
|
* to the clock. This is used for the icount timeout so we
|
||||||
* ignore whether or not the clock should be used in deadline
|
* ignore whether or not the clock should be used in deadline
|
||||||
|
Loading…
Reference in New Issue
Block a user