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:
Peter Maydell 2021-03-12 11:30:55 +00:00
commit 1941858448
28 changed files with 754 additions and 296 deletions

View 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

View 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
View 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
View 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.

View File

@ -0,0 +1,12 @@
pSeries family boards (``pseries``)
===================================
Supported devices
-----------------
Missing devices
---------------
Firmware
--------

View File

@ -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/.

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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");

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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);
} }
} }

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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
*/ */

View File

@ -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

Binary file not shown.

@ -1 +1 @@
Subproject commit e18ddad8516ff2cfe36ec130200318f7251aa78c Subproject commit 33a7322de13e9dca4b38851a345a58d37e7a441d

View File

@ -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;

View File

@ -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

View File

@ -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"

View 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)

View 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
View 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;
}

View File

@ -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