"Host Memory Backends" and "Memory devices" queue ("mem"):
 - Fixup handling of virtio-mem unplug during system resets, as
   preparation for s390x support (especially kdump in the Linux guest)
 - virtio-mem support for s390x
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEG9nKrXNcTDpGDfzKTd4Q9wD/g1oFAmdnFD4RHGRhdmlkQHJl
 ZGhhdC5jb20ACgkQTd4Q9wD/g1rWBBAAp7WkYaNAjRy1PgpjNZ3z1gUJc/vk+skJ
 xVgGodA8txrJOFpNrbTyfhrdLs2TV4oWDvB/zrZRRtuxvur3O1EhFd9k6EqXuydr
 0FunvLvVJwRHfEZycjN4aacQMRH3CJw07OaTzexeSl5UR/6w5PRofwUK4HX7W/Ka
 arqomGa3OJrs1+WgkV0Qcn4vh9HLRVv3iNC2Xo4W1wOCr1Du9zSPn9oC7zOQ0EO4
 ZC//7QsdkNRjUX/yMXMkhlSXx3b/RmRg2DBrxo7BZXg27VwGu4uHxL4LRBZiB2A7
 V9MqFOcVKzPMkXKTRjrgZ0vXQx1MPJ6WprEihMzMpYU6DrpA7KN/l8Ca8H24B2ln
 h7+bmkDsHVVcWovE9ii/9cMRfws6uWXXg3KoA8RQ8IbX1tU02lblw2uHhXEzcoge
 npqp/Z5LAiKVMetEnNnLH5thjut5PAEjuqD00cmZAMy4DNngLX2bGSdzMeVBkDMa
 78ehLGRplm3t7ibUfaZaMKe6UD9tFrcD6XKsvUTXXHNbYO8ynbx58WOxSZmY98zU
 n3JNQRqtXYjBVlH3Dqm47vOTZHgOzFv3raa8BmSLpcBDeTXCTcUIl20s77dGw/vT
 r5YNCMN7O4YPFKUoRK9604QTgw6qlYaRTQlJD09usprGqVylb6gQtfZZuZkYDMp8
 sEI77QHsePA=
 =HDxr
 -----END PGP SIGNATURE-----

Merge tag 'mem-2024-12-21' of https://github.com/davidhildenbrand/qemu into staging

Hi,

"Host Memory Backends" and "Memory devices" queue ("mem"):
- Fixup handling of virtio-mem unplug during system resets, as
  preparation for s390x support (especially kdump in the Linux guest)
- virtio-mem support for s390x

 # -----BEGIN PGP SIGNATURE-----
 #
 # iQJFBAABCAAvFiEEG9nKrXNcTDpGDfzKTd4Q9wD/g1oFAmdnFD4RHGRhdmlkQHJl
 # ZGhhdC5jb20ACgkQTd4Q9wD/g1rWBBAAp7WkYaNAjRy1PgpjNZ3z1gUJc/vk+skJ
 # xVgGodA8txrJOFpNrbTyfhrdLs2TV4oWDvB/zrZRRtuxvur3O1EhFd9k6EqXuydr
 # 0FunvLvVJwRHfEZycjN4aacQMRH3CJw07OaTzexeSl5UR/6w5PRofwUK4HX7W/Ka
 # arqomGa3OJrs1+WgkV0Qcn4vh9HLRVv3iNC2Xo4W1wOCr1Du9zSPn9oC7zOQ0EO4
 # ZC//7QsdkNRjUX/yMXMkhlSXx3b/RmRg2DBrxo7BZXg27VwGu4uHxL4LRBZiB2A7
 # V9MqFOcVKzPMkXKTRjrgZ0vXQx1MPJ6WprEihMzMpYU6DrpA7KN/l8Ca8H24B2ln
 # h7+bmkDsHVVcWovE9ii/9cMRfws6uWXXg3KoA8RQ8IbX1tU02lblw2uHhXEzcoge
 # npqp/Z5LAiKVMetEnNnLH5thjut5PAEjuqD00cmZAMy4DNngLX2bGSdzMeVBkDMa
 # 78ehLGRplm3t7ibUfaZaMKe6UD9tFrcD6XKsvUTXXHNbYO8ynbx58WOxSZmY98zU
 # n3JNQRqtXYjBVlH3Dqm47vOTZHgOzFv3raa8BmSLpcBDeTXCTcUIl20s77dGw/vT
 # r5YNCMN7O4YPFKUoRK9604QTgw6qlYaRTQlJD09usprGqVylb6gQtfZZuZkYDMp8
 # sEI77QHsePA=
 # =HDxr
 # -----END PGP SIGNATURE-----
 # gpg: Signature made Sat 21 Dec 2024 14:17:18 EST
 # gpg:                using RSA key 1BD9CAAD735C4C3A460DFCCA4DDE10F700FF835A
 # gpg:                issuer "david@redhat.com"
 # gpg: Good signature from "David Hildenbrand <david@redhat.com>" [unknown]
 # gpg:                 aka "David Hildenbrand <davidhildenbrand@gmail.com>" [full]
 # gpg:                 aka "David Hildenbrand <hildenbr@in.tum.de>" [unknown]
 # gpg: WARNING: The key's User ID is not certified with a trusted signature!
 # gpg:          There is no indication that the signature belongs to the owner.
 # Primary key fingerprint: 1BD9 CAAD 735C 4C3A 460D  FCCA 4DDE 10F7 00FF 835A

* tag 'mem-2024-12-21' of https://github.com/davidhildenbrand/qemu:
  s390x: virtio-mem support
  s390x/virtio-ccw: add support for virtio based memory devices
  s390x: remember the maximum page size
  s390x/pv: prepare for memory devices
  s390x/s390-virtio-ccw: prepare for memory devices
  s390x/s390-skeys: prepare for memory devices
  s390x/s390-stattrib-kvm: prepare for memory devices and sparse memory layouts
  s390x/s390-hypercall: introduce DIAG500 STORAGE_LIMIT
  s390x: introduce s390_get_memory_limit()
  s390x/s390-virtio-ccw: move setting the maximum guest size from sclp to machine code
  s390x: rename s390-virtio-hcall* to s390-hypercall*
  s390x/s390-virtio-hcall: prepare for more diag500 hypercalls
  s390x/s390-virtio-hcall: remove hypercall registration mechanism
  s390x/s390-virtio-ccw: don't crash on weird RAM sizes
  virtio-mem: unplug memory only during system resets, not device resets

Conflicts:
- hw/s390x/s390-stattrib-kvm.c
  sysemu/ -> system/ header rename conflict.
- hw/s390x/virtio-ccw-mem.c
  Make Property array const and removed DEFINE_PROP_END_OF_LIST() to
  conform to the latest conventions.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2024-12-22 10:45:35 -05:00
commit aa3a285b5b
25 changed files with 879 additions and 235 deletions

View file

@ -2384,6 +2384,9 @@ F: include/hw/virtio/virtio-crypto.h
virtio based memory device
M: David Hildenbrand <david@redhat.com>
S: Supported
F: hw/s390x/virtio-ccw-md.c
F: hw/s390x/virtio-ccw-md.h
F: hw/s390x/virtio-ccw-md-stubs.c
F: hw/virtio/virtio-md-pci.c
F: include/hw/virtio/virtio-md-pci.h
F: stubs/virtio-md-pci.c
@ -2395,6 +2398,8 @@ W: https://virtio-mem.gitlab.io/
F: hw/virtio/virtio-mem.c
F: hw/virtio/virtio-mem-pci.h
F: hw/virtio/virtio-mem-pci.c
F: hw/s390x/virtio-ccw-mem.c
F: hw/s390x/virtio-ccw-mem.h
F: include/hw/virtio/virtio-mem.h
virtio-snd

View file

@ -16,3 +16,4 @@ config S390_CCW_VIRTIO
select SCLPCONSOLE
select VIRTIO_CCW
select MSI_NONBROKEN
select VIRTIO_MEM_SUPPORTED

View file

@ -12,7 +12,6 @@ s390x_ss.add(files(
's390-pci-inst.c',
's390-skeys.c',
's390-stattrib.c',
's390-virtio-hcall.c',
'sclp.c',
'sclpcpu.c',
'sclpquiesce.c',
@ -28,7 +27,10 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files(
s390x_ss.add(when: 'CONFIG_TCG', if_true: files(
'tod-tcg.c',
))
s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files('s390-virtio-ccw.c'))
s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files(
's390-virtio-ccw.c',
's390-hypercall.c',
))
s390x_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('3270-ccw.c'))
s390x_ss.add(when: 'CONFIG_VFIO', if_true: files('s390-pci-vfio.c'))
@ -48,8 +50,12 @@ endif
virtio_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-ccw.c'))
virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-ccw.c'))
virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-ccw.c'))
virtio_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-ccw-md.c'))
virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-ccw-mem.c'))
s390x_ss.add_all(when: 'CONFIG_VIRTIO_CCW', if_true: virtio_ss)
s390x_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-ccw-md-stubs.c'))
hw_arch += {'s390x': s390x_ss}
hw_s390x_modules = {}

85
hw/s390x/s390-hypercall.c Normal file
View file

@ -0,0 +1,85 @@
/*
* Support for QEMU/KVM hypercalls on s390
*
* Copyright 2012 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/s390x/s390-hypercall.h"
#include "hw/s390x/ioinst.h"
#include "hw/s390x/css.h"
#include "virtio-ccw.h"
static int handle_virtio_notify(uint64_t mem)
{
MachineState *ms = MACHINE(qdev_get_machine());
if (mem < ms->ram_size) {
/* Early printk */
return 0;
}
return -EINVAL;
}
static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data)
{
SubchDev *sch;
VirtIODevice *vdev;
int cssid, ssid, schid, m;
uint16_t vq_idx = data;
if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) {
return -EINVAL;
}
sch = css_find_subch(m, cssid, ssid, schid);
if (!sch || !css_subch_visible(sch)) {
return -EINVAL;
}
vdev = virtio_ccw_get_vdev(sch);
if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) {
return -EINVAL;
}
if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx),
(data >> 16) & 0xFFFF);
}
virtio_queue_notify(vdev, vq_idx);
return 0;
}
static uint64_t handle_storage_limit(void)
{
S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
return s390_get_memory_limit(s390ms) - 1;
}
void handle_diag_500(S390CPU *cpu, uintptr_t ra)
{
CPUS390XState *env = &cpu->env;
const uint64_t subcode = env->regs[1];
switch (subcode) {
case DIAG500_VIRTIO_NOTIFY:
env->regs[2] = handle_virtio_notify(env->regs[2]);
break;
case DIAG500_VIRTIO_CCW_NOTIFY:
env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]);
break;
case DIAG500_STORAGE_LIMIT:
env->regs[2] = handle_storage_limit();
break;
default:
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
}
}

25
hw/s390x/s390-hypercall.h Normal file
View file

@ -0,0 +1,25 @@
/*
* Support for QEMU/KVM hypercalls on s390x
*
* Copyright IBM Corp. 2012, 2017
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#ifndef HW_S390_HYPERCALL_H
#define HW_S390_HYPERCALL_H
#include "cpu.h"
#define DIAG500_VIRTIO_NOTIFY 0 /* legacy, implemented as a NOP */
#define DIAG500_VIRTIO_RESET 1 /* legacy */
#define DIAG500_VIRTIO_SET_STATUS 2 /* legacy */
#define DIAG500_VIRTIO_CCW_NOTIFY 3 /* KVM_S390_VIRTIO_CCW_NOTIFY */
#define DIAG500_STORAGE_LIMIT 4
void handle_diag_500(S390CPU *cpu, uintptr_t ra);
#endif /* HW_S390_HYPERCALL_H */

View file

@ -11,7 +11,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "hw/boards.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/qdev-properties.h"
#include "hw/s390x/storage-keys.h"
#include "qapi/error.h"
@ -251,9 +251,9 @@ static bool qemu_s390_enable_skeys(S390SKeysState *ss)
* g_once_init_enter() is good enough.
*/
if (g_once_init_enter(&initialized)) {
MachineState *machine = MACHINE(qdev_get_machine());
S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
skeys->key_count = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE;
skeys->keydata = g_malloc0(skeys->key_count);
g_once_init_leave(&initialized, 1);
}

View file

@ -10,11 +10,12 @@
*/
#include "qemu/osdep.h"
#include "hw/boards.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "migration/qemu-file.h"
#include "hw/s390x/storage-attributes.h"
#include "qemu/error-report.h"
#include "system/kvm.h"
#include "system/memory_mapping.h"
#include "exec/ram_addr.h"
#include "kvm/kvm_s390x.h"
#include "qapi/error.h"
@ -84,8 +85,8 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa,
uint8_t *values)
{
KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa);
MachineState *machine = MACHINE(qdev_get_machine());
unsigned long max = machine->ram_size / TARGET_PAGE_SIZE;
S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE;
if (start_gfn + count > max) {
error_report("Out of memory bounds when setting storage attributes");
@ -103,39 +104,57 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa,
static void kvm_s390_stattrib_synchronize(S390StAttribState *sa)
{
KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa);
MachineState *machine = MACHINE(qdev_get_machine());
unsigned long max = machine->ram_size / TARGET_PAGE_SIZE;
/* We do not need to reach the maximum buffer size allowed */
unsigned long cx, len = KVM_S390_SKEYS_MAX / 2;
S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE;
unsigned long start_gfn, end_gfn, pages;
GuestPhysBlockList guest_phys_blocks;
GuestPhysBlock *block;
int r;
struct kvm_s390_cmma_log clog = {
.flags = 0,
.mask = ~0ULL,
};
if (sas->incoming_buffer) {
for (cx = 0; cx + len <= max; cx += len) {
clog.start_gfn = cx;
clog.count = len;
clog.values = (uint64_t)(sas->incoming_buffer + cx);
r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog);
if (r) {
error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r));
return;
}
}
if (cx < max) {
clog.start_gfn = cx;
clog.count = max - cx;
clog.values = (uint64_t)(sas->incoming_buffer + cx);
r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog);
if (r) {
error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r));
}
}
g_free(sas->incoming_buffer);
sas->incoming_buffer = NULL;
if (!sas->incoming_buffer) {
return;
}
guest_phys_blocks_init(&guest_phys_blocks);
guest_phys_blocks_append(&guest_phys_blocks);
QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
start_gfn = block->target_start / TARGET_PAGE_SIZE;
end_gfn = block->target_end / TARGET_PAGE_SIZE;
while (start_gfn < end_gfn) {
/* Don't exceed the maximum buffer size. */
pages = MIN(end_gfn - start_gfn, KVM_S390_SKEYS_MAX / 2);
/*
* If we ever get guest physical memory beyond the configured
* memory limit, something went very wrong.
*/
assert(start_gfn + pages <= max);
clog.start_gfn = start_gfn;
clog.count = pages;
clog.values = (uint64_t)(sas->incoming_buffer + start_gfn);
r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog);
if (r) {
error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r));
goto out;
}
start_gfn += pages;
}
}
out:
guest_phys_blocks_free(&guest_phys_blocks);
g_free(sas->incoming_buffer);
sas->incoming_buffer = NULL;
}
static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val,

View file

@ -16,11 +16,8 @@
#include "exec/ram_addr.h"
#include "system/confidential-guest-support.h"
#include "hw/boards.h"
#include "hw/s390x/s390-virtio-hcall.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/ioinst.h"
#include "hw/s390x/css.h"
#include "virtio-ccw.h"
#include "qemu/config-file.h"
#include "qemu/ctype.h"
@ -48,6 +45,9 @@
#include "migration/blocker.h"
#include "qapi/visitor.h"
#include "hw/s390x/cpu-topology.h"
#include "kvm/kvm_s390x.h"
#include "hw/virtio/virtio-md-pci.h"
#include "hw/s390x/virtio-ccw-md.h"
#include CONFIG_DEVICES
static Error *pv_mig_blocker;
@ -124,70 +124,86 @@ static void subsystem_reset(void)
}
}
static int virtio_ccw_hcall_notify(const uint64_t *args)
static void s390_set_memory_limit(S390CcwMachineState *s390ms,
uint64_t new_limit)
{
uint64_t subch_id = args[0];
uint64_t data = args[1];
SubchDev *sch;
VirtIODevice *vdev;
int cssid, ssid, schid, m;
uint16_t vq_idx = data;
uint64_t hw_limit = 0;
int ret = 0;
if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) {
return -EINVAL;
assert(!s390ms->memory_limit && new_limit);
if (kvm_enabled()) {
ret = kvm_s390_set_mem_limit(new_limit, &hw_limit);
}
sch = css_find_subch(m, cssid, ssid, schid);
if (!sch || !css_subch_visible(sch)) {
return -EINVAL;
if (ret == -E2BIG) {
error_report("host supports a maximum of %" PRIu64 " GB",
hw_limit / GiB);
exit(EXIT_FAILURE);
} else if (ret) {
error_report("setting the guest size failed");
exit(EXIT_FAILURE);
}
vdev = virtio_ccw_get_vdev(sch);
if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) {
return -EINVAL;
}
if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx),
(data >> 16) & 0xFFFF);
}
virtio_queue_notify(vdev, vq_idx);
return 0;
s390ms->memory_limit = new_limit;
}
static int virtio_ccw_hcall_early_printk(const uint64_t *args)
static void s390_set_max_pagesize(S390CcwMachineState *s390ms,
uint64_t pagesize)
{
uint64_t mem = args[0];
MachineState *ms = MACHINE(qdev_get_machine());
if (mem < ms->ram_size) {
/* Early printk */
return 0;
assert(!s390ms->max_pagesize && pagesize);
if (kvm_enabled()) {
kvm_s390_set_max_pagesize(pagesize, &error_fatal);
}
return -EINVAL;
s390ms->max_pagesize = pagesize;
}
static void virtio_ccw_register_hcalls(void)
{
s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY,
virtio_ccw_hcall_notify);
/* Tolerate early printk. */
s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY,
virtio_ccw_hcall_early_printk);
}
static void s390_memory_init(MemoryRegion *ram)
static void s390_memory_init(MachineState *machine)
{
S390CcwMachineState *s390ms = S390_CCW_MACHINE(machine);
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = machine->ram;
uint64_t ram_size = memory_region_size(ram);
uint64_t devmem_base, devmem_size;
/* allocate RAM for core */
if (!QEMU_IS_ALIGNED(ram_size, 1 * MiB)) {
/*
* SCLP cannot possibly expose smaller granularity right now and KVM
* cannot handle smaller granularity. As we don't support NUMA, the
* region size directly corresponds to machine->ram_size, and the region
* is a single RAM memory region.
*/
error_report("ram size must be multiples of 1 MiB");
exit(EXIT_FAILURE);
}
devmem_size = 0;
devmem_base = ram_size;
#ifdef CONFIG_MEM_DEVICE
if (machine->ram_size < machine->maxram_size) {
/*
* Make sure memory devices have a sane default alignment, even
* when weird initial memory sizes are specified.
*/
devmem_base = QEMU_ALIGN_UP(devmem_base, 1 * GiB);
devmem_size = machine->maxram_size - machine->ram_size;
}
#endif
s390_set_memory_limit(s390ms, devmem_base + devmem_size);
/* Map the initial memory. Must happen after setting the memory limit. */
memory_region_add_subregion(sysmem, 0, ram);
/* Initialize address space for memory devices. */
#ifdef CONFIG_MEM_DEVICE
if (devmem_size) {
machine_memory_devices_init(machine, devmem_base, devmem_size);
}
#endif /* CONFIG_MEM_DEVICE */
/*
* Configure the maximum page size. As no memory devices were created
* yet, this is the page size of initial memory only.
*/
s390_set_max_pagesize(qemu_maxrampagesize(), &error_fatal);
s390_set_max_pagesize(s390ms, qemu_maxrampagesize());
/* Initialize storage key device */
s390_skeys_init();
/* Initialize storage attributes device */
@ -255,7 +271,7 @@ static void ccw_init(MachineState *machine)
qdev_realize_and_unref(DEVICE(ms->sclp), NULL, &error_fatal);
/* init memory + setup max page size. Required for the CPU model */
s390_memory_init(machine->ram);
s390_memory_init(machine);
/* init CPUs (incl. CPU model) early so s390_has_feature() works */
s390_init_cpus(machine);
@ -285,9 +301,6 @@ static void ccw_init(MachineState *machine)
OBJECT(dev));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
/* register hypercalls */
virtio_ccw_register_hcalls();
s390_enable_css_support(s390_cpu_addr2state(0));
ret = css_create_css_image(VIRTUAL_CSSID, true);
@ -535,11 +548,39 @@ static void s390_machine_reset(MachineState *machine, ResetType type)
s390_ipl_clear_reset_request();
}
static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
virtio_ccw_md_pre_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
error_setg(errp,
"PCI-attached virtio based memory devices not supported");
}
}
static void s390_machine_device_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
S390CcwMachineState *s390ms = S390_CCW_MACHINE(hotplug_dev);
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
s390_cpu_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
/*
* At this point, the device is realized and set all memdevs mapped, so
* qemu_maxrampagesize() will pick up the page sizes of these memdevs
* as well. Before we plug the device and expose any RAM memory regions
* to the system, make sure we don't exceed the previously set max page
* size. While only relevant for KVM, there is not really any use case
* for this with TCG, so we'll unconditionally reject it.
*/
if (qemu_maxrampagesize() != s390ms->max_pagesize) {
error_setg(errp, "Memory device uses a bigger page size than"
" initial memory");
return;
}
virtio_ccw_md_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp);
}
}
@ -549,9 +590,20 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
error_setg(errp, "CPU hot unplug not supported on this machine");
return;
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
virtio_ccw_md_unplug_request(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev),
errp);
}
}
static void s390_machine_device_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
virtio_ccw_md_unplug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp);
}
}
static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms,
unsigned cpu_index)
{
@ -598,7 +650,9 @@ static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms)
static HotplugHandler *s390_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
return HOTPLUG_HANDLER(machine);
}
return NULL;
@ -758,8 +812,10 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids;
/* it is overridden with 'host' cpu *in kvm_arch_init* */
mc->default_cpu_type = S390_CPU_TYPE_NAME("qemu");
hc->pre_plug = s390_machine_device_pre_plug;
hc->plug = s390_machine_device_plug;
hc->unplug_request = s390_machine_device_unplug_request;
hc->unplug = s390_machine_device_unplug;
nc->nmi_monitor_handler = s390_nmi;
mc->default_ram_id = "s390.ram";
mc->default_nic = "virtio-net-ccw";

View file

@ -1,41 +0,0 @@
/*
* Support for virtio hypercalls on s390
*
* Copyright 2012 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/s390x/s390-virtio-hcall.h"
#define MAX_DIAG_SUBCODES 255
static s390_virtio_fn s390_diag500_table[MAX_DIAG_SUBCODES];
void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn)
{
assert(code < MAX_DIAG_SUBCODES);
assert(!s390_diag500_table[code]);
s390_diag500_table[code] = fn;
}
int s390_virtio_hypercall(CPUS390XState *env)
{
s390_virtio_fn fn;
if (env->regs[1] < MAX_DIAG_SUBCODES) {
fn = s390_diag500_table[env->regs[1]];
if (fn) {
env->regs[2] = fn(&env->regs[2]);
return 0;
}
}
return -EINVAL;
}

View file

@ -1,25 +0,0 @@
/*
* Support for virtio hypercalls on s390x
*
* Copyright IBM Corp. 2012, 2017
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#ifndef HW_S390_VIRTIO_HCALL_H
#define HW_S390_VIRTIO_HCALL_H
#include "standard-headers/asm-s390/virtio-ccw.h"
#include "cpu.h"
/* The only thing that we need from the old kvm_virtio.h file */
#define KVM_S390_VIRTIO_NOTIFY 0
typedef int (*s390_virtio_fn)(const uint64_t *args);
void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn);
int s390_virtio_hypercall(CPUS390XState *env);
#endif /* HW_S390_VIRTIO_HCALL_H */

View file

@ -161,7 +161,11 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
read_info->rnsize2 = cpu_to_be32(rnsize);
}
/* we don't support standby memory, maxram_size is never exposed */
/*
* We don't support standby memory. maxram_size is used for sizing the
* memory device region, which is not exposed through SCLP but through
* diag500.
*/
rnmax = machine->ram_size >> sclp->increment_size;
if (rnmax < 0x10000) {
read_info->rnmax = cpu_to_be16(rnmax);
@ -376,10 +380,7 @@ void sclp_service_interrupt(uint32_t sccb)
/* qemu object creation and initialization functions */
static void sclp_realize(DeviceState *dev, Error **errp)
{
MachineState *machine = MACHINE(qdev_get_machine());
SCLPDevice *sclp = SCLP(dev);
uint64_t hw_limit;
int ret;
/*
* qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long
@ -389,14 +390,6 @@ static void sclp_realize(DeviceState *dev, Error **errp)
if (!sysbus_realize(SYS_BUS_DEVICE(sclp->event_facility), errp)) {
return;
}
ret = s390_set_memory_limit(machine->maxram_size, &hw_limit);
if (ret == -E2BIG) {
error_setg(errp, "host supports a maximum of %" PRIu64 " GB",
hw_limit / GiB);
} else if (ret) {
error_setg(errp, "setting the guest size failed");
}
}
static void sclp_memory_init(SCLPDevice *sclp)

View file

@ -0,0 +1,24 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/s390x/virtio-ccw-md.h"
void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}
void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}
void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}
void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}

153
hw/s390x/virtio-ccw-md.c Normal file
View file

@ -0,0 +1,153 @@
/*
* Virtio CCW support for abstract virtio based memory device
*
* Copyright (C) 2024 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/s390x/virtio-ccw-md.h"
#include "hw/mem/memory-device.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
MemoryDeviceState *md = MEMORY_DEVICE(vmd);
Error *local_err = NULL;
if (!bus_handler && dev->hotplugged) {
/*
* Without a bus hotplug handler, we cannot control the plug/unplug
* order. We should never reach this point when hotplugging, but
* better add a safety net.
*/
error_setg(errp, "hotplug of virtio based memory devices not supported"
" on this bus.");
return;
}
/*
* First, see if we can plug this memory device at all. If that
* succeeds, branch of to the actual hotplug handler.
*/
memory_device_pre_plug(md, ms, &local_err);
if (!local_err && bus_handler) {
hotplug_handler_pre_plug(bus_handler, dev, &local_err);
}
error_propagate(errp, local_err);
}
void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
MemoryDeviceState *md = MEMORY_DEVICE(vmd);
Error *local_err = NULL;
/*
* Plug the memory device first and then branch off to the actual
* hotplug handler. If that one fails, we can easily undo the memory
* device bits.
*/
memory_device_plug(md, ms);
if (bus_handler) {
hotplug_handler_plug(bus_handler, dev, &local_err);
if (local_err) {
memory_device_unplug(md, ms);
}
}
error_propagate(errp, local_err);
}
void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
Error **errp)
{
VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_GET_CLASS(vmd);
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
HotplugHandlerClass *hdc;
Error *local_err = NULL;
if (!vmdc->unplug_request_check) {
error_setg(errp,
"this virtio based memory devices cannot be unplugged");
return;
}
if (!bus_handler) {
error_setg(errp, "hotunplug of virtio based memory devices not"
"supported on this bus");
return;
}
vmdc->unplug_request_check(vmd, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
/*
* Forward the async request or turn it into a sync request (handling it
* like qdev_unplug()).
*/
hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler);
if (hdc->unplug_request) {
hotplug_handler_unplug_request(bus_handler, dev, &local_err);
} else {
virtio_ccw_md_unplug(vmd, ms, &local_err);
if (!local_err) {
object_unparent(OBJECT(dev));
}
}
}
void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
MemoryDeviceState *md = MEMORY_DEVICE(vmd);
Error *local_err = NULL;
/* Unplug the memory device while it is still realized. */
memory_device_unplug(md, ms);
if (bus_handler) {
hotplug_handler_unplug(bus_handler, dev, &local_err);
if (local_err) {
/* Not expected to fail ... but still try to recover. */
memory_device_plug(md, ms);
error_propagate(errp, local_err);
return;
}
} else {
/* Very unexpected, but let's just try to do the right thing. */
warn_report("Unexpected unplug of virtio based memory device");
qdev_unrealize(dev);
}
}
static const TypeInfo virtio_ccw_md_info = {
.name = TYPE_VIRTIO_MD_CCW,
.parent = TYPE_VIRTIO_CCW_DEVICE,
.instance_size = sizeof(VirtIOMDCcw),
.class_size = sizeof(VirtIOMDCcwClass),
.abstract = true,
.interfaces = (InterfaceInfo[]) {
{ TYPE_MEMORY_DEVICE },
{ }
},
};
static void virtio_ccw_md_register(void)
{
type_register_static(&virtio_ccw_md_info);
}
type_init(virtio_ccw_md_register)

44
hw/s390x/virtio-ccw-md.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Virtio CCW support for abstract virtio based memory device
*
* Copyright (C) 2024 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#ifndef HW_S390X_VIRTIO_CCW_MD_H
#define HW_S390X_VIRTIO_CCW_MD_H
#include "virtio-ccw.h"
#include "qom/object.h"
/*
* virtio-md-ccw: This extends VirtioCcwDevice.
*/
#define TYPE_VIRTIO_MD_CCW "virtio-md-ccw"
OBJECT_DECLARE_TYPE(VirtIOMDCcw, VirtIOMDCcwClass, VIRTIO_MD_CCW)
struct VirtIOMDCcwClass {
/* private */
VirtIOCCWDeviceClass parent;
/* public */
void (*unplug_request_check)(VirtIOMDCcw *vmd, Error **errp);
};
struct VirtIOMDCcw {
VirtioCcwDevice parent_obj;
};
void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp);
void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp);
void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
Error **errp);
void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp);
#endif /* HW_S390X_VIRTIO_CCW_MD_H */

225
hw/s390x/virtio-ccw-mem.c Normal file
View file

@ -0,0 +1,225 @@
/*
* virtio-mem CCW implementation
*
* Copyright (C) 2024 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "virtio-ccw-mem.h"
#include "hw/mem/memory-device.h"
#include "qapi/qapi-events-machine.h"
#include "qapi/qapi-events-misc.h"
static void virtio_ccw_mem_realize(VirtioCcwDevice *ccw_dev, Error **errp)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(ccw_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
qdev_realize(vdev, BUS(&ccw_dev->bus), errp);
}
static void virtio_ccw_mem_set_addr(MemoryDeviceState *md, uint64_t addr,
Error **errp)
{
object_property_set_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP, addr, errp);
}
static uint64_t virtio_ccw_mem_get_addr(const MemoryDeviceState *md)
{
return object_property_get_uint(OBJECT(md), VIRTIO_MEM_ADDR_PROP,
&error_abort);
}
static MemoryRegion *virtio_ccw_mem_get_memory_region(MemoryDeviceState *md,
Error **errp)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md);
VirtIOMEM *vmem = &dev->vdev;
VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem);
return vmc->get_memory_region(vmem, errp);
}
static void virtio_ccw_mem_decide_memslots(MemoryDeviceState *md,
unsigned int limit)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md);
VirtIOMEM *vmem = VIRTIO_MEM(&dev->vdev);
VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem);
vmc->decide_memslots(vmem, limit);
}
static unsigned int virtio_ccw_mem_get_memslots(MemoryDeviceState *md)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md);
VirtIOMEM *vmem = VIRTIO_MEM(&dev->vdev);
VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem);
return vmc->get_memslots(vmem);
}
static uint64_t virtio_ccw_mem_get_plugged_size(const MemoryDeviceState *md,
Error **errp)
{
return object_property_get_uint(OBJECT(md), VIRTIO_MEM_SIZE_PROP,
errp);
}
static void virtio_ccw_mem_fill_device_info(const MemoryDeviceState *md,
MemoryDeviceInfo *info)
{
VirtioMEMDeviceInfo *vi = g_new0(VirtioMEMDeviceInfo, 1);
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(md);
VirtIOMEM *vmem = &dev->vdev;
VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem);
DeviceState *vdev = DEVICE(md);
if (vdev->id) {
vi->id = g_strdup(vdev->id);
}
/* let the real device handle everything else */
vpc->fill_device_info(vmem, vi);
info->u.virtio_mem.data = vi;
info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM;
}
static uint64_t virtio_ccw_mem_get_min_alignment(const MemoryDeviceState *md)
{
return object_property_get_uint(OBJECT(md), VIRTIO_MEM_BLOCK_SIZE_PROP,
&error_abort);
}
static void virtio_ccw_mem_size_change_notify(Notifier *notifier, void *data)
{
VirtIOMEMCcw *dev = container_of(notifier, VirtIOMEMCcw,
size_change_notifier);
DeviceState *vdev = DEVICE(dev);
char *qom_path = object_get_canonical_path(OBJECT(dev));
const uint64_t * const size_p = data;
qapi_event_send_memory_device_size_change(vdev->id, *size_p, qom_path);
g_free(qom_path);
}
static void virtio_ccw_mem_unplug_request_check(VirtIOMDCcw *vmd, Error **errp)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(vmd);
VirtIOMEM *vmem = &dev->vdev;
VirtIOMEMClass *vpc = VIRTIO_MEM_GET_CLASS(vmem);
vpc->unplug_request_check(vmem, errp);
}
static void virtio_ccw_mem_get_requested_size(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj);
object_property_get(OBJECT(&dev->vdev), name, v, errp);
}
static void virtio_ccw_mem_set_requested_size(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj);
DeviceState *vdev = DEVICE(obj);
/*
* If we passed virtio_ccw_mem_unplug_request_check(), making sure that
* the requested size is 0, don't allow modifying the requested size
* anymore, otherwise the VM might end up hotplugging memory before
* handling the unplug request.
*/
if (vdev->pending_deleted_event) {
error_setg(errp, "'%s' cannot be changed if the device is in the"
" process of unplug", name);
return;
}
object_property_set(OBJECT(&dev->vdev), name, v, errp);
}
static const Property virtio_ccw_mem_properties[] = {
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
VIRTIO_CCW_MAX_REV),
};
static void virtio_ccw_mem_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(klass);
VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_CLASS(klass);
k->realize = virtio_ccw_mem_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
device_class_set_props(dc, virtio_ccw_mem_properties);
mdc->get_addr = virtio_ccw_mem_get_addr;
mdc->set_addr = virtio_ccw_mem_set_addr;
mdc->get_plugged_size = virtio_ccw_mem_get_plugged_size;
mdc->get_memory_region = virtio_ccw_mem_get_memory_region;
mdc->decide_memslots = virtio_ccw_mem_decide_memslots;
mdc->get_memslots = virtio_ccw_mem_get_memslots;
mdc->fill_device_info = virtio_ccw_mem_fill_device_info;
mdc->get_min_alignment = virtio_ccw_mem_get_min_alignment;
vmdc->unplug_request_check = virtio_ccw_mem_unplug_request_check;
}
static void virtio_ccw_mem_instance_init(Object *obj)
{
VirtIOMEMCcw *dev = VIRTIO_MEM_CCW(obj);
VirtIOMEMClass *vmc;
VirtIOMEM *vmem;
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
TYPE_VIRTIO_MEM);
dev->size_change_notifier.notify = virtio_ccw_mem_size_change_notify;
vmem = &dev->vdev;
vmc = VIRTIO_MEM_GET_CLASS(vmem);
/*
* We never remove the notifier again, as we expect both devices to
* disappear at the same time.
*/
vmc->add_size_change_notifier(vmem, &dev->size_change_notifier);
object_property_add_alias(obj, VIRTIO_MEM_BLOCK_SIZE_PROP,
OBJECT(&dev->vdev), VIRTIO_MEM_BLOCK_SIZE_PROP);
object_property_add_alias(obj, VIRTIO_MEM_SIZE_PROP, OBJECT(&dev->vdev),
VIRTIO_MEM_SIZE_PROP);
object_property_add(obj, VIRTIO_MEM_REQUESTED_SIZE_PROP, "size",
virtio_ccw_mem_get_requested_size,
virtio_ccw_mem_set_requested_size, NULL, NULL);
}
static const TypeInfo virtio_ccw_mem = {
.name = TYPE_VIRTIO_MEM_CCW,
.parent = TYPE_VIRTIO_MD_CCW,
.instance_size = sizeof(VirtIOMEMCcw),
.instance_init = virtio_ccw_mem_instance_init,
.class_init = virtio_ccw_mem_class_init,
};
static void virtio_ccw_mem_register_types(void)
{
type_register_static(&virtio_ccw_mem);
}
type_init(virtio_ccw_mem_register_types)

34
hw/s390x/virtio-ccw-mem.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Virtio MEM CCW device
*
* Copyright (C) 2024 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#ifndef HW_S390X_VIRTIO_CCW_MEM_H
#define HW_S390X_VIRTIO_CCW_MEM_H
#include "virtio-ccw-md.h"
#include "hw/virtio/virtio-mem.h"
#include "qom/object.h"
typedef struct VirtIOMEMCcw VirtIOMEMCcw;
/*
* virtio-mem-ccw: This extends VirtIOMDCcw
*/
#define TYPE_VIRTIO_MEM_CCW "virtio-mem-ccw"
DECLARE_INSTANCE_CHECKER(VirtIOMEMCcw, VIRTIO_MEM_CCW, TYPE_VIRTIO_MEM_CCW)
struct VirtIOMEMCcw {
VirtIOMDCcw parent_obj;
VirtIOMEM vdev;
Notifier size_change_notifier;
};
#endif /* HW_S390X_VIRTIO_CCW_MEM_H */

View file

@ -29,6 +29,7 @@ config VIRTIO_MMIO
config VIRTIO_CCW
bool
select VIRTIO
select VIRTIO_MD_SUPPORTED
config VIRTIO_BALLOON
bool

View file

@ -61,6 +61,8 @@ static uint32_t virtio_mem_default_thp_size(void)
} else if (qemu_real_host_page_size() == 64 * KiB) {
default_thp_size = 512 * MiB;
}
#elif defined(__s390x__)
default_thp_size = 1 * MiB;
#endif
return default_thp_size;
@ -168,7 +170,7 @@ static bool virtio_mem_has_shared_zeropage(RAMBlock *rb)
* necessary (as the section size can change). But it's more likely that the
* section size will rather get smaller and not bigger over time.
*/
#if defined(TARGET_X86_64) || defined(TARGET_I386)
#if defined(TARGET_X86_64) || defined(TARGET_I386) || defined(TARGET_S390X)
#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
#elif defined(TARGET_ARM)
#define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB))
@ -956,6 +958,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
VirtIOMEM *vmem = VIRTIO_MEM(dev);
uint64_t page_size;
RAMBlock *rb;
Object *obj;
int ret;
if (!vmem->memdev) {
@ -1121,7 +1124,28 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
vmstate_register_any(VMSTATE_IF(vmem),
&vmstate_virtio_mem_device_early, vmem);
}
qemu_register_resettable(OBJECT(vmem));
/*
* We only want to unplug all memory to start with a clean slate when
* it is safe for the guest -- during system resets that call
* qemu_devices_reset().
*
* We'll filter out selected qemu_devices_reset() calls used for other
* purposes, like resetting all devices during wakeup from suspend on
* x86 based on the reset type passed to qemu_devices_reset().
*
* Unplugging all memory during simple device resets can result in the VM
* unexpectedly losing RAM, corrupting VM state.
*
* Simple device resets (or resets triggered by getting a parent device
* reset) must not change the state of plugged memory blocks. Therefore,
* we need a dedicated reset object that only gets called during
* qemu_devices_reset().
*/
obj = object_new(TYPE_VIRTIO_MEM_SYSTEM_RESET);
vmem->system_reset = VIRTIO_MEM_SYSTEM_RESET(obj);
vmem->system_reset->vmem = vmem;
qemu_register_resettable(obj);
/*
* Set ourselves as RamDiscardManager before the plug handler maps the
@ -1141,7 +1165,10 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
* found via an address space anymore. Unset ourselves.
*/
memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
qemu_unregister_resettable(OBJECT(vmem));
qemu_unregister_resettable(OBJECT(vmem->system_reset));
object_unref(OBJECT(vmem->system_reset));
if (vmem->early_migration) {
vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early,
vmem);
@ -1840,38 +1867,12 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp)
}
}
static ResettableState *virtio_mem_get_reset_state(Object *obj)
{
VirtIOMEM *vmem = VIRTIO_MEM(obj);
return &vmem->reset_state;
}
static void virtio_mem_system_reset_hold(Object *obj, ResetType type)
{
VirtIOMEM *vmem = VIRTIO_MEM(obj);
/*
* When waking up from standby/suspend-to-ram, do not unplug any memory.
*/
if (type == RESET_TYPE_WAKEUP) {
return;
}
/*
* During usual resets, we will unplug all memory and shrink the usable
* region size. This is, however, not possible in all scenarios. Then,
* the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL).
*/
virtio_mem_unplug_all(vmem);
}
static void virtio_mem_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass);
RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
device_class_set_props(dc, virtio_mem_properties);
dc->vmsd = &vmstate_virtio_mem;
@ -1898,9 +1899,6 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data)
rdmc->replay_discarded = virtio_mem_rdm_replay_discarded;
rdmc->register_listener = virtio_mem_rdm_register_listener;
rdmc->unregister_listener = virtio_mem_rdm_unregister_listener;
rc->get_state = virtio_mem_get_reset_state;
rc->phases.hold = virtio_mem_system_reset_hold;
}
static const TypeInfo virtio_mem_info = {
@ -1923,3 +1921,48 @@ static void virtio_register_types(void)
}
type_init(virtio_register_types)
OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(VirtioMemSystemReset, virtio_mem_system_reset, VIRTIO_MEM_SYSTEM_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { })
static void virtio_mem_system_reset_init(Object *obj)
{
}
static void virtio_mem_system_reset_finalize(Object *obj)
{
}
static ResettableState *virtio_mem_system_reset_get_state(Object *obj)
{
VirtioMemSystemReset *vmem_reset = VIRTIO_MEM_SYSTEM_RESET(obj);
return &vmem_reset->reset_state;
}
static void virtio_mem_system_reset_hold(Object *obj, ResetType type)
{
VirtioMemSystemReset *vmem_reset = VIRTIO_MEM_SYSTEM_RESET(obj);
VirtIOMEM *vmem = vmem_reset->vmem;
/*
* When waking up from standby/suspend-to-ram, do not unplug any memory.
*/
if (type == RESET_TYPE_WAKEUP) {
return;
}
/*
* During usual resets, we will unplug all memory and shrink the usable
* region size. This is, however, not possible in all scenarios. Then,
* the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL).
*/
virtio_mem_unplug_all(vmem);
}
static void virtio_mem_system_reset_class_init(ObjectClass *klass, void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->get_state = virtio_mem_system_reset_get_state;
rc->phases.hold = virtio_mem_system_reset_hold;
}

View file

@ -29,10 +29,19 @@ struct S390CcwMachineState {
bool dea_key_wrap;
bool pv;
uint8_t loadparm[8];
uint64_t memory_limit;
uint64_t max_pagesize;
SCLPDevice *sclp;
};
static inline uint64_t s390_get_memory_limit(S390CcwMachineState *s390ms)
{
/* We expect to be called only after the limit was set. */
assert(s390ms->memory_limit);
return s390ms->memory_limit;
}
#define S390_PTF_REASON_NONE (0x00 << 8)
#define S390_PTF_REASON_DONE (0x01 << 8)
#define S390_PTF_REASON_BUSY (0x02 << 8)

View file

@ -25,6 +25,10 @@
OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass,
VIRTIO_MEM)
#define TYPE_VIRTIO_MEM_SYSTEM_RESET "virtio-mem-system-reset"
OBJECT_DECLARE_SIMPLE_TYPE(VirtioMemSystemReset, VIRTIO_MEM_SYSTEM_RESET)
#define VIRTIO_MEM_MEMDEV_PROP "memdev"
#define VIRTIO_MEM_NODE_PROP "node"
#define VIRTIO_MEM_SIZE_PROP "size"
@ -117,8 +121,15 @@ struct VirtIOMEM {
/* listeners to notify on plug/unplug activity. */
QLIST_HEAD(, RamDiscardListener) rdl_list;
/* State of the resettable container */
/* Catch system resets -> qemu_devices_reset() only. */
VirtioMemSystemReset *system_reset;
};
struct VirtioMemSystemReset {
Object parent;
ResettableState reset_state;
VirtIOMEM *vmem;
};
struct VirtIOMEMClass {

View file

@ -255,21 +255,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
return s390_count_running_cpus();
}
int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit)
{
if (kvm_enabled()) {
return kvm_s390_set_mem_limit(new_limit, hw_limit);
}
return 0;
}
void s390_set_max_pagesize(uint64_t pagesize, Error **errp)
{
if (kvm_enabled()) {
kvm_s390_set_max_pagesize(pagesize, errp);
}
}
void s390_cmma_reset(void)
{
if (kvm_enabled()) {

View file

@ -881,8 +881,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg)
/* cpu.c */
void s390_crypto_reset(void);
int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit);
void s390_set_max_pagesize(uint64_t pagesize, Error **errp);
void s390_cmma_reset(void);
void s390_enable_css_support(S390CPU *cpu);
void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg);

View file

@ -49,8 +49,9 @@
#include "hw/s390x/ebcdic.h"
#include "exec/memattrs.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/s390x/s390-virtio-hcall.h"
#include "hw/s390x/s390-hypercall.h"
#include "target/s390x/kvm/pv.h"
#include CONFIG_DEVICES
#define kvm_vm_check_mem_attr(s, attr) \
kvm_vm_check_attr(s, KVM_S390_VM_MEM_CTRL, attr)
@ -1491,20 +1492,6 @@ static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl)
return r;
}
static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
{
CPUS390XState *env = &cpu->env;
int ret;
ret = s390_virtio_hypercall(env);
if (ret == -EINVAL) {
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
return 0;
}
return ret;
}
static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
{
uint64_t r1, r3;
@ -1600,9 +1587,11 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
case DIAG_SET_CONTROL_PROGRAM_CODES:
handle_diag_318(cpu, run);
break;
#ifdef CONFIG_S390_CCW_VIRTIO
case DIAG_KVM_HYPERCALL:
r = handle_hypercall(cpu, run);
handle_diag_500(cpu, RA_IGNORED);
break;
#endif /* CONFIG_S390_CCW_VIRTIO */
case DIAG_KVM_BREAKPOINT:
r = handle_sw_breakpoint(cpu, run);
break;

View file

@ -133,7 +133,7 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms)
* If the feature is not present or if the VM is not larger than 2 GiB,
* KVM_PV_ASYNC_CLEANUP_PREPARE fill fail; no point in attempting it.
*/
if ((MACHINE(ms)->maxram_size <= 2 * GiB) ||
if (s390_get_memory_limit(ms) <= 2 * GiB ||
!kvm_check_extension(kvm_state, KVM_CAP_S390_PROTECTED_ASYNC_DISABLE)) {
return false;
}

View file

@ -36,13 +36,14 @@
#include "system/cpus.h"
#include "system/system.h"
#include "hw/s390x/ebcdic.h"
#include "hw/s390x/s390-virtio-hcall.h"
#include "hw/s390x/s390-hypercall.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/ioinst.h"
#include "hw/s390x/s390-pci-inst.h"
#include "hw/boards.h"
#include "hw/s390x/tod.h"
#include CONFIG_DEVICES
#endif
/* #define DEBUG_HELPER */
@ -116,12 +117,15 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
uint64_t r;
switch (num) {
#ifdef CONFIG_S390_CCW_VIRTIO
case 0x500:
/* KVM hypercall */
/* QEMU/KVM hypercall */
bql_lock();
r = s390_virtio_hypercall(env);
handle_diag_500(env_archcpu(env), GETPC());
bql_unlock();
r = 0;
break;
#endif /* CONFIG_S390_CCW_VIRTIO */
case 0x44:
/* yield */
r = 0;