s390x updates:

- rework interrupt handling for tcg, smp is now considered non-experimental
 - some general improvements in the flic
 - improvements in the pci code, and wiring it up in tcg
 - add PTFF subfunctions for multiple-epoch to the cpu model
 - maintainership updates
 - various other fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEEw9DWbcNiT/aowBjO3s9rk8bwL68FAlp9ZCISHGNvaHVja0By
 ZWRoYXQuY29tAAoJEN7Pa5PG8C+vuhwQAKS4IepY5sEA0eUScpA+xDBLiXYkwTIP
 Nq8Jm553voTvKgadBmYp8zC4U0jxfbsKd6NFxFisRiFtxsrT91L/akQTolTRU2F4
 i44K6LCcfqWIclSWQrZVGoxYpg7h2tZYBsq9stZOT+8eeoNeseZLMQGzjif9DbmS
 jTwGLcWUAmzvemk/rygzRHHhpNS3Ed5OHCeMunpG+RuKlRBkleVU9yft2QbztNtG
 LOBlLtlKX+6r8A0Xv3yKh/q9q+lxEOebONt8A1MhpcLlqMP3XHesS+T7v/rh7Bld
 CXCYWXy6QHm4xDUV+9rflawaNisYnZDJfovKk7k6UPHi+EXhivPBjT0teS/CZ0D+
 O+iDiXwlZc58/5A7fsUoXFzj/ZlLu5fJXvmGz0iYyDh+UthjqzrDqdcPp5h788gH
 8TsjapTc9X6g+D8lgPVh93/ejnCcVqaeSrjEbmjzksnBprTMEb94ew8nnjcWlppR
 zii1fQfpstoaBUWDPMpHImnL46oSEyKJ1XF/NA4X/gRIQsxfuUbtQAfwv+rgamQb
 T3nVyvdu0vlO+SSrkCJ/siZ+Nyf4A+ksNfIBSfUPgzVF6J0nzJ/uD3jOkxm/M5pa
 ScFrEop41jYcrZsOJqrb2HifzJpETssd4+s9lD44vUimiORpEPoeG7zF5sqbrlvc
 fO9X3ufY8n2i
 =/u33
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20180209' into staging

s390x updates:
- rework interrupt handling for tcg, smp is now considered non-experimental
- some general improvements in the flic
- improvements in the pci code, and wiring it up in tcg
- add PTFF subfunctions for multiple-epoch to the cpu model
- maintainership updates
- various other fixes and improvements

# gpg: Signature made Fri 09 Feb 2018 09:04:34 GMT
# gpg:                using RSA key DECF6B93C6F02FAF
# gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>"
# gpg:                 aka "Cornelia Huck <huckc@linux.vnet.ibm.com>"
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>"
# gpg:                 aka "Cornelia Huck <cohuck@kernel.org>"
# gpg:                 aka "Cornelia Huck <cohuck@redhat.com>"
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20180209: (29 commits)
  MAINTAINERS: add David as additional tcg/s390 maintainer
  MAINTAINERS: reorganize s390-ccw bios maintainership
  MAINTAINERS: add myself as overall s390x maintainer
  s390x/pci: use the right pal and pba in reg_ioat()
  s390x/pci: fixup global refresh
  s390x/pci: fixup the code walking IOMMU tables
  s390x/cpumodel: model PTFF subfunctions for Multiple-epoch facility
  s390x/cpumodel: allow zpci features in qemu model
  s390x/tcg: wire up pci instructions
  s390x/sclp: fix event mask handling
  s390x/flic: cache the common flic class in a central function
  s390x/kvm: cache the kvm flic in a central function
  s390x/tcg: cache the qemu flic in a central function
  configure: s390x supports mttcg now
  s390x/tcg: remove SMP warning
  s390x/tcg: STSI overhaul
  s390x: fix size + content of STSI blocks
  s390x/flic: optimize CPU wakeup for TCG
  s390x/flic: implement qemu_s390_clear_io_flic()
  s390x/tcg: implement TEST PENDING INTERRUPTION
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-02-09 11:46:32 +00:00
commit fdcbebe451
28 changed files with 1241 additions and 549 deletions

View File

@ -76,6 +76,29 @@ K: ^Subject:.*(?i)trivial
T: git git://git.corpit.ru/qemu.git trivial-patches T: git git://git.corpit.ru/qemu.git trivial-patches
T: git git://github.com/vivier/qemu.git trivial-patches T: git git://github.com/vivier/qemu.git trivial-patches
Architecture support
--------------------
S390
M: Cornelia Huck <cohuck@redhat.com>
S: Supported
F: default-configs/s390x-softmmu.mak
F: gdb-xml/s390*.xml
F: hw/char/sclp*.[hc]
F: hw/char/terminal3270.c
F: hw/intc/s390_flic.c
F: hw/intc/s390_flic_kvm.c
F: hw/s390x/
F: hw/vfio/ccw.c
F: hw/watchdog/wdt_diag288.c
F: include/hw/s390x/
F: include/hw/watchdog/wdt_diag288.h
F: pc-bios/s390-ccw/
F: pc-bios/s390-ccw.img
F: target/s390x/
K: ^Subject:.*(?i)s390x?
T: git git://github.com/cohuck/qemu.git s390-next
L: qemu-s390x@nongnu.org
Guest CPU cores (TCG): Guest CPU cores (TCG):
---------------------- ----------------------
Overall Overall
@ -213,6 +236,7 @@ F: disas/ppc.c
S390 S390
M: Richard Henderson <rth@twiddle.net> M: Richard Henderson <rth@twiddle.net>
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
M: David Hildenbrand <david@redhat.com>
S: Maintained S: Maintained
F: target/s390x/ F: target/s390x/
F: hw/s390x/ F: hw/s390x/
@ -832,15 +856,22 @@ F: hw/char/sclp*.[hc]
F: hw/char/terminal3270.c F: hw/char/terminal3270.c
F: hw/s390x/ F: hw/s390x/
F: include/hw/s390x/ F: include/hw/s390x/
F: pc-bios/s390-ccw/
F: hw/watchdog/wdt_diag288.c F: hw/watchdog/wdt_diag288.c
F: include/hw/watchdog/wdt_diag288.h F: include/hw/watchdog/wdt_diag288.h
F: pc-bios/s390-ccw.img
F: default-configs/s390x-softmmu.mak F: default-configs/s390x-softmmu.mak
T: git git://github.com/cohuck/qemu.git s390-next T: git git://github.com/cohuck/qemu.git s390-next
T: git git://github.com/borntraeger/qemu.git s390-next T: git git://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org L: qemu-s390x@nongnu.org
S390-ccw Bios
M: Christian Borntraeger <borntraeger@de.ibm.com>
M: Thomas Huth <thuth@redhat.com>
S: Supported
F: pc-bios/s390-ccw/
F: pc-bios/s390-ccw.img
T: git git://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org
UniCore32 Machines UniCore32 Machines
------------- -------------
PKUnity-3 SoC initramfs-with-busybox PKUnity-3 SoC initramfs-with-busybox

5
configure vendored
View File

@ -1933,9 +1933,9 @@ int main(int argc, char *argv[]) {
EOF EOF
if compile_object ; then if compile_object ; then
if grep -q BiGeNdIaN $TMPO ; then if strings -a $TMPO | grep -q BiGeNdIaN ; then
bigendian="yes" bigendian="yes"
elif grep -q LiTtLeEnDiAn $TMPO ; then elif strings -a $TMPO | grep -q LiTtLeEnDiAn ; then
bigendian="no" bigendian="no"
else else
echo big/little test failed echo big/little test failed
@ -6779,6 +6779,7 @@ case "$target_name" in
echo "TARGET_ABI32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak
;; ;;
s390x) s390x)
mttcg=yes
gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml" gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml"
;; ;;
tilegx) tilegx)

View File

@ -22,16 +22,36 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-virtio-ccw.h"
S390FLICStateClass *s390_get_flic_class(S390FLICState *fs)
{
static S390FLICStateClass *class;
if (!class) {
/* we only have one flic device, so this is fine to cache */
class = S390_FLIC_COMMON_GET_CLASS(fs);
}
return class;
}
QEMUS390FLICState *s390_get_qemu_flic(S390FLICState *fs)
{
static QEMUS390FLICState *flic;
if (!flic) {
/* we only have one flic device, so this is fine to cache */
flic = QEMU_S390_FLIC(fs);
}
return flic;
}
S390FLICState *s390_get_flic(void) S390FLICState *s390_get_flic(void)
{ {
static S390FLICState *fs; static S390FLICState *fs;
if (!fs) { if (!fs) {
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); fs = S390_FLIC_COMMON(object_resolve_path_type("",
if (!fs) { TYPE_S390_FLIC_COMMON,
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL));
NULL));
}
} }
return fs; return fs;
} }
@ -40,8 +60,11 @@ void s390_flic_init(void)
{ {
DeviceState *dev; DeviceState *dev;
dev = s390_flic_kvm_create(); if (kvm_enabled()) {
if (!dev) { dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
OBJECT(dev), NULL);
} else {
dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC); dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC);
object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC, object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC,
OBJECT(dev), NULL); OBJECT(dev), NULL);
@ -78,14 +101,41 @@ static void qemu_s390_release_adapter_routes(S390FLICState *fs,
static int qemu_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, static int qemu_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id,
uint16_t subchannel_nr) uint16_t subchannel_nr)
{ {
/* Fixme TCG */ QEMUS390FLICState *flic = s390_get_qemu_flic(fs);
return -ENOSYS; QEMUS390FlicIO *cur, *next;
uint8_t isc;
g_assert(qemu_mutex_iothread_locked());
if (!(flic->pending & FLIC_PENDING_IO)) {
return 0;
}
/* check all iscs */
for (isc = 0; isc < 8; isc++) {
if (QLIST_EMPTY(&flic->io[isc])) {
continue;
}
/* search and delete any matching one */
QLIST_FOREACH_SAFE(cur, &flic->io[isc], next, next) {
if (cur->id == subchannel_id && cur->nr == subchannel_nr) {
QLIST_REMOVE(cur, next);
g_free(cur);
}
}
/* update our indicator bit */
if (QLIST_EMPTY(&flic->io[isc])) {
flic->pending &= ~ISC_TO_PENDING_IO(isc);
}
}
return 0;
} }
static int qemu_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc, static int qemu_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc,
uint16_t mode) uint16_t mode)
{ {
QEMUS390FLICState *flic = QEMU_S390_FLIC(fs); QEMUS390FLICState *flic = s390_get_qemu_flic(fs);
switch (mode) { switch (mode) {
case SIC_IRQ_MODE_ALL: case SIC_IRQ_MODE_ALL:
@ -106,7 +156,8 @@ static int qemu_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc,
static int qemu_s390_inject_airq(S390FLICState *fs, uint8_t type, static int qemu_s390_inject_airq(S390FLICState *fs, uint8_t type,
uint8_t isc, uint8_t flags) uint8_t isc, uint8_t flags)
{ {
QEMUS390FLICState *flic = QEMU_S390_FLIC(fs); QEMUS390FLICState *flic = s390_get_qemu_flic(fs);
S390FLICStateClass *fsc = s390_get_flic_class(fs);
bool flag = flags & S390_ADAPTER_SUPPRESSIBLE; bool flag = flags & S390_ADAPTER_SUPPRESSIBLE;
uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI;
@ -115,7 +166,7 @@ static int qemu_s390_inject_airq(S390FLICState *fs, uint8_t type,
return 0; return 0;
} }
s390_io_interrupt(0, 0, 0, io_int_word); fsc->inject_io(fs, 0, 0, 0, io_int_word);
if (flag && (flic->simm & AIS_MODE_MASK(isc))) { if (flag && (flic->simm & AIS_MODE_MASK(isc))) {
flic->nimm |= AIS_MODE_MASK(isc); flic->nimm |= AIS_MODE_MASK(isc);
@ -126,12 +177,180 @@ static int qemu_s390_inject_airq(S390FLICState *fs, uint8_t type,
return 0; return 0;
} }
static void qemu_s390_flic_notify(uint32_t type)
{
CPUState *cs;
/*
* We have to make all CPUs see CPU_INTERRUPT_HARD, so they might
* consider it. We will kick all running CPUs and only relevant
* sleeping ones.
*/
CPU_FOREACH(cs) {
S390CPU *cpu = S390_CPU(cs);
cs->interrupt_request |= CPU_INTERRUPT_HARD;
/* ignore CPUs that are not sleeping */
if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING &&
s390_cpu_get_state(cpu) != CPU_STATE_LOAD) {
continue;
}
/* we always kick running CPUs for now, this is tricky */
if (cs->halted) {
/* don't check for subclasses, CPUs double check when waking up */
if (type & FLIC_PENDING_SERVICE) {
if (!(cpu->env.psw.mask & PSW_MASK_EXT)) {
continue;
}
} else if (type & FLIC_PENDING_IO) {
if (!(cpu->env.psw.mask & PSW_MASK_IO)) {
continue;
}
} else if (type & FLIC_PENDING_MCHK_CR) {
if (!(cpu->env.psw.mask & PSW_MASK_MCHECK)) {
continue;
}
}
}
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
}
}
uint32_t qemu_s390_flic_dequeue_service(QEMUS390FLICState *flic)
{
uint32_t tmp;
g_assert(qemu_mutex_iothread_locked());
g_assert(flic->pending & FLIC_PENDING_SERVICE);
tmp = flic->service_param;
flic->service_param = 0;
flic->pending &= ~FLIC_PENDING_SERVICE;
return tmp;
}
/* caller has to free the returned object */
QEMUS390FlicIO *qemu_s390_flic_dequeue_io(QEMUS390FLICState *flic, uint64_t cr6)
{
QEMUS390FlicIO *io;
uint8_t isc;
g_assert(qemu_mutex_iothread_locked());
if (!(flic->pending & CR6_TO_PENDING_IO(cr6))) {
return NULL;
}
for (isc = 0; isc < 8; isc++) {
if (QLIST_EMPTY(&flic->io[isc]) || !(cr6 & ISC_TO_ISC_BITS(isc))) {
continue;
}
io = QLIST_FIRST(&flic->io[isc]);
QLIST_REMOVE(io, next);
/* update our indicator bit */
if (QLIST_EMPTY(&flic->io[isc])) {
flic->pending &= ~ISC_TO_PENDING_IO(isc);
}
return io;
}
return NULL;
}
void qemu_s390_flic_dequeue_crw_mchk(QEMUS390FLICState *flic)
{
g_assert(qemu_mutex_iothread_locked());
g_assert(flic->pending & FLIC_PENDING_MCHK_CR);
flic->pending &= ~FLIC_PENDING_MCHK_CR;
}
static void qemu_s390_inject_service(S390FLICState *fs, uint32_t parm)
{
QEMUS390FLICState *flic = s390_get_qemu_flic(fs);
g_assert(qemu_mutex_iothread_locked());
/* multiplexing is good enough for sclp - kvm does it internally as well */
flic->service_param |= parm;
flic->pending |= FLIC_PENDING_SERVICE;
qemu_s390_flic_notify(FLIC_PENDING_SERVICE);
}
static void qemu_s390_inject_io(S390FLICState *fs, uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word)
{
const uint8_t isc = IO_INT_WORD_ISC(io_int_word);
QEMUS390FLICState *flic = s390_get_qemu_flic(fs);
QEMUS390FlicIO *io;
g_assert(qemu_mutex_iothread_locked());
io = g_new0(QEMUS390FlicIO, 1);
io->id = subchannel_id;
io->nr = subchannel_nr;
io->parm = io_int_parm;
io->word = io_int_word;
QLIST_INSERT_HEAD(&flic->io[isc], io, next);
flic->pending |= ISC_TO_PENDING_IO(isc);
qemu_s390_flic_notify(ISC_TO_PENDING_IO(isc));
}
static void qemu_s390_inject_crw_mchk(S390FLICState *fs)
{
QEMUS390FLICState *flic = s390_get_qemu_flic(fs);
g_assert(qemu_mutex_iothread_locked());
flic->pending |= FLIC_PENDING_MCHK_CR;
qemu_s390_flic_notify(FLIC_PENDING_MCHK_CR);
}
bool qemu_s390_flic_has_service(QEMUS390FLICState *flic)
{
/* called without lock via cc->has_work, will be validated under lock */
return !!(flic->pending & FLIC_PENDING_SERVICE);
}
bool qemu_s390_flic_has_io(QEMUS390FLICState *flic, uint64_t cr6)
{
/* called without lock via cc->has_work, will be validated under lock */
return !!(flic->pending & CR6_TO_PENDING_IO(cr6));
}
bool qemu_s390_flic_has_crw_mchk(QEMUS390FLICState *flic)
{
/* called without lock via cc->has_work, will be validated under lock */
return !!(flic->pending & FLIC_PENDING_MCHK_CR);
}
bool qemu_s390_flic_has_any(QEMUS390FLICState *flic)
{
g_assert(qemu_mutex_iothread_locked());
return !!flic->pending;
}
static void qemu_s390_flic_reset(DeviceState *dev) static void qemu_s390_flic_reset(DeviceState *dev)
{ {
QEMUS390FLICState *flic = QEMU_S390_FLIC(dev); QEMUS390FLICState *flic = QEMU_S390_FLIC(dev);
QEMUS390FlicIO *cur, *next;
int isc;
g_assert(qemu_mutex_iothread_locked());
flic->simm = 0; flic->simm = 0;
flic->nimm = 0; flic->nimm = 0;
flic->pending = 0;
/* remove all pending io interrupts */
for (isc = 0; isc < 8; isc++) {
QLIST_FOREACH_SAFE(cur, &flic->io[isc], next, next) {
QLIST_REMOVE(cur, next);
g_free(cur);
}
}
} }
bool ais_needed(void *opaque) bool ais_needed(void *opaque)
@ -153,6 +372,16 @@ static const VMStateDescription qemu_s390_flic_vmstate = {
} }
}; };
static void qemu_s390_flic_instance_init(Object *obj)
{
QEMUS390FLICState *flic = QEMU_S390_FLIC(obj);
int isc;
for (isc = 0; isc < 8; isc++) {
QLIST_INIT(&flic->io[isc]);
}
}
static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) static void qemu_s390_flic_class_init(ObjectClass *oc, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc);
@ -167,6 +396,9 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data)
fsc->clear_io_irq = qemu_s390_clear_io_flic; fsc->clear_io_irq = qemu_s390_clear_io_flic;
fsc->modify_ais_mode = qemu_s390_modify_ais_mode; fsc->modify_ais_mode = qemu_s390_modify_ais_mode;
fsc->inject_airq = qemu_s390_inject_airq; fsc->inject_airq = qemu_s390_inject_airq;
fsc->inject_service = qemu_s390_inject_service;
fsc->inject_io = qemu_s390_inject_io;
fsc->inject_crw_mchk = qemu_s390_inject_crw_mchk;
} }
static Property s390_flic_common_properties[] = { static Property s390_flic_common_properties[] = {
@ -201,6 +433,7 @@ static const TypeInfo qemu_s390_flic_info = {
.name = TYPE_QEMU_S390_FLIC, .name = TYPE_QEMU_S390_FLIC,
.parent = TYPE_S390_FLIC_COMMON, .parent = TYPE_S390_FLIC_COMMON,
.instance_size = sizeof(QEMUS390FLICState), .instance_size = sizeof(QEMUS390FLICState),
.instance_init = qemu_s390_flic_instance_init,
.class_init = qemu_s390_flic_class_init, .class_init = qemu_s390_flic_class_init,
}; };

View File

@ -35,16 +35,15 @@ typedef struct KVMS390FLICState {
bool clear_io_supported; bool clear_io_supported;
} KVMS390FLICState; } KVMS390FLICState;
DeviceState *s390_flic_kvm_create(void) static KVMS390FLICState *s390_get_kvm_flic(S390FLICState *fs)
{ {
DeviceState *dev = NULL; static KVMS390FLICState *flic;
if (kvm_enabled()) { if (!flic) {
dev = qdev_create(NULL, TYPE_KVM_S390_FLIC); /* we only have one flic device, so this is fine to cache */
object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC, flic = KVM_S390_FLIC(fs);
OBJECT(dev), NULL);
} }
return dev; return flic;
} }
/** /**
@ -123,20 +122,70 @@ static int flic_enqueue_irqs(void *buf, uint64_t len,
return rc ? -errno : 0; return rc ? -errno : 0;
} }
int kvm_s390_inject_flic(struct kvm_s390_irq *irq) static void kvm_s390_inject_flic(S390FLICState *fs, struct kvm_s390_irq *irq)
{ {
static KVMS390FLICState *flic; static bool use_flic = true;
int r;
if (unlikely(!flic)) { if (use_flic) {
flic = KVM_S390_FLIC(s390_get_flic()); r = flic_enqueue_irqs(irq, sizeof(*irq), s390_get_kvm_flic(fs));
if (r == -ENOSYS) {
use_flic = false;
}
if (!r) {
return;
}
} }
return flic_enqueue_irqs(irq, sizeof(*irq), flic); /* fallback to legacy KVM IOCTL in case FLIC fails */
kvm_s390_floating_interrupt_legacy(irq);
}
static void kvm_s390_inject_service(S390FLICState *fs, uint32_t parm)
{
struct kvm_s390_irq irq = {
.type = KVM_S390_INT_SERVICE,
.u.ext.ext_params = parm,
};
kvm_s390_inject_flic(fs, &irq);
}
static void kvm_s390_inject_io(S390FLICState *fs, uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word)
{
struct kvm_s390_irq irq = {
.u.io.subchannel_id = subchannel_id,
.u.io.subchannel_nr = subchannel_nr,
.u.io.io_int_parm = io_int_parm,
.u.io.io_int_word = io_int_word,
};
if (io_int_word & IO_INT_WORD_AI) {
irq.type = KVM_S390_INT_IO(1, 0, 0, 0);
} else {
irq.type = KVM_S390_INT_IO(0, (subchannel_id & 0xff00) >> 8,
(subchannel_id & 0x0006),
subchannel_nr);
}
kvm_s390_inject_flic(fs, &irq);
}
static void kvm_s390_inject_crw_mchk(S390FLICState *fs)
{
struct kvm_s390_irq irq = {
.type = KVM_S390_MCHK,
.u.mchk.cr14 = CR14_CHANNEL_REPORT_SC,
.u.mchk.mcic = s390_build_validity_mcic() | MCIC_SC_CP,
};
kvm_s390_inject_flic(fs, &irq);
} }
static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id,
uint16_t subchannel_nr) uint16_t subchannel_nr)
{ {
KVMS390FLICState *flic = KVM_S390_FLIC(fs); KVMS390FLICState *flic = s390_get_kvm_flic(fs);
int rc; int rc;
uint32_t sid = subchannel_id << 16 | subchannel_nr; uint32_t sid = subchannel_id << 16 | subchannel_nr;
struct kvm_device_attr attr = { struct kvm_device_attr attr = {
@ -154,7 +203,7 @@ static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id,
static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc, static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc,
uint16_t mode) uint16_t mode)
{ {
KVMS390FLICState *flic = KVM_S390_FLIC(fs); KVMS390FLICState *flic = s390_get_kvm_flic(fs);
struct kvm_s390_ais_req req = { struct kvm_s390_ais_req req = {
.isc = isc, .isc = isc,
.mode = mode, .mode = mode,
@ -174,7 +223,7 @@ static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc,
static int kvm_s390_inject_airq(S390FLICState *fs, uint8_t type, static int kvm_s390_inject_airq(S390FLICState *fs, uint8_t type,
uint8_t isc, uint8_t flags) uint8_t isc, uint8_t flags)
{ {
KVMS390FLICState *flic = KVM_S390_FLIC(fs); KVMS390FLICState *flic = s390_get_kvm_flic(fs);
uint32_t id = css_get_adapter_id(type, isc); uint32_t id = css_get_adapter_id(type, isc);
struct kvm_device_attr attr = { struct kvm_device_attr attr = {
.group = KVM_DEV_FLIC_AIRQ_INJECT, .group = KVM_DEV_FLIC_AIRQ_INJECT,
@ -263,7 +312,7 @@ static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
.group = KVM_DEV_FLIC_ADAPTER_MODIFY, .group = KVM_DEV_FLIC_ADAPTER_MODIFY,
.addr = (uint64_t)&req, .addr = (uint64_t)&req,
}; };
KVMS390FLICState *flic = KVM_S390_FLIC(fs); KVMS390FLICState *flic = s390_get_kvm_flic(fs);
int r; int r;
if (!kvm_gsi_routing_enabled()) { if (!kvm_gsi_routing_enabled()) {
@ -614,6 +663,9 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
fsc->clear_io_irq = kvm_s390_clear_io_flic; fsc->clear_io_irq = kvm_s390_clear_io_flic;
fsc->modify_ais_mode = kvm_s390_modify_ais_mode; fsc->modify_ais_mode = kvm_s390_modify_ais_mode;
fsc->inject_airq = kvm_s390_inject_airq; fsc->inject_airq = kvm_s390_inject_airq;
fsc->inject_service = kvm_s390_inject_service;
fsc->inject_io = kvm_s390_inject_io;
fsc->inject_crw_mchk = kvm_s390_inject_crw_mchk;
} }
static const TypeInfo kvm_s390_flic_info = { static const TypeInfo kvm_s390_flic_info = {

View File

@ -439,7 +439,7 @@ static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
bool do_map) bool do_map)
{ {
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map); return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
} }
@ -520,7 +520,7 @@ void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable,
int ret, isc; int ret, isc;
IoAdapter *adapter; IoAdapter *adapter;
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
/* /*
* Disallow multiple registrations for the same device type. * Disallow multiple registrations for the same device type.
@ -566,7 +566,7 @@ static void css_clear_io_interrupt(uint16_t subchannel_id,
Error *err = NULL; Error *err = NULL;
static bool no_clear_irq; static bool no_clear_irq;
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
int r; int r;
if (unlikely(no_clear_irq)) { if (unlikely(no_clear_irq)) {
@ -640,7 +640,7 @@ void css_conditional_io_interrupt(SubchDev *sch)
int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode) int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode)
{ {
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
int r; int r;
if (env->psw.mask & PSW_MASK_PSTATE) { if (env->psw.mask & PSW_MASK_PSTATE) {
@ -666,7 +666,7 @@ out:
void css_adapter_interrupt(CssIoAdapterType type, uint8_t isc) void css_adapter_interrupt(CssIoAdapterType type, uint8_t isc)
{ {
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI;
IoAdapter *adapter = channel_subsys.io_adapters[type][isc]; IoAdapter *adapter = channel_subsys.io_adapters[type][isc];

View File

@ -293,10 +293,10 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
ef->receive_mask = be32_to_cpu(tmp_mask); ef->receive_mask = be32_to_cpu(tmp_mask);
/* return the SCLP's capability masks to the guest */ /* return the SCLP's capability masks to the guest */
tmp_mask = cpu_to_be32(get_host_send_mask(ef)); tmp_mask = cpu_to_be32(get_host_receive_mask(ef));
copy_mask(WEM_RECEIVE_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask, copy_mask(WEM_RECEIVE_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask,
mask_length, sizeof(tmp_mask)); mask_length, sizeof(tmp_mask));
tmp_mask = cpu_to_be32(get_host_receive_mask(ef)); tmp_mask = cpu_to_be32(get_host_send_mask(ef));
copy_mask(WEM_SEND_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask, copy_mask(WEM_SEND_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask,
mask_length, sizeof(tmp_mask)); mask_length, sizeof(tmp_mask));

View File

@ -309,49 +309,187 @@ static uint64_t get_st_pto(uint64_t entry)
: 0; : 0;
} }
static uint64_t s390_guest_io_table_walk(uint64_t guest_iota, static bool rt_entry_isvalid(uint64_t entry)
uint64_t guest_dma_address)
{ {
uint64_t sto_a, pto_a, px_a; return (entry & ZPCI_TABLE_VALID_MASK) == ZPCI_TABLE_VALID;
uint64_t sto, pto, pte; }
uint32_t rtx, sx, px;
rtx = calc_rtx(guest_dma_address); static bool pt_entry_isvalid(uint64_t entry)
sx = calc_sx(guest_dma_address); {
px = calc_px(guest_dma_address); return (entry & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID;
}
sto_a = guest_iota + rtx * sizeof(uint64_t); static bool entry_isprotected(uint64_t entry)
sto = address_space_ldq(&address_space_memory, sto_a, {
MEMTXATTRS_UNSPECIFIED, NULL); return (entry & ZPCI_TABLE_PROT_MASK) == ZPCI_TABLE_PROTECTED;
sto = get_rt_sto(sto); }
if (!sto) {
pte = 0; /* ett is expected table type, -1 page table, 0 segment table, 1 region table */
static uint64_t get_table_index(uint64_t iova, int8_t ett)
{
switch (ett) {
case ZPCI_ETT_PT:
return calc_px(iova);
case ZPCI_ETT_ST:
return calc_sx(iova);
case ZPCI_ETT_RT:
return calc_rtx(iova);
}
return -1;
}
static bool entry_isvalid(uint64_t entry, int8_t ett)
{
switch (ett) {
case ZPCI_ETT_PT:
return pt_entry_isvalid(entry);
case ZPCI_ETT_ST:
case ZPCI_ETT_RT:
return rt_entry_isvalid(entry);
}
return false;
}
/* Return true if address translation is done */
static bool translate_iscomplete(uint64_t entry, int8_t ett)
{
switch (ett) {
case 0:
return (entry & ZPCI_TABLE_FC) ? true : false;
case 1:
return false;
}
return true;
}
static uint64_t get_frame_size(int8_t ett)
{
switch (ett) {
case ZPCI_ETT_PT:
return 1ULL << 12;
case ZPCI_ETT_ST:
return 1ULL << 20;
case ZPCI_ETT_RT:
return 1ULL << 31;
}
return 0;
}
static uint64_t get_next_table_origin(uint64_t entry, int8_t ett)
{
switch (ett) {
case ZPCI_ETT_PT:
return entry & ZPCI_PTE_ADDR_MASK;
case ZPCI_ETT_ST:
return get_st_pto(entry);
case ZPCI_ETT_RT:
return get_rt_sto(entry);
}
return 0;
}
/**
* table_translate: do translation within one table and return the following
* table origin
*
* @entry: the entry being translated, the result is stored in this.
* @to: the address of table origin.
* @ett: expected table type, 1 region table, 0 segment table and -1 page table.
* @error: error code
*/
static uint64_t table_translate(S390IOTLBEntry *entry, uint64_t to, int8_t ett,
uint16_t *error)
{
uint64_t tx, te, nto = 0;
uint16_t err = 0;
tx = get_table_index(entry->iova, ett);
te = address_space_ldq(&address_space_memory, to + tx * sizeof(uint64_t),
MEMTXATTRS_UNSPECIFIED, NULL);
if (!te) {
err = ERR_EVENT_INVALTE;
goto out; goto out;
} }
pto_a = sto + sx * sizeof(uint64_t); if (!entry_isvalid(te, ett)) {
pto = address_space_ldq(&address_space_memory, pto_a, entry->perm &= IOMMU_NONE;
MEMTXATTRS_UNSPECIFIED, NULL);
pto = get_st_pto(pto);
if (!pto) {
pte = 0;
goto out; goto out;
} }
px_a = pto + px * sizeof(uint64_t); if (ett == ZPCI_ETT_RT && ((te & ZPCI_TABLE_LEN_RTX) != ZPCI_TABLE_LEN_RTX
pte = address_space_ldq(&address_space_memory, px_a, || te & ZPCI_TABLE_OFFSET_MASK)) {
MEMTXATTRS_UNSPECIFIED, NULL); err = ERR_EVENT_INVALTL;
goto out;
}
nto = get_next_table_origin(te, ett);
if (!nto) {
err = ERR_EVENT_TT;
goto out;
}
if (entry_isprotected(te)) {
entry->perm &= IOMMU_RO;
} else {
entry->perm &= IOMMU_RW;
}
if (translate_iscomplete(te, ett)) {
switch (ett) {
case ZPCI_ETT_PT:
entry->translated_addr = te & ZPCI_PTE_ADDR_MASK;
break;
case ZPCI_ETT_ST:
entry->translated_addr = (te & ZPCI_SFAA_MASK) |
(entry->iova & ~ZPCI_SFAA_MASK);
break;
}
nto = 0;
}
out: out:
return pte; if (err) {
entry->perm = IOMMU_NONE;
*error = err;
}
entry->len = get_frame_size(ett);
return nto;
}
uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr,
S390IOTLBEntry *entry)
{
uint64_t to = s390_pci_get_table_origin(g_iota);
int8_t ett = 1;
uint16_t error = 0;
entry->iova = addr & PAGE_MASK;
entry->translated_addr = 0;
entry->perm = IOMMU_RW;
if (entry_isprotected(g_iota)) {
entry->perm &= IOMMU_RO;
}
while (to) {
to = table_translate(entry, to, ett--, &error);
}
return error;
} }
static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
IOMMUAccessFlags flag) IOMMUAccessFlags flag)
{ {
uint64_t pte;
uint32_t flags;
S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr); S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
S390IOTLBEntry *entry;
uint64_t iova = addr & PAGE_MASK;
uint16_t error = 0;
IOMMUTLBEntry ret = { IOMMUTLBEntry ret = {
.target_as = &address_space_memory, .target_as = &address_space_memory,
.iova = 0, .iova = 0,
@ -374,26 +512,31 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr); DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr);
if (addr < iommu->pba || addr > iommu->pal) { if (addr < iommu->pba || addr > iommu->pal) {
return ret; error = ERR_EVENT_OORANGE;
goto err;
} }
pte = s390_guest_io_table_walk(s390_pci_get_table_origin(iommu->g_iota), entry = g_hash_table_lookup(iommu->iotlb, &iova);
addr); if (entry) {
if (!pte) { ret.iova = entry->iova;
return ret; ret.translated_addr = entry->translated_addr;
} ret.addr_mask = entry->len - 1;
ret.perm = entry->perm;
flags = pte & ZPCI_PTE_FLAG_MASK;
ret.iova = addr;
ret.translated_addr = pte & ZPCI_PTE_ADDR_MASK;
ret.addr_mask = 0xfff;
if (flags & ZPCI_PTE_INVALID) {
ret.perm = IOMMU_NONE;
} else { } else {
ret.perm = IOMMU_RW; ret.iova = iova;
ret.addr_mask = ~PAGE_MASK;
ret.perm = IOMMU_NONE;
} }
if (flag != IOMMU_NONE && !(flag & ret.perm)) {
error = ERR_EVENT_TPROTE;
}
err:
if (error) {
iommu->pbdev->state = ZPCI_FS_ERROR;
s390_pci_generate_error_event(error, iommu->pbdev->fh,
iommu->pbdev->fid, addr, 0);
}
return ret; return ret;
} }
@ -435,6 +578,8 @@ static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus,
PCI_FUNC(devfn)); PCI_FUNC(devfn));
memory_region_init(&iommu->mr, OBJECT(iommu), mr_name, UINT64_MAX); memory_region_init(&iommu->mr, OBJECT(iommu), mr_name, UINT64_MAX);
address_space_init(&iommu->as, &iommu->mr, as_name); address_space_init(&iommu->as, &iommu->mr, as_name);
iommu->iotlb = g_hash_table_new_full(g_int64_hash, g_int64_equal,
NULL, g_free);
table->iommu[PCI_SLOT(devfn)] = iommu; table->iommu[PCI_SLOT(devfn)] = iommu;
g_free(mr_name); g_free(mr_name);
@ -524,6 +669,7 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu)
void s390_pci_iommu_disable(S390PCIIOMMU *iommu) void s390_pci_iommu_disable(S390PCIIOMMU *iommu)
{ {
iommu->enabled = false; iommu->enabled = false;
g_hash_table_remove_all(iommu->iotlb);
memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr)); memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr));
object_unparent(OBJECT(&iommu->iommu_mr)); object_unparent(OBJECT(&iommu->iommu_mr));
} }
@ -539,6 +685,7 @@ static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn)
} }
table->iommu[PCI_SLOT(devfn)] = NULL; table->iommu[PCI_SLOT(devfn)] = NULL;
g_hash_table_destroy(iommu->iotlb);
address_space_destroy(&iommu->as); address_space_destroy(&iommu->as);
object_unparent(OBJECT(&iommu->mr)); object_unparent(OBJECT(&iommu->mr));
object_unparent(OBJECT(iommu)); object_unparent(OBJECT(iommu));

View File

@ -148,6 +148,8 @@ enum ZpciIoatDtype {
#define ZPCI_STE_FLAG_MASK 0x7ffULL #define ZPCI_STE_FLAG_MASK 0x7ffULL
#define ZPCI_STE_ADDR_MASK (~ZPCI_STE_FLAG_MASK) #define ZPCI_STE_ADDR_MASK (~ZPCI_STE_FLAG_MASK)
#define ZPCI_SFAA_MASK (~((1ULL << 20) - 1))
/* I/O Page tables */ /* I/O Page tables */
#define ZPCI_PTE_VALID_MASK 0x400 #define ZPCI_PTE_VALID_MASK 0x400
#define ZPCI_PTE_INVALID 0x400 #define ZPCI_PTE_INVALID 0x400
@ -165,10 +167,15 @@ enum ZpciIoatDtype {
#define ZPCI_TABLE_INVALID 0x20 #define ZPCI_TABLE_INVALID 0x20
#define ZPCI_TABLE_PROTECTED 0x200 #define ZPCI_TABLE_PROTECTED 0x200
#define ZPCI_TABLE_UNPROTECTED 0x000 #define ZPCI_TABLE_UNPROTECTED 0x000
#define ZPCI_TABLE_FC 0x400
#define ZPCI_TABLE_VALID_MASK 0x20 #define ZPCI_TABLE_VALID_MASK 0x20
#define ZPCI_TABLE_PROT_MASK 0x200 #define ZPCI_TABLE_PROT_MASK 0x200
#define ZPCI_ETT_RT 1
#define ZPCI_ETT_ST 0
#define ZPCI_ETT_PT -1
/* PCI Function States /* PCI Function States
* *
* reserved: default; device has just been plugged or is in progress of being * reserved: default; device has just been plugged or is in progress of being
@ -253,6 +260,13 @@ typedef struct S390MsixInfo {
uint32_t pba_offset; uint32_t pba_offset;
} S390MsixInfo; } S390MsixInfo;
typedef struct S390IOTLBEntry {
uint64_t iova;
uint64_t translated_addr;
uint64_t len;
uint64_t perm;
} S390IOTLBEntry;
typedef struct S390PCIBusDevice S390PCIBusDevice; typedef struct S390PCIBusDevice S390PCIBusDevice;
typedef struct S390PCIIOMMU { typedef struct S390PCIIOMMU {
Object parent_obj; Object parent_obj;
@ -264,6 +278,7 @@ typedef struct S390PCIIOMMU {
uint64_t g_iota; uint64_t g_iota;
uint64_t pba; uint64_t pba;
uint64_t pal; uint64_t pal;
GHashTable *iotlb;
} S390PCIIOMMU; } S390PCIIOMMU;
typedef struct S390PCIIOMMUTable { typedef struct S390PCIIOMMUTable {
@ -320,6 +335,8 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu);
void s390_pci_iommu_disable(S390PCIIOMMU *iommu); void s390_pci_iommu_disable(S390PCIIOMMU *iommu);
void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
uint64_t faddr, uint32_t e); uint64_t faddr, uint32_t e);
uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr,
S390IOTLBEntry *entry);
S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx); S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx);
S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh); S390PCIBusDevice *s390_pci_find_dev_by_fh(S390pciState *s, uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid); S390PCIBusDevice *s390_pci_find_dev_by_fid(S390pciState *s, uint32_t fid);

View File

@ -571,27 +571,65 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0; return 0;
} }
static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry)
{
S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
IOMMUTLBEntry notify = {
.target_as = &address_space_memory,
.iova = entry->iova,
.translated_addr = entry->translated_addr,
.perm = entry->perm,
.addr_mask = ~PAGE_MASK,
};
if (entry->perm == IOMMU_NONE) {
if (!cache) {
return;
}
g_hash_table_remove(iommu->iotlb, &entry->iova);
} else {
if (cache) {
if (cache->perm == entry->perm &&
cache->translated_addr == entry->translated_addr) {
return;
}
notify.perm = IOMMU_NONE;
memory_region_notify_iommu(&iommu->iommu_mr, notify);
notify.perm = entry->perm;
}
cache = g_new(S390IOTLBEntry, 1);
cache->iova = entry->iova;
cache->translated_addr = entry->translated_addr;
cache->len = PAGE_SIZE;
cache->perm = entry->perm;
g_hash_table_replace(iommu->iotlb, &cache->iova, cache);
}
memory_region_notify_iommu(&iommu->iommu_mr, notify);
}
int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
{ {
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
uint32_t fh; uint32_t fh;
uint16_t error = 0;
S390PCIBusDevice *pbdev; S390PCIBusDevice *pbdev;
S390PCIIOMMU *iommu; S390PCIIOMMU *iommu;
S390IOTLBEntry entry;
hwaddr start, end; hwaddr start, end;
IOMMUTLBEntry entry;
IOMMUMemoryRegion *iommu_mr;
IOMMUMemoryRegionClass *imrc;
cpu_synchronize_state(CPU(cpu)); cpu_synchronize_state(CPU(cpu));
if (env->psw.mask & PSW_MASK_PSTATE) { if (env->psw.mask & PSW_MASK_PSTATE) {
s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
goto out; return 0;
} }
if (r2 & 0x1) { if (r2 & 0x1) {
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
goto out; return 0;
} }
fh = env->regs[r1] >> 32; fh = env->regs[r1] >> 32;
@ -602,7 +640,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
if (!pbdev) { if (!pbdev) {
DPRINTF("rpcit no pci dev\n"); DPRINTF("rpcit no pci dev\n");
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
goto out; return 0;
} }
switch (pbdev->state) { switch (pbdev->state) {
@ -622,44 +660,37 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
iommu = pbdev->iommu; iommu = pbdev->iommu;
if (!iommu->g_iota) { if (!iommu->g_iota) {
pbdev->state = ZPCI_FS_ERROR; error = ERR_EVENT_INVALAS;
setcc(cpu, ZPCI_PCI_LS_ERR); goto err;
s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
start, 0);
goto out;
} }
if (end < iommu->pba || start > iommu->pal) { if (end < iommu->pba || start > iommu->pal) {
pbdev->state = ZPCI_FS_ERROR; error = ERR_EVENT_OORANGE;
setcc(cpu, ZPCI_PCI_LS_ERR); goto err;
s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
start, 0);
goto out;
} }
iommu_mr = &iommu->iommu_mr;
imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
while (start < end) { while (start < end) {
entry = imrc->translate(iommu_mr, start, IOMMU_NONE); error = s390_guest_io_table_walk(iommu->g_iota, start, &entry);
if (error) {
if (!entry.translated_addr) { break;
pbdev->state = ZPCI_FS_ERROR;
setcc(cpu, ZPCI_PCI_LS_ERR);
s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
start, ERR_EVENT_Q_BIT);
goto out;
} }
memory_region_notify_iommu(iommu_mr, entry); start += entry.len;
start += entry.addr_mask + 1; while (entry.iova < start && entry.iova < end) {
s390_pci_update_iotlb(iommu, &entry);
entry.iova += PAGE_SIZE;
entry.translated_addr += PAGE_SIZE;
}
}
err:
if (error) {
pbdev->state = ZPCI_FS_ERROR;
setcc(cpu, ZPCI_PCI_LS_ERR);
s390_set_status_code(env, r1, ZPCI_PCI_ST_FUNC_IN_ERR);
s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, start, 0);
} else {
setcc(cpu, ZPCI_PCI_LS_OK);
} }
setcc(cpu, ZPCI_PCI_LS_OK);
out:
return 0; return 0;
} }
@ -834,6 +865,8 @@ static int reg_ioat(CPUS390XState *env, S390PCIIOMMU *iommu, ZpciFib fib,
uint8_t dt = (g_iota >> 2) & 0x7; uint8_t dt = (g_iota >> 2) & 0x7;
uint8_t t = (g_iota >> 11) & 0x1; uint8_t t = (g_iota >> 11) & 0x1;
pba &= ~0xfff;
pal |= 0xfff;
if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) { if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
s390_program_interrupt(env, PGM_OPERAND, 6, ra); s390_program_interrupt(env, PGM_OPERAND, 6, ra);
return -EINVAL; return -EINVAL;

View File

@ -78,10 +78,6 @@ static void s390_init_cpus(MachineState *machine)
MachineClass *mc = MACHINE_GET_CLASS(machine); MachineClass *mc = MACHINE_GET_CLASS(machine);
int i; int i;
if (tcg_enabled() && max_cpus > 1) {
error_report("WARNING: SMP support on s390x is experimental!");
}
/* initialize possible_cpus */ /* initialize possible_cpus */
mc->possible_cpu_arch_ids(machine); mc->possible_cpu_arch_ids(machine);

View File

@ -1111,7 +1111,7 @@ static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
int ret; int ret;
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
ret = virtio_ccw_get_mappings(dev); ret = virtio_ccw_get_mappings(dev);
if (ret) { if (ret) {
@ -1129,7 +1129,7 @@ static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
{ {
S390FLICState *fs = s390_get_flic(); S390FLICState *fs = s390_get_flic();
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); S390FLICStateClass *fsc = s390_get_flic_class(fs);
fsc->release_adapter_routes(fs, &dev->routes); fsc->release_adapter_routes(fs, &dev->routes);
} }

View File

@ -16,6 +16,7 @@
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "hw/s390x/adapter.h" #include "hw/s390x/adapter.h"
#include "hw/virtio/virtio.h" #include "hw/virtio/virtio.h"
#include "qemu/queue.h"
/* /*
* Reserve enough gsis to accommodate all virtio devices. * Reserve enough gsis to accommodate all virtio devices.
@ -66,6 +67,11 @@ typedef struct S390FLICStateClass {
int (*modify_ais_mode)(S390FLICState *fs, uint8_t isc, uint16_t mode); int (*modify_ais_mode)(S390FLICState *fs, uint8_t isc, uint16_t mode);
int (*inject_airq)(S390FLICState *fs, uint8_t type, uint8_t isc, int (*inject_airq)(S390FLICState *fs, uint8_t type, uint8_t isc,
uint8_t flags); uint8_t flags);
void (*inject_service)(S390FLICState *fs, uint32_t parm);
void (*inject_io)(S390FLICState *fs, uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word);
void (*inject_crw_mchk)(S390FLICState *fs);
} S390FLICStateClass; } S390FLICStateClass;
#define TYPE_KVM_S390_FLIC "s390-flic-kvm" #define TYPE_KVM_S390_FLIC "s390-flic-kvm"
@ -80,24 +86,57 @@ typedef struct S390FLICStateClass {
#define SIC_IRQ_MODE_SINGLE 1 #define SIC_IRQ_MODE_SINGLE 1
#define AIS_MODE_MASK(isc) (0x80 >> isc) #define AIS_MODE_MASK(isc) (0x80 >> isc)
#define ISC_TO_PENDING_IO(_isc) (0x80 >> (_isc))
#define CR6_TO_PENDING_IO(_cr6) (((_cr6) >> 24) & 0xff)
/* organize the ISC bits so that the macros above work */
#define FLIC_PENDING_IO_ISC7 (1 << 0)
#define FLIC_PENDING_IO_ISC6 (1 << 1)
#define FLIC_PENDING_IO_ISC5 (1 << 2)
#define FLIC_PENDING_IO_ISC4 (1 << 3)
#define FLIC_PENDING_IO_ISC3 (1 << 4)
#define FLIC_PENDING_IO_ISC2 (1 << 5)
#define FLIC_PENDING_IO_ISC1 (1 << 6)
#define FLIC_PENDING_IO_ISC0 (1 << 7)
#define FLIC_PENDING_SERVICE (1 << 8)
#define FLIC_PENDING_MCHK_CR (1 << 9)
#define FLIC_PENDING_IO (FLIC_PENDING_IO_ISC0 | FLIC_PENDING_IO_ISC1 | \
FLIC_PENDING_IO_ISC2 | FLIC_PENDING_IO_ISC3 | \
FLIC_PENDING_IO_ISC4 | FLIC_PENDING_IO_ISC5 | \
FLIC_PENDING_IO_ISC6 | FLIC_PENDING_IO_ISC7)
typedef struct QEMUS390FlicIO {
uint16_t id;
uint16_t nr;
uint32_t parm;
uint32_t word;
QLIST_ENTRY(QEMUS390FlicIO) next;
} QEMUS390FlicIO;
typedef struct QEMUS390FLICState { typedef struct QEMUS390FLICState {
S390FLICState parent_obj; S390FLICState parent_obj;
uint32_t pending;
uint32_t service_param;
uint8_t simm; uint8_t simm;
uint8_t nimm; uint8_t nimm;
QLIST_HEAD(, QEMUS390FlicIO) io[8];
} QEMUS390FLICState; } QEMUS390FLICState;
uint32_t qemu_s390_flic_dequeue_service(QEMUS390FLICState *flic);
QEMUS390FlicIO *qemu_s390_flic_dequeue_io(QEMUS390FLICState *flic,
uint64_t cr6);
void qemu_s390_flic_dequeue_crw_mchk(QEMUS390FLICState *flic);
bool qemu_s390_flic_has_service(QEMUS390FLICState *flic);
bool qemu_s390_flic_has_io(QEMUS390FLICState *fs, uint64_t cr6);
bool qemu_s390_flic_has_crw_mchk(QEMUS390FLICState *flic);
bool qemu_s390_flic_has_any(QEMUS390FLICState *flic);
void s390_flic_init(void); void s390_flic_init(void);
S390FLICState *s390_get_flic(void); S390FLICState *s390_get_flic(void);
QEMUS390FLICState *s390_get_qemu_flic(S390FLICState *fs);
S390FLICStateClass *s390_get_flic_class(S390FLICState *fs);
bool ais_needed(void *opaque); bool ais_needed(void *opaque);
#ifdef CONFIG_KVM
DeviceState *s390_flic_kvm_create(void);
#else
static inline DeviceState *s390_flic_kvm_create(void)
{
return NULL;
}
#endif
#endif /* HW_S390_FLIC_H */ #endif /* HW_S390_FLIC_H */

View File

@ -100,7 +100,6 @@ static void s390_cpu_initial_reset(CPUState *s)
{ {
S390CPU *cpu = S390_CPU(s); S390CPU *cpu = S390_CPU(s);
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
int i;
s390_cpu_reset(s); s390_cpu_reset(s);
/* initial reset does not clear everything! */ /* initial reset does not clear everything! */
@ -116,10 +115,6 @@ static void s390_cpu_initial_reset(CPUState *s)
env->gbea = 1; env->gbea = 1;
env->pfault_token = -1UL; env->pfault_token = -1UL;
for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
env->io_index[i] = -1;
}
env->mchk_index = -1;
/* tininess for underflow is detected before rounding */ /* tininess for underflow is detected before rounding */
set_float_detect_tininess(float_tininess_before_rounding, set_float_detect_tininess(float_tininess_before_rounding,
@ -137,7 +132,6 @@ static void s390_cpu_full_reset(CPUState *s)
S390CPU *cpu = S390_CPU(s); S390CPU *cpu = S390_CPU(s);
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
int i;
scc->parent_reset(s); scc->parent_reset(s);
cpu->env.sigp_order = 0; cpu->env.sigp_order = 0;
@ -153,10 +147,6 @@ static void s390_cpu_full_reset(CPUState *s)
env->gbea = 1; env->gbea = 1;
env->pfault_token = -1UL; env->pfault_token = -1UL;
for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
env->io_index[i] = -1;
}
env->mchk_index = -1;
/* tininess for underflow is detected before rounding */ /* tininess for underflow is detected before rounding */
set_float_detect_tininess(float_tininess_before_rounding, set_float_detect_tininess(float_tininess_before_rounding,

View File

@ -53,12 +53,6 @@
#define MMU_USER_IDX 0 #define MMU_USER_IDX 0
#define MAX_IO_QUEUE 16
#define MAX_MCHK_QUEUE 16
#define PSW_MCHK_MASK 0x0004000000000000
#define PSW_IO_MASK 0x0200000000000000
#define S390_MAX_CPUS 248 #define S390_MAX_CPUS 248
typedef struct PSW { typedef struct PSW {
@ -66,17 +60,6 @@ typedef struct PSW {
uint64_t addr; uint64_t addr;
} PSW; } PSW;
typedef struct IOIntQueue {
uint16_t id;
uint16_t nr;
uint32_t parm;
uint32_t word;
} IOIntQueue;
typedef struct MchkQueue {
uint16_t type;
} MchkQueue;
struct CPUS390XState { struct CPUS390XState {
uint64_t regs[16]; /* GP registers */ uint64_t regs[16]; /* GP registers */
/* /*
@ -122,15 +105,9 @@ struct CPUS390XState {
uint64_t cregs[16]; /* control registers */ uint64_t cregs[16]; /* control registers */
IOIntQueue io_queue[MAX_IO_QUEUE][8];
MchkQueue mchk_queue[MAX_MCHK_QUEUE];
int pending_int; int pending_int;
uint32_t service_param;
uint16_t external_call_addr; uint16_t external_call_addr;
DECLARE_BITMAP(emergency_signals, S390_MAX_CPUS); DECLARE_BITMAP(emergency_signals, S390_MAX_CPUS);
int io_index[8];
int mchk_index;
uint64_t ckc; uint64_t ckc;
uint64_t cputm; uint64_t cputm;
@ -409,9 +386,6 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
#define EXCP_IO 7 /* I/O interrupt */ #define EXCP_IO 7 /* I/O interrupt */
#define EXCP_MCHK 8 /* machine check */ #define EXCP_MCHK 8 /* machine check */
#define INTERRUPT_IO (1 << 0)
#define INTERRUPT_MCHK (1 << 1)
#define INTERRUPT_EXT_SERVICE (1 << 2)
#define INTERRUPT_EXT_CPU_TIMER (1 << 3) #define INTERRUPT_EXT_CPU_TIMER (1 << 3)
#define INTERRUPT_EXT_CLOCK_COMPARATOR (1 << 4) #define INTERRUPT_EXT_CLOCK_COMPARATOR (1 << 4)
#define INTERRUPT_EXTERNAL_CALL (1 << 5) #define INTERRUPT_EXTERNAL_CALL (1 << 5)
@ -452,62 +426,66 @@ static inline void setcc(S390CPU *cpu, uint64_t cc)
} }
/* STSI */ /* STSI */
#define STSI_LEVEL_MASK 0x00000000f0000000ULL #define STSI_R0_FC_MASK 0x00000000f0000000ULL
#define STSI_LEVEL_CURRENT 0x0000000000000000ULL #define STSI_R0_FC_CURRENT 0x0000000000000000ULL
#define STSI_LEVEL_1 0x0000000010000000ULL #define STSI_R0_FC_LEVEL_1 0x0000000010000000ULL
#define STSI_LEVEL_2 0x0000000020000000ULL #define STSI_R0_FC_LEVEL_2 0x0000000020000000ULL
#define STSI_LEVEL_3 0x0000000030000000ULL #define STSI_R0_FC_LEVEL_3 0x0000000030000000ULL
#define STSI_R0_RESERVED_MASK 0x000000000fffff00ULL #define STSI_R0_RESERVED_MASK 0x000000000fffff00ULL
#define STSI_R0_SEL1_MASK 0x00000000000000ffULL #define STSI_R0_SEL1_MASK 0x00000000000000ffULL
#define STSI_R1_RESERVED_MASK 0x00000000ffff0000ULL #define STSI_R1_RESERVED_MASK 0x00000000ffff0000ULL
#define STSI_R1_SEL2_MASK 0x000000000000ffffULL #define STSI_R1_SEL2_MASK 0x000000000000ffffULL
/* Basic Machine Configuration */ /* Basic Machine Configuration */
struct sysib_111 { typedef struct SysIB_111 {
uint32_t res1[8]; uint8_t res1[32];
uint8_t manuf[16]; uint8_t manuf[16];
uint8_t type[4]; uint8_t type[4];
uint8_t res2[12]; uint8_t res2[12];
uint8_t model[16]; uint8_t model[16];
uint8_t sequence[16]; uint8_t sequence[16];
uint8_t plant[4]; uint8_t plant[4];
uint8_t res3[156]; uint8_t res3[3996];
}; } SysIB_111;
QEMU_BUILD_BUG_ON(sizeof(SysIB_111) != 4096);
/* Basic Machine CPU */ /* Basic Machine CPU */
struct sysib_121 { typedef struct SysIB_121 {
uint32_t res1[80]; uint8_t res1[80];
uint8_t sequence[16]; uint8_t sequence[16];
uint8_t plant[4]; uint8_t plant[4];
uint8_t res2[2]; uint8_t res2[2];
uint16_t cpu_addr; uint16_t cpu_addr;
uint8_t res3[152]; uint8_t res3[3992];
}; } SysIB_121;
QEMU_BUILD_BUG_ON(sizeof(SysIB_121) != 4096);
/* Basic Machine CPUs */ /* Basic Machine CPUs */
struct sysib_122 { typedef struct SysIB_122 {
uint8_t res1[32]; uint8_t res1[32];
uint32_t capability; uint32_t capability;
uint16_t total_cpus; uint16_t total_cpus;
uint16_t active_cpus; uint16_t conf_cpus;
uint16_t standby_cpus; uint16_t standby_cpus;
uint16_t reserved_cpus; uint16_t reserved_cpus;
uint16_t adjustments[2026]; uint16_t adjustments[2026];
}; } SysIB_122;
QEMU_BUILD_BUG_ON(sizeof(SysIB_122) != 4096);
/* LPAR CPU */ /* LPAR CPU */
struct sysib_221 { typedef struct SysIB_221 {
uint32_t res1[80]; uint8_t res1[80];
uint8_t sequence[16]; uint8_t sequence[16];
uint8_t plant[4]; uint8_t plant[4];
uint16_t cpu_id; uint16_t cpu_id;
uint16_t cpu_addr; uint16_t cpu_addr;
uint8_t res3[152]; uint8_t res3[3992];
}; } SysIB_221;
QEMU_BUILD_BUG_ON(sizeof(SysIB_221) != 4096);
/* LPAR CPUs */ /* LPAR CPUs */
struct sysib_222 { typedef struct SysIB_222 {
uint32_t res1[32]; uint8_t res1[32];
uint16_t lpar_num; uint16_t lpar_num;
uint8_t res2; uint8_t res2;
uint8_t lcpuc; uint8_t lcpuc;
@ -520,11 +498,12 @@ struct sysib_222 {
uint8_t res3[16]; uint8_t res3[16];
uint16_t dedicated_cpus; uint16_t dedicated_cpus;
uint16_t shared_cpus; uint16_t shared_cpus;
uint8_t res4[180]; uint8_t res4[4020];
}; } SysIB_222;
QEMU_BUILD_BUG_ON(sizeof(SysIB_222) != 4096);
/* VM CPUs */ /* VM CPUs */
struct sysib_322 { typedef struct SysIB_322 {
uint8_t res1[31]; uint8_t res1[31];
uint8_t count; uint8_t count;
struct { struct {
@ -543,7 +522,18 @@ struct sysib_322 {
} vm[8]; } vm[8];
uint8_t res4[1504]; uint8_t res4[1504];
uint8_t ext_names[8][256]; uint8_t ext_names[8][256];
}; } SysIB_322;
QEMU_BUILD_BUG_ON(sizeof(SysIB_322) != 4096);
typedef union SysIB {
SysIB_111 sysib_111;
SysIB_121 sysib_121;
SysIB_122 sysib_122;
SysIB_221 sysib_221;
SysIB_222 sysib_222;
SysIB_322 sysib_322;
} SysIB;
QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096);
/* MMU defines */ /* MMU defines */
#define _ASCE_ORIGIN ~0xfffULL /* segment table origin */ #define _ASCE_ORIGIN ~0xfffULL /* segment table origin */
@ -718,6 +708,10 @@ static inline unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
return 0; return 0;
} }
#endif /* CONFIG_USER_ONLY */ #endif /* CONFIG_USER_ONLY */
static inline uint8_t s390_cpu_get_state(S390CPU *cpu)
{
return cpu->env.cpu_state;
}
/* cpu_models.c */ /* cpu_models.c */
@ -752,7 +746,6 @@ void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
/* service interrupts are floating therefore we must not pass an cpustate */ /* service interrupts are floating therefore we must not pass an cpustate */
void s390_sclp_extint(uint32_t parm); void s390_sclp_extint(uint32_t parm);
/* mmu_helper.c */ /* mmu_helper.c */
int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
int len, bool is_write); int len, bool is_write);

View File

@ -156,8 +156,12 @@ static const S390FeatDef s390_features[] = {
FEAT_INIT("ptff-qpc", S390_FEAT_TYPE_PTFF, 3, "PTFF Query Physical Clock"), FEAT_INIT("ptff-qpc", S390_FEAT_TYPE_PTFF, 3, "PTFF Query Physical Clock"),
FEAT_INIT("ptff-qui", S390_FEAT_TYPE_PTFF, 4, "PTFF Query UTC Information"), FEAT_INIT("ptff-qui", S390_FEAT_TYPE_PTFF, 4, "PTFF Query UTC Information"),
FEAT_INIT("ptff-qtou", S390_FEAT_TYPE_PTFF, 5, "PTFF Query TOD Offset User"), FEAT_INIT("ptff-qtou", S390_FEAT_TYPE_PTFF, 5, "PTFF Query TOD Offset User"),
FEAT_INIT("ptff-qsie", S390_FEAT_TYPE_PTFF, 10, "PTFF Query Steering Information Extended"),
FEAT_INIT("ptff-qtoue", S390_FEAT_TYPE_PTFF, 13, "PTFF Query TOD Offset User Extended"),
FEAT_INIT("ptff-sto", S390_FEAT_TYPE_PTFF, 65, "PTFF Set TOD Offset"), FEAT_INIT("ptff-sto", S390_FEAT_TYPE_PTFF, 65, "PTFF Set TOD Offset"),
FEAT_INIT("ptff-stou", S390_FEAT_TYPE_PTFF, 69, "PTFF Set TOD Offset User"), FEAT_INIT("ptff-stou", S390_FEAT_TYPE_PTFF, 69, "PTFF Set TOD Offset User"),
FEAT_INIT("ptff-stoe", S390_FEAT_TYPE_PTFF, 73, "PTFF Set TOD Offset Extended"),
FEAT_INIT("ptff-stoue", S390_FEAT_TYPE_PTFF, 77, "PTFF Set TOD Offset User Extended"),
FEAT_INIT("kmac-dea", S390_FEAT_TYPE_KMAC, 1, "KMAC DEA"), FEAT_INIT("kmac-dea", S390_FEAT_TYPE_KMAC, 1, "KMAC DEA"),
FEAT_INIT("kmac-tdea-128", S390_FEAT_TYPE_KMAC, 2, "KMAC TDEA-128"), FEAT_INIT("kmac-tdea-128", S390_FEAT_TYPE_KMAC, 2, "KMAC TDEA-128"),
@ -445,6 +449,7 @@ static S390FeatGroupDef s390_feature_groups[] = {
FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"), FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"),
FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"), FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"),
FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"), FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"),
FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"),
FEAT_GROUP_INIT("msa", MSA, "Message-security-assist facility"), FEAT_GROUP_INIT("msa", MSA, "Message-security-assist facility"),
FEAT_GROUP_INIT("msa1", MSA_EXT_1, "Message-security-assist-extension 1 facility"), FEAT_GROUP_INIT("msa1", MSA_EXT_1, "Message-security-assist-extension 1 facility"),
FEAT_GROUP_INIT("msa2", MSA_EXT_2, "Message-security-assist-extension 2 facility"), FEAT_GROUP_INIT("msa2", MSA_EXT_2, "Message-security-assist-extension 2 facility"),

View File

@ -151,8 +151,12 @@ typedef enum {
S390_FEAT_PTFF_QPT, S390_FEAT_PTFF_QPT,
S390_FEAT_PTFF_QUI, S390_FEAT_PTFF_QUI,
S390_FEAT_PTFF_QTOU, S390_FEAT_PTFF_QTOU,
S390_FEAT_PTFF_QSIE,
S390_FEAT_PTFF_QTOUE,
S390_FEAT_PTFF_STO, S390_FEAT_PTFF_STO,
S390_FEAT_PTFF_STOU, S390_FEAT_PTFF_STOU,
S390_FEAT_PTFF_STOE,
S390_FEAT_PTFF_STOUE,
/* KMAC */ /* KMAC */
S390_FEAT_KMAC_DEA, S390_FEAT_KMAC_DEA,

View File

@ -23,6 +23,7 @@
#include "qapi/qmp/qbool.h" #include "qapi/qmp/qbool.h"
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
#include "sysemu/arch_init.h" #include "sysemu/arch_init.h"
#include "hw/pci/pci.h"
#endif #endif
#define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \
@ -1271,6 +1272,11 @@ static void register_types(void)
/* init all bitmaps from gnerated data initially */ /* init all bitmaps from gnerated data initially */
s390_init_feat_bitmap(qemu_max_cpu_feat_init, qemu_max_cpu_feat); s390_init_feat_bitmap(qemu_max_cpu_feat_init, qemu_max_cpu_feat);
#ifndef CONFIG_USER_ONLY
if (!pci_available) {
clear_bit(S390_FEAT_ZPCI, qemu_max_cpu_feat);
}
#endif
for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) { for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) {
s390_init_feat_bitmap(s390_cpu_defs[i].base_init, s390_init_feat_bitmap(s390_cpu_defs[i].base_init,
s390_cpu_defs[i].base_feat); s390_cpu_defs[i].base_feat);

View File

@ -29,6 +29,7 @@
#include "exec/address-spaces.h" #include "exec/address-spaces.h"
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/s390x/s390_flic.h"
#endif #endif
/* #define DEBUG_S390 */ /* #define DEBUG_S390 */
@ -237,6 +238,7 @@ static void do_svc_interrupt(CPUS390XState *env)
static void do_ext_interrupt(CPUS390XState *env) static void do_ext_interrupt(CPUS390XState *env)
{ {
QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
S390CPU *cpu = s390_env_get_cpu(env); S390CPU *cpu = s390_env_get_cpu(env);
uint64_t mask, addr; uint64_t mask, addr;
uint16_t cpu_addr; uint16_t cpu_addr;
@ -273,17 +275,14 @@ static void do_ext_interrupt(CPUS390XState *env)
lowcore->ext_int_code = cpu_to_be16(EXT_CPU_TIMER); lowcore->ext_int_code = cpu_to_be16(EXT_CPU_TIMER);
lowcore->cpu_addr = 0; lowcore->cpu_addr = 0;
env->pending_int &= ~INTERRUPT_EXT_CPU_TIMER; env->pending_int &= ~INTERRUPT_EXT_CPU_TIMER;
} else if ((env->pending_int & INTERRUPT_EXT_SERVICE) && } else if (qemu_s390_flic_has_service(flic) &&
(env->cregs[0] & CR0_SERVICE_SC)) { (env->cregs[0] & CR0_SERVICE_SC)) {
/* uint32_t param;
* FIXME: floating IRQs should be considered by all CPUs and
* shuld not get cleared by CPU reset. param = qemu_s390_flic_dequeue_service(flic);
*/
lowcore->ext_int_code = cpu_to_be16(EXT_SERVICE); lowcore->ext_int_code = cpu_to_be16(EXT_SERVICE);
lowcore->ext_params = cpu_to_be32(env->service_param); lowcore->ext_params = cpu_to_be32(param);
lowcore->cpu_addr = 0; lowcore->cpu_addr = 0;
env->service_param = 0;
env->pending_int &= ~INTERRUPT_EXT_SERVICE;
} else { } else {
g_assert_not_reached(); g_assert_not_reached();
} }
@ -303,95 +302,46 @@ static void do_ext_interrupt(CPUS390XState *env)
static void do_io_interrupt(CPUS390XState *env) static void do_io_interrupt(CPUS390XState *env)
{ {
S390CPU *cpu = s390_env_get_cpu(env); QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
uint64_t mask, addr;
QEMUS390FlicIO *io;
LowCore *lowcore; LowCore *lowcore;
IOIntQueue *q;
uint8_t isc;
int disable = 1;
int found = 0;
if (!(env->psw.mask & PSW_MASK_IO)) { g_assert(env->psw.mask & PSW_MASK_IO);
cpu_abort(CPU(cpu), "I/O int w/o I/O mask\n"); io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]);
} g_assert(io);
for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) { lowcore = cpu_map_lowcore(env);
uint64_t isc_bits;
if (env->io_index[isc] < 0) { lowcore->subchannel_id = cpu_to_be16(io->id);
continue; lowcore->subchannel_nr = cpu_to_be16(io->nr);
} lowcore->io_int_parm = cpu_to_be32(io->parm);
if (env->io_index[isc] >= MAX_IO_QUEUE) { lowcore->io_int_word = cpu_to_be32(io->word);
cpu_abort(CPU(cpu), "I/O queue overrun for isc %d: %d\n", lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
isc, env->io_index[isc]); lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
} mask = be64_to_cpu(lowcore->io_new_psw.mask);
addr = be64_to_cpu(lowcore->io_new_psw.addr);
q = &env->io_queue[env->io_index[isc]][isc]; cpu_unmap_lowcore(lowcore);
isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word)); g_free(io);
if (!(env->cregs[6] & isc_bits)) {
disable = 0;
continue;
}
if (!found) {
uint64_t mask, addr;
found = 1;
lowcore = cpu_map_lowcore(env);
lowcore->subchannel_id = cpu_to_be16(q->id);
lowcore->subchannel_nr = cpu_to_be16(q->nr);
lowcore->io_int_parm = cpu_to_be32(q->parm);
lowcore->io_int_word = cpu_to_be32(q->word);
lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env));
lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr);
mask = be64_to_cpu(lowcore->io_new_psw.mask);
addr = be64_to_cpu(lowcore->io_new_psw.addr);
cpu_unmap_lowcore(lowcore);
env->io_index[isc]--;
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
env->psw.mask, env->psw.addr);
load_psw(env, mask, addr);
}
if (env->io_index[isc] >= 0) {
disable = 0;
}
continue;
}
if (disable) {
env->pending_int &= ~INTERRUPT_IO;
}
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, env->psw.mask,
env->psw.addr);
load_psw(env, mask, addr);
} }
static void do_mchk_interrupt(CPUS390XState *env) static void do_mchk_interrupt(CPUS390XState *env)
{ {
S390CPU *cpu = s390_env_get_cpu(env); QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
uint64_t mask, addr; uint64_t mask, addr;
LowCore *lowcore; LowCore *lowcore;
MchkQueue *q;
int i; int i;
if (!(env->psw.mask & PSW_MASK_MCHECK)) { /* for now we only support channel report machine checks (floating) */
cpu_abort(CPU(cpu), "Machine check w/o mchk mask\n"); g_assert(env->psw.mask & PSW_MASK_MCHECK);
} g_assert(env->cregs[14] & CR14_CHANNEL_REPORT_SC);
if (env->mchk_index < 0 || env->mchk_index >= MAX_MCHK_QUEUE) { qemu_s390_flic_dequeue_crw_mchk(flic);
cpu_abort(CPU(cpu), "Mchk queue overrun: %d\n", env->mchk_index);
}
q = &env->mchk_queue[env->mchk_index];
if (q->type != 1) {
/* Don't know how to handle this... */
cpu_abort(CPU(cpu), "Unknown machine check type %d\n", q->type);
}
if (!(env->cregs[14] & (1 << 28))) {
/* CRW machine checks disabled */
return;
}
lowcore = cpu_map_lowcore(env); lowcore = cpu_map_lowcore(env);
@ -418,11 +368,6 @@ static void do_mchk_interrupt(CPUS390XState *env)
cpu_unmap_lowcore(lowcore); cpu_unmap_lowcore(lowcore);
env->mchk_index--;
if (env->mchk_index == -1) {
env->pending_int &= ~INTERRUPT_MCHK;
}
DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__,
env->psw.mask, env->psw.addr); env->psw.mask, env->psw.addr);
@ -431,12 +376,15 @@ static void do_mchk_interrupt(CPUS390XState *env)
void s390_cpu_do_interrupt(CPUState *cs) void s390_cpu_do_interrupt(CPUState *cs)
{ {
QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic());
S390CPU *cpu = S390_CPU(cs); S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
bool stopped = false;
qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
__func__, cs->exception_index, env->psw.addr); __func__, cs->exception_index, env->psw.addr);
try_deliver:
/* handle machine checks */ /* handle machine checks */
if (cs->exception_index == -1 && s390_cpu_has_mcck_int(cpu)) { if (cs->exception_index == -1 && s390_cpu_has_mcck_int(cpu)) {
cs->exception_index = EXCP_MCHK; cs->exception_index = EXCP_MCHK;
@ -479,20 +427,30 @@ void s390_cpu_do_interrupt(CPUState *cs)
break; break;
case EXCP_STOP: case EXCP_STOP:
do_stop_interrupt(env); do_stop_interrupt(env);
stopped = true;
break; break;
} }
/* WAIT PSW during interrupt injection or STOP interrupt */ if (cs->exception_index != -1 && !stopped) {
if (cs->exception_index == EXCP_HLT) { /* check if there are more pending interrupts to deliver */
/* don't trigger a cpu_loop_exit(), use an interrupt instead */ cs->exception_index = -1;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT); goto try_deliver;
} }
cs->exception_index = -1; cs->exception_index = -1;
/* we might still have pending interrupts, but not deliverable */ /* we might still have pending interrupts, but not deliverable */
if (!env->pending_int) { if (!env->pending_int && !qemu_s390_flic_has_any(flic)) {
cs->interrupt_request &= ~CPU_INTERRUPT_HARD; cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
} }
/* WAIT PSW during interrupt injection or STOP interrupt */
if ((env->psw.mask & PSW_MASK_WAIT) || stopped) {
/* don't trigger a cpu_loop_exit(), use an interrupt instead */
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT);
} else if (cs->halted) {
/* unhalt if we had a WAIT PSW somehwere in our injection chain */
s390_cpu_unhalt(cpu);
}
} }
bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request) bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
@ -510,6 +468,11 @@ bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
s390_cpu_do_interrupt(cs); s390_cpu_do_interrupt(cs);
return true; return true;
} }
if (env->psw.mask & PSW_MASK_WAIT) {
/* Woken up because of a floating interrupt but it has already
* been delivered. Go back to sleep. */
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT);
}
} }
return false; return false;
} }

View File

@ -59,6 +59,12 @@
S390_FEAT_PTFF_QTOU, \ S390_FEAT_PTFF_QTOU, \
S390_FEAT_PTFF_STOU S390_FEAT_PTFF_STOU
#define S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF \
S390_FEAT_PTFF_QSIE, \
S390_FEAT_PTFF_QTOUE, \
S390_FEAT_PTFF_STOE, \
S390_FEAT_PTFF_STOUE
#define S390_FEAT_GROUP_MSA \ #define S390_FEAT_GROUP_MSA \
S390_FEAT_MSA, \ S390_FEAT_MSA, \
S390_FEAT_KMAC_DEA, \ S390_FEAT_KMAC_DEA, \
@ -219,6 +225,9 @@ static uint16_t group_TOD_CLOCK_STEERING[] = {
static uint16_t group_GEN13_PTFF[] = { static uint16_t group_GEN13_PTFF[] = {
S390_FEAT_GROUP_GEN13_PTFF, S390_FEAT_GROUP_GEN13_PTFF,
}; };
static uint16_t group_MULTIPLE_EPOCH_PTFF[] = {
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
};
static uint16_t group_MSA[] = { static uint16_t group_MSA[] = {
S390_FEAT_GROUP_MSA, S390_FEAT_GROUP_MSA,
}; };
@ -466,6 +475,7 @@ static uint16_t full_GEN14_GA1[] = {
S390_FEAT_CMM_NT, S390_FEAT_CMM_NT,
S390_FEAT_HPMA2, S390_FEAT_HPMA2,
S390_FEAT_SIE_KSS, S390_FEAT_SIE_KSS,
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
}; };
/* Default features (in order of release) /* Default features (in order of release)
@ -572,8 +582,10 @@ static uint16_t qemu_LATEST[] = {
S390_FEAT_STFLE_49, S390_FEAT_STFLE_49,
S390_FEAT_LOCAL_TLB_CLEARING, S390_FEAT_LOCAL_TLB_CLEARING,
S390_FEAT_INTERLOCKED_ACCESS_2, S390_FEAT_INTERLOCKED_ACCESS_2,
S390_FEAT_MSA_EXT_4, S390_FEAT_ADAPTER_EVENT_NOTIFICATION,
S390_FEAT_ADAPTER_INT_SUPPRESSION,
S390_FEAT_MSA_EXT_3, S390_FEAT_MSA_EXT_3,
S390_FEAT_MSA_EXT_4,
}; };
/* add all new definitions before this point */ /* add all new definitions before this point */
@ -582,6 +594,8 @@ static uint16_t qemu_MAX[] = {
S390_FEAT_STFLE_53, S390_FEAT_STFLE_53,
/* generates a dependency warning, leave it out for now */ /* generates a dependency warning, leave it out for now */
S390_FEAT_MSA_EXT_5, S390_FEAT_MSA_EXT_5,
/* only with CONFIG_PCI */
S390_FEAT_ZPCI,
}; };
/****** END FEATURE DEFS ******/ /****** END FEATURE DEFS ******/
@ -664,6 +678,7 @@ static FeatGroupDefSpec FeatGroupDef[] = {
FEAT_GROUP_INITIALIZER(PLO), FEAT_GROUP_INITIALIZER(PLO),
FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING), FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING),
FEAT_GROUP_INITIALIZER(GEN13_PTFF), FEAT_GROUP_INITIALIZER(GEN13_PTFF),
FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF),
FEAT_GROUP_INITIALIZER(MSA), FEAT_GROUP_INITIALIZER(MSA),
FEAT_GROUP_INITIALIZER(MSA_EXT_1), FEAT_GROUP_INITIALIZER(MSA_EXT_1),
FEAT_GROUP_INITIALIZER(MSA_EXT_2), FEAT_GROUP_INITIALIZER(MSA_EXT_2),

View File

@ -170,6 +170,16 @@ DEF_HELPER_4(schm, void, env, i64, i64, i64)
DEF_HELPER_3(ssch, void, env, i64, i64) DEF_HELPER_3(ssch, void, env, i64, i64)
DEF_HELPER_2(stcrw, void, env, i64) DEF_HELPER_2(stcrw, void, env, i64)
DEF_HELPER_3(stsch, void, env, i64, i64) DEF_HELPER_3(stsch, void, env, i64, i64)
DEF_HELPER_2(tpi, i32, env, i64)
DEF_HELPER_3(tsch, void, env, i64, i64) DEF_HELPER_3(tsch, void, env, i64, i64)
DEF_HELPER_2(chsc, void, env, i64) DEF_HELPER_2(chsc, void, env, i64)
DEF_HELPER_2(clp, void, env, i32)
DEF_HELPER_3(pcilg, void, env, i32, i32)
DEF_HELPER_3(pcistg, void, env, i32, i32)
DEF_HELPER_4(stpcifc, void, env, i32, i64, i32)
DEF_HELPER_3(sic, void, env, i64, i64)
DEF_HELPER_3(rpcit, void, env, i32, i32)
DEF_HELPER_5(pcistb, void, env, i32, i32, i64, i32)
DEF_HELPER_4(mpcifc, void, env, i32, i64, i32)
#endif #endif

View File

@ -1063,8 +1063,22 @@
C(0xb233, SSCH, S, Z, 0, insn, 0, 0, ssch, 0) C(0xb233, SSCH, S, Z, 0, insn, 0, 0, ssch, 0)
C(0xb239, STCRW, S, Z, 0, insn, 0, 0, stcrw, 0) C(0xb239, STCRW, S, Z, 0, insn, 0, 0, stcrw, 0)
C(0xb234, STSCH, S, Z, 0, insn, 0, 0, stsch, 0) C(0xb234, STSCH, S, Z, 0, insn, 0, 0, stsch, 0)
C(0xb236, TPI , S, Z, la2, 0, 0, 0, tpi, 0)
C(0xb235, TSCH, S, Z, 0, insn, 0, 0, tsch, 0) C(0xb235, TSCH, S, Z, 0, insn, 0, 0, tsch, 0)
/* ??? Not listed in PoO ninth edition, but there's a linux driver that /* ??? Not listed in PoO ninth edition, but there's a linux driver that
uses it: "A CHSC subchannel is usually present on LPAR only." */ uses it: "A CHSC subchannel is usually present on LPAR only." */
C(0xb25f, CHSC, RRE, Z, 0, insn, 0, 0, chsc, 0) C(0xb25f, CHSC, RRE, Z, 0, insn, 0, 0, chsc, 0)
/* zPCI Instructions */
/* None of these instructions are documented in the PoP, so this is all
based upon target/s390x/kvm.c and Linux code and likely incomplete */
C(0xebd0, PCISTB, RSY_a, PCI, la2, 0, 0, 0, pcistb, 0)
C(0xebd1, SIC, RSY_a, AIS, r1, r3, 0, 0, sic, 0)
C(0xb9a0, CLP, RRF_c, PCI, 0, 0, 0, 0, clp, 0)
C(0xb9d0, PCISTG, RRE, PCI, 0, 0, 0, 0, pcistg, 0)
C(0xb9d2, PCILG, RRE, PCI, 0, 0, 0, 0, pcilg, 0)
C(0xb9d3, RPCIT, RRE, PCI, 0, 0, 0, 0, rpcit, 0)
C(0xe3d0, MPCIFC, RXY_a, PCI, la2, 0, 0, 0, mpcifc, 0)
C(0xe3d4, STPCIFC, RXY_a, PCI, la2, 0, 0, 0, stpcifc, 0)
#endif /* CONFIG_USER_ONLY */ #endif /* CONFIG_USER_ONLY */

View File

@ -278,11 +278,6 @@ static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg)
cpu_reset(cs); cpu_reset(cs);
} }
static inline uint8_t s390_cpu_get_state(S390CPU *cpu)
{
return cpu->env.cpu_state;
}
/* arch_dump.c */ /* arch_dump.c */
int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,

View File

@ -15,6 +15,9 @@
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "hw/s390x/ioinst.h" #include "hw/s390x/ioinst.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/s390x/s390_flic.h"
#endif
/* Ensure to exit the TB after this call! */ /* Ensure to exit the TB after this call! */
void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
@ -55,17 +58,6 @@ void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
} }
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
static void cpu_inject_service(S390CPU *cpu, uint32_t param)
{
CPUS390XState *env = &cpu->env;
/* multiplexing is good enough for sclp - kvm does it internally as well*/
env->service_param |= param;
env->pending_int |= INTERRUPT_EXT_SERVICE;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
void cpu_inject_clock_comparator(S390CPU *cpu) void cpu_inject_clock_comparator(S390CPU *cpu)
{ {
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
@ -134,48 +126,6 @@ void cpu_inject_stop(S390CPU *cpu)
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
} }
static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id,
uint16_t subchannel_number,
uint32_t io_int_parm, uint32_t io_int_word)
{
CPUS390XState *env = &cpu->env;
int isc = IO_INT_WORD_ISC(io_int_word);
if (env->io_index[isc] == MAX_IO_QUEUE - 1) {
/* ugh - can't queue anymore. Let's drop. */
return;
}
env->io_index[isc]++;
assert(env->io_index[isc] < MAX_IO_QUEUE);
env->io_queue[env->io_index[isc]][isc].id = subchannel_id;
env->io_queue[env->io_index[isc]][isc].nr = subchannel_number;
env->io_queue[env->io_index[isc]][isc].parm = io_int_parm;
env->io_queue[env->io_index[isc]][isc].word = io_int_word;
env->pending_int |= INTERRUPT_IO;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
static void cpu_inject_crw_mchk(S390CPU *cpu)
{
CPUS390XState *env = &cpu->env;
if (env->mchk_index == MAX_MCHK_QUEUE - 1) {
/* ugh - can't queue anymore. Let's drop. */
return;
}
env->mchk_index++;
assert(env->mchk_index < MAX_MCHK_QUEUE);
env->mchk_queue[env->mchk_index].type = 1;
env->pending_int |= INTERRUPT_MCHK;
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
}
/* /*
* All of the following interrupts are floating, i.e. not per-vcpu. * All of the following interrupts are floating, i.e. not per-vcpu.
* We just need a dummy cpustate in order to be able to inject in the * We just need a dummy cpustate in order to be able to inject in the
@ -183,53 +133,50 @@ static void cpu_inject_crw_mchk(S390CPU *cpu)
*/ */
void s390_sclp_extint(uint32_t parm) void s390_sclp_extint(uint32_t parm)
{ {
if (kvm_enabled()) { S390FLICState *fs = s390_get_flic();
kvm_s390_service_interrupt(parm); S390FLICStateClass *fsc = s390_get_flic_class(fs);
} else {
S390CPU *dummy_cpu = s390_cpu_addr2state(0);
cpu_inject_service(dummy_cpu, parm); fsc->inject_service(fs, parm);
}
} }
void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
uint32_t io_int_parm, uint32_t io_int_word) uint32_t io_int_parm, uint32_t io_int_word)
{ {
if (kvm_enabled()) { S390FLICState *fs = s390_get_flic();
kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm, S390FLICStateClass *fsc = s390_get_flic_class(fs);
io_int_word);
} else {
S390CPU *dummy_cpu = s390_cpu_addr2state(0);
cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm, fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word);
io_int_word);
}
} }
void s390_crw_mchk(void) void s390_crw_mchk(void)
{ {
if (kvm_enabled()) { S390FLICState *fs = s390_get_flic();
kvm_s390_crw_mchk(); S390FLICStateClass *fsc = s390_get_flic_class(fs);
} else {
S390CPU *dummy_cpu = s390_cpu_addr2state(0);
cpu_inject_crw_mchk(dummy_cpu); fsc->inject_crw_mchk(fs);
}
} }
bool s390_cpu_has_mcck_int(S390CPU *cpu) bool s390_cpu_has_mcck_int(S390CPU *cpu)
{ {
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
if (!(env->psw.mask & PSW_MASK_MCHECK)) { if (!(env->psw.mask & PSW_MASK_MCHECK)) {
return false; return false;
} }
return env->pending_int & INTERRUPT_MCHK; /* for now we only support channel report machine checks (floating) */
if (qemu_s390_flic_has_crw_mchk(flic) &&
(env->cregs[14] & CR14_CHANNEL_REPORT_SC)) {
return true;
}
return false;
} }
bool s390_cpu_has_ext_int(S390CPU *cpu) bool s390_cpu_has_ext_int(S390CPU *cpu)
{ {
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
if (!(env->psw.mask & PSW_MASK_EXT)) { if (!(env->psw.mask & PSW_MASK_EXT)) {
@ -261,7 +208,7 @@ bool s390_cpu_has_ext_int(S390CPU *cpu)
return true; return true;
} }
if ((env->pending_int & INTERRUPT_EXT_SERVICE) && if (qemu_s390_flic_has_service(flic) &&
(env->cregs[0] & CR0_SERVICE_SC)) { (env->cregs[0] & CR0_SERVICE_SC)) {
return true; return true;
} }
@ -271,13 +218,14 @@ bool s390_cpu_has_ext_int(S390CPU *cpu)
bool s390_cpu_has_io_int(S390CPU *cpu) bool s390_cpu_has_io_int(S390CPU *cpu)
{ {
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
CPUS390XState *env = &cpu->env; CPUS390XState *env = &cpu->env;
if (!(env->psw.mask & PSW_MASK_IO)) { if (!(env->psw.mask & PSW_MASK_IO)) {
return false; return false;
} }
return env->pending_int & INTERRUPT_IO; return qemu_s390_flic_has_io(flic, env->cregs[6]);
} }
bool s390_cpu_has_restart_int(S390CPU *cpu) bool s390_cpu_has_restart_int(S390CPU *cpu)

View File

@ -12,10 +12,6 @@
#include "cpu.h" #include "cpu.h"
#include "kvm_s390x.h" #include "kvm_s390x.h"
void kvm_s390_service_interrupt(uint32_t parm)
{
}
void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code) void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code)
{ {
} }
@ -30,15 +26,6 @@ void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code)
{ {
} }
void kvm_s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
uint32_t io_int_parm, uint32_t io_int_word)
{
}
void kvm_s390_crw_mchk(void)
{
}
int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
{ {
return -ENOSYS; return -ENOSYS;

View File

@ -1034,7 +1034,7 @@ void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq)
inject_vcpu_irq_legacy(cs, irq); inject_vcpu_irq_legacy(cs, irq);
} }
static void __kvm_s390_floating_interrupt(struct kvm_s390_irq *irq) void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq)
{ {
struct kvm_s390_interrupt kvmint = {}; struct kvm_s390_interrupt kvmint = {};
int r; int r;
@ -1052,33 +1052,6 @@ static void __kvm_s390_floating_interrupt(struct kvm_s390_irq *irq)
} }
} }
void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq)
{
static bool use_flic = true;
int r;
if (use_flic) {
r = kvm_s390_inject_flic(irq);
if (r == -ENOSYS) {
use_flic = false;
}
if (!r) {
return;
}
}
__kvm_s390_floating_interrupt(irq);
}
void kvm_s390_service_interrupt(uint32_t parm)
{
struct kvm_s390_irq irq = {
.type = KVM_S390_INT_SERVICE,
.u.ext.ext_params = parm,
};
kvm_s390_floating_interrupt(&irq);
}
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code) void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code)
{ {
struct kvm_s390_irq irq = { struct kvm_s390_irq irq = {
@ -1690,10 +1663,10 @@ static int handle_tsch(S390CPU *cpu)
* If an I/O interrupt had been dequeued, we have to reinject it. * If an I/O interrupt had been dequeued, we have to reinject it.
*/ */
if (run->s390_tsch.dequeued) { if (run->s390_tsch.dequeued) {
kvm_s390_io_interrupt(run->s390_tsch.subchannel_id, s390_io_interrupt(run->s390_tsch.subchannel_id,
run->s390_tsch.subchannel_nr, run->s390_tsch.subchannel_nr,
run->s390_tsch.io_int_parm, run->s390_tsch.io_int_parm,
run->s390_tsch.io_int_word); run->s390_tsch.io_int_word);
} }
ret = 0; ret = 0;
} }
@ -1702,7 +1675,7 @@ static int handle_tsch(S390CPU *cpu)
static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar) static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar)
{ {
struct sysib_322 sysib; SysIB_322 sysib;
int del; int del;
if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) { if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) {
@ -1840,37 +1813,6 @@ bool kvm_arch_stop_on_emulation_error(CPUState *cpu)
return true; return true;
} }
void kvm_s390_io_interrupt(uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word)
{
struct kvm_s390_irq irq = {
.u.io.subchannel_id = subchannel_id,
.u.io.subchannel_nr = subchannel_nr,
.u.io.io_int_parm = io_int_parm,
.u.io.io_int_word = io_int_word,
};
if (io_int_word & IO_INT_WORD_AI) {
irq.type = KVM_S390_INT_IO(1, 0, 0, 0);
} else {
irq.type = KVM_S390_INT_IO(0, (subchannel_id & 0xff00) >> 8,
(subchannel_id & 0x0006),
subchannel_nr);
}
kvm_s390_floating_interrupt(&irq);
}
void kvm_s390_crw_mchk(void)
{
struct kvm_s390_irq irq = {
.type = KVM_S390_MCHK,
.u.mchk.cr14 = CR14_CHANNEL_REPORT_SC,
.u.mchk.mcic = s390_build_validity_mcic() | MCIC_SC_CP,
};
kvm_s390_floating_interrupt(&irq);
}
void kvm_s390_enable_css_support(S390CPU *cpu) void kvm_s390_enable_css_support(S390CPU *cpu)
{ {
int r; int r;
@ -2279,6 +2221,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
return; return;
} }
/* PTFF subfunctions might be indicated although kernel support missing */
if (!test_bit(S390_FEAT_MULTIPLE_EPOCH, model->features)) {
clear_bit(S390_FEAT_PTFF_QSIE, model->features);
clear_bit(S390_FEAT_PTFF_QTOUE, model->features);
clear_bit(S390_FEAT_PTFF_STOE, model->features);
clear_bit(S390_FEAT_PTFF_STOUE, model->features);
}
/* with cpu model support, CMM is only indicated if really available */ /* with cpu model support, CMM is only indicated if really available */
if (kvm_s390_cmma_available()) { if (kvm_s390_cmma_available()) {
set_bit(S390_FEAT_CMM, model->features); set_bit(S390_FEAT_CMM, model->features);

View File

@ -12,17 +12,12 @@
struct kvm_s390_irq; struct kvm_s390_irq;
void kvm_s390_floating_interrupt(struct kvm_s390_irq *irq); void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq);
void kvm_s390_service_interrupt(uint32_t parm);
void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq); void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq);
void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code); void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code);
int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf, int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf,
int len, bool is_write); int len, bool is_write);
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code); void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code);
void kvm_s390_io_interrupt(uint16_t subchannel_id,
uint16_t subchannel_nr, uint32_t io_int_parm,
uint32_t io_int_word);
void kvm_s390_crw_mchk(void);
int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
@ -44,7 +39,4 @@ void kvm_s390_crypto_reset(void);
void kvm_s390_restart_interrupt(S390CPU *cpu); void kvm_s390_restart_interrupt(S390CPU *cpu);
void kvm_s390_stop_interrupt(S390CPU *cpu); void kvm_s390_stop_interrupt(S390CPU *cpu);
/* implemented outside of target/s390x/ */
int kvm_s390_inject_flic(struct kvm_s390_irq *irq);
#endif /* KVM_S390X_H */ #endif /* KVM_S390X_H */

View File

@ -36,6 +36,10 @@
#include "hw/s390x/ebcdic.h" #include "hw/s390x/ebcdic.h"
#include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/s390-virtio-hcall.h"
#include "hw/s390x/sclp.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"
#endif #endif
/* #define DEBUG_HELPER */ /* #define DEBUG_HELPER */
@ -194,132 +198,148 @@ void HELPER(spt)(CPUS390XState *env, uint64_t time)
} }
/* Store System Information */ /* Store System Information */
uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1)
uint64_t r0, uint64_t r1)
{ {
const uintptr_t ra = GETPC();
const uint32_t sel1 = r0 & STSI_R0_SEL1_MASK;
const uint32_t sel2 = r1 & STSI_R1_SEL2_MASK;
const MachineState *ms = MACHINE(qdev_get_machine());
uint16_t total_cpus = 0, conf_cpus = 0, reserved_cpus = 0;
S390CPU *cpu = s390_env_get_cpu(env); S390CPU *cpu = s390_env_get_cpu(env);
int cc = 0; SysIB sysib = { 0 };
int sel1, sel2; int i, cc = 0;
if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 && if ((r0 & STSI_R0_FC_MASK) > STSI_R0_FC_LEVEL_3) {
((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) { /* invalid function code: no other checks are performed */
/* valid function code, invalid reserved bits */ return 3;
s390_program_interrupt(env, PGM_SPECIFICATION, 4, GETPC());
} }
sel1 = r0 & STSI_R0_SEL1_MASK; if ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK)) {
sel2 = r1 & STSI_R1_SEL2_MASK; s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
}
/* XXX: spec exception if sysib is not 4k-aligned */ if ((r0 & STSI_R0_FC_MASK) == STSI_R0_FC_CURRENT) {
/* query the current level: no further checks are performed */
env->regs[0] = STSI_R0_FC_LEVEL_3;
return 0;
}
switch (r0 & STSI_LEVEL_MASK) { if (a0 & ~TARGET_PAGE_MASK) {
case STSI_LEVEL_1: s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
}
/* count the cpus and split them into configured and reserved ones */
for (i = 0; i < ms->possible_cpus->len; i++) {
total_cpus++;
if (ms->possible_cpus->cpus[i].cpu) {
conf_cpus++;
} else {
reserved_cpus++;
}
}
/*
* In theory, we could report Level 1 / Level 2 as current. However,
* the Linux kernel will detect this as running under LPAR and assume
* that we have a sclp linemode console (which is always present on
* LPAR, but not the default for QEMU), therefore not displaying boot
* messages and making booting a Linux kernel under TCG harder.
*
* For now we fake the same SMP configuration on all levels.
*
* TODO: We could later make the level configurable via the machine
* and change defaults (linemode console) based on machine type
* and accelerator.
*/
switch (r0 & STSI_R0_FC_MASK) {
case STSI_R0_FC_LEVEL_1:
if ((sel1 == 1) && (sel2 == 1)) { if ((sel1 == 1) && (sel2 == 1)) {
/* Basic Machine Configuration */ /* Basic Machine Configuration */
struct sysib_111 sysib;
char type[5] = {}; char type[5] = {};
memset(&sysib, 0, sizeof(sysib)); ebcdic_put(sysib.sysib_111.manuf, "QEMU ", 16);
ebcdic_put(sysib.manuf, "QEMU ", 16);
/* same as machine type number in STORE CPU ID, but in EBCDIC */ /* same as machine type number in STORE CPU ID, but in EBCDIC */
snprintf(type, ARRAY_SIZE(type), "%X", cpu->model->def->type); snprintf(type, ARRAY_SIZE(type), "%X", cpu->model->def->type);
ebcdic_put(sysib.type, type, 4); ebcdic_put(sysib.sysib_111.type, type, 4);
/* model number (not stored in STORE CPU ID for z/Architecure) */ /* model number (not stored in STORE CPU ID for z/Architecure) */
ebcdic_put(sysib.model, "QEMU ", 16); ebcdic_put(sysib.sysib_111.model, "QEMU ", 16);
ebcdic_put(sysib.sequence, "QEMU ", 16); ebcdic_put(sysib.sysib_111.sequence, "QEMU ", 16);
ebcdic_put(sysib.plant, "QEMU", 4); ebcdic_put(sysib.sysib_111.plant, "QEMU", 4);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else if ((sel1 == 2) && (sel2 == 1)) { } else if ((sel1 == 2) && (sel2 == 1)) {
/* Basic Machine CPU */ /* Basic Machine CPU */
struct sysib_121 sysib; ebcdic_put(sysib.sysib_121.sequence, "QEMUQEMUQEMUQEMU", 16);
ebcdic_put(sysib.sysib_121.plant, "QEMU", 4);
memset(&sysib, 0, sizeof(sysib)); sysib.sysib_121.cpu_addr = cpu_to_be16(env->core_id);
/* XXX make different for different CPUs? */
ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
ebcdic_put(sysib.plant, "QEMU", 4);
stw_p(&sysib.cpu_addr, env->core_id);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else if ((sel1 == 2) && (sel2 == 2)) { } else if ((sel1 == 2) && (sel2 == 2)) {
/* Basic Machine CPUs */ /* Basic Machine CPUs */
struct sysib_122 sysib; sysib.sysib_122.capability = cpu_to_be32(0x443afc29);
sysib.sysib_122.total_cpus = cpu_to_be16(total_cpus);
memset(&sysib, 0, sizeof(sysib)); sysib.sysib_122.conf_cpus = cpu_to_be16(conf_cpus);
stl_p(&sysib.capability, 0x443afc29); sysib.sysib_122.reserved_cpus = cpu_to_be16(reserved_cpus);
/* XXX change when SMP comes */
stw_p(&sysib.total_cpus, 1);
stw_p(&sysib.active_cpus, 1);
stw_p(&sysib.standby_cpus, 0);
stw_p(&sysib.reserved_cpus, 0);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else { } else {
cc = 3; cc = 3;
} }
break; break;
case STSI_LEVEL_2: case STSI_R0_FC_LEVEL_2:
{ if ((sel1 == 2) && (sel2 == 1)) {
if ((sel1 == 2) && (sel2 == 1)) { /* LPAR CPU */
/* LPAR CPU */ ebcdic_put(sysib.sysib_221.sequence, "QEMUQEMUQEMUQEMU", 16);
struct sysib_221 sysib; ebcdic_put(sysib.sysib_221.plant, "QEMU", 4);
sysib.sysib_221.cpu_addr = cpu_to_be16(env->core_id);
memset(&sysib, 0, sizeof(sysib)); } else if ((sel1 == 2) && (sel2 == 2)) {
/* XXX make different for different CPUs? */ /* LPAR CPUs */
ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16); sysib.sysib_222.lcpuc = 0x80; /* dedicated */
ebcdic_put(sysib.plant, "QEMU", 4); sysib.sysib_222.total_cpus = cpu_to_be16(total_cpus);
stw_p(&sysib.cpu_addr, env->core_id); sysib.sysib_222.conf_cpus = cpu_to_be16(conf_cpus);
stw_p(&sysib.cpu_id, 0); sysib.sysib_222.reserved_cpus = cpu_to_be16(reserved_cpus);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib)); ebcdic_put(sysib.sysib_222.name, "QEMU ", 8);
} else if ((sel1 == 2) && (sel2 == 2)) { sysib.sysib_222.caf = cpu_to_be32(1000);
/* LPAR CPUs */ sysib.sysib_222.dedicated_cpus = cpu_to_be16(conf_cpus);
struct sysib_222 sysib; } else {
cc = 3;
memset(&sysib, 0, sizeof(sysib));
stw_p(&sysib.lpar_num, 0);
sysib.lcpuc = 0;
/* XXX change when SMP comes */
stw_p(&sysib.total_cpus, 1);
stw_p(&sysib.conf_cpus, 1);
stw_p(&sysib.standby_cpus, 0);
stw_p(&sysib.reserved_cpus, 0);
ebcdic_put(sysib.name, "QEMU ", 8);
stl_p(&sysib.caf, 1000);
stw_p(&sysib.dedicated_cpus, 0);
stw_p(&sysib.shared_cpus, 0);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else {
cc = 3;
}
break;
} }
case STSI_LEVEL_3: break;
{ case STSI_R0_FC_LEVEL_3:
if ((sel1 == 2) && (sel2 == 2)) { if ((sel1 == 2) && (sel2 == 2)) {
/* VM CPUs */ /* VM CPUs */
struct sysib_322 sysib; sysib.sysib_322.count = 1;
sysib.sysib_322.vm[0].total_cpus = cpu_to_be16(total_cpus);
sysib.sysib_322.vm[0].conf_cpus = cpu_to_be16(conf_cpus);
sysib.sysib_322.vm[0].reserved_cpus = cpu_to_be16(reserved_cpus);
sysib.sysib_322.vm[0].caf = cpu_to_be32(1000);
/* Linux kernel uses this to distinguish us from z/VM */
ebcdic_put(sysib.sysib_322.vm[0].cpi, "KVM/Linux ", 16);
sysib.sysib_322.vm[0].ext_name_encoding = 2; /* UTF-8 */
memset(&sysib, 0, sizeof(sysib)); /* If our VM has a name, use the real name */
sysib.count = 1; if (qemu_name) {
/* XXX change when SMP comes */ memset(sysib.sysib_322.vm[0].name, 0x40,
stw_p(&sysib.vm[0].total_cpus, 1); sizeof(sysib.sysib_322.vm[0].name));
stw_p(&sysib.vm[0].conf_cpus, 1); ebcdic_put(sysib.sysib_322.vm[0].name, qemu_name,
stw_p(&sysib.vm[0].standby_cpus, 0); MIN(sizeof(sysib.sysib_322.vm[0].name),
stw_p(&sysib.vm[0].reserved_cpus, 0); strlen(qemu_name)));
ebcdic_put(sysib.vm[0].name, "KVMguest", 8); strncpy((char *)sysib.sysib_322.ext_names[0], qemu_name,
stl_p(&sysib.vm[0].caf, 1000); sizeof(sysib.sysib_322.ext_names[0]));
ebcdic_put(sysib.vm[0].cpi, "KVM/Linux ", 16);
cpu_physical_memory_write(a0, &sysib, sizeof(sysib));
} else { } else {
cc = 3; ebcdic_put(sysib.sysib_322.vm[0].name, "TCGguest", 8);
strcpy((char *)sysib.sysib_322.ext_names[0], "TCGguest");
} }
break;
/* add the uuid */
memcpy(sysib.sysib_322.vm[0].uuid, &qemu_uuid,
sizeof(sysib.sysib_322.vm[0].uuid));
} else {
cc = 3;
} }
case STSI_LEVEL_CURRENT:
env->regs[0] = STSI_LEVEL_3;
break;
default:
cc = 3;
break; break;
} }
if (cc == 0) {
if (s390_cpu_virt_mem_write(cpu, a0, 0, &sysib, sizeof(sysib))) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
}
}
return cc; return cc;
} }
@ -429,6 +449,59 @@ void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
} }
uint32_t HELPER(tpi)(CPUS390XState *env, uint64_t addr)
{
const uintptr_t ra = GETPC();
S390CPU *cpu = s390_env_get_cpu(env);
QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
QEMUS390FlicIO *io = NULL;
LowCore *lowcore;
if (addr & 0x3) {
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
}
qemu_mutex_lock_iothread();
io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]);
if (!io) {
qemu_mutex_unlock_iothread();
return 0;
}
if (addr) {
struct {
uint16_t id;
uint16_t nr;
uint32_t parm;
} intc = {
.id = cpu_to_be16(io->id),
.nr = cpu_to_be16(io->nr),
.parm = cpu_to_be32(io->parm),
};
if (s390_cpu_virt_mem_write(cpu, addr, 0, &intc, sizeof(intc))) {
/* writing failed, reinject and properly clean up */
s390_io_interrupt(io->id, io->nr, io->parm, io->word);
qemu_mutex_unlock_iothread();
g_free(io);
s390_cpu_virt_mem_handle_exc(cpu, ra);
return 0;
}
} else {
/* no protection applies */
lowcore = cpu_map_lowcore(env);
lowcore->subchannel_id = cpu_to_be16(io->id);
lowcore->subchannel_nr = cpu_to_be16(io->nr);
lowcore->io_int_parm = cpu_to_be32(io->parm);
lowcore->io_int_word = cpu_to_be32(io->word);
cpu_unmap_lowcore(lowcore);
}
g_free(io);
qemu_mutex_unlock_iothread();
return 1;
}
void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst) void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
{ {
S390CPU *cpu = s390_env_get_cpu(env); S390CPU *cpu = s390_env_get_cpu(env);
@ -560,3 +633,91 @@ uint32_t HELPER(stfle)(CPUS390XState *env, uint64_t addr)
env->regs[0] = deposit64(env->regs[0], 0, 8, (max_bytes / 8) - 1); env->regs[0] = deposit64(env->regs[0], 0, 8, (max_bytes / 8) - 1);
return count_bytes >= max_bytes ? 0 : 3; return count_bytes >= max_bytes ? 0 : 3;
} }
#ifndef CONFIG_USER_ONLY
/*
* Note: we ignore any return code of the functions called for the pci
* instructions, as the only time they return !0 is when the stub is
* called, and in that case we didn't even offer the zpci facility.
* The only exception is SIC, where program checks need to be handled
* by the caller.
*/
void HELPER(clp)(CPUS390XState *env, uint32_t r2)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
clp_service_call(cpu, r2, GETPC());
qemu_mutex_unlock_iothread();
}
void HELPER(pcilg)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
pcilg_service_call(cpu, r1, r2, GETPC());
qemu_mutex_unlock_iothread();
}
void HELPER(pcistg)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
pcistg_service_call(cpu, r1, r2, GETPC());
qemu_mutex_unlock_iothread();
}
void HELPER(stpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba,
uint32_t ar)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
stpcifc_service_call(cpu, r1, fiba, ar, GETPC());
qemu_mutex_unlock_iothread();
}
void HELPER(sic)(CPUS390XState *env, uint64_t r1, uint64_t r3)
{
int r;
qemu_mutex_lock_iothread();
r = css_do_sic(env, (r3 >> 27) & 0x7, r1 & 0xffff);
qemu_mutex_unlock_iothread();
/* css_do_sic() may actually return a PGM_xxx value to inject */
if (r) {
s390_program_interrupt(env, -r, 4, GETPC());
}
}
void HELPER(rpcit)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
rpcit_service_call(cpu, r1, r2, GETPC());
qemu_mutex_unlock_iothread();
}
void HELPER(pcistb)(CPUS390XState *env, uint32_t r1, uint32_t r3,
uint64_t gaddr, uint32_t ar)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
pcistb_service_call(cpu, r1, r3, gaddr, ar, GETPC());
qemu_mutex_unlock_iothread();
}
void HELPER(mpcifc)(CPUS390XState *env, uint32_t r1, uint64_t fiba,
uint32_t ar)
{
S390CPU *cpu = s390_env_get_cpu(env);
qemu_mutex_lock_iothread();
mpcifc_service_call(cpu, r1, fiba, ar, GETPC());
qemu_mutex_unlock_iothread();
}
#endif

View File

@ -4199,6 +4199,14 @@ static ExitStatus op_stcrw(DisasContext *s, DisasOps *o)
return NO_EXIT; return NO_EXIT;
} }
static ExitStatus op_tpi(DisasContext *s, DisasOps *o)
{
check_privileged(s);
gen_helper_tpi(cc_op, cpu_env, o->addr1);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_tsch(DisasContext *s, DisasOps *o) static ExitStatus op_tsch(DisasContext *s, DisasOps *o)
{ {
check_privileged(s); check_privileged(s);
@ -4777,6 +4785,106 @@ static ExitStatus op_zero2(DisasContext *s, DisasOps *o)
return NO_EXIT; return NO_EXIT;
} }
#ifndef CONFIG_USER_ONLY
static ExitStatus op_clp(DisasContext *s, DisasOps *o)
{
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_clp(cpu_env, r2);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_pcilg(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_pcilg(cpu_env, r1, r2);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_pcistg(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_pcistg(cpu_env, r1, r2);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_stpcifc(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2));
check_privileged(s);
gen_helper_stpcifc(cpu_env, r1, o->addr1, ar);
tcg_temp_free_i32(ar);
tcg_temp_free_i32(r1);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_sic(DisasContext *s, DisasOps *o)
{
check_privileged(s);
gen_helper_sic(cpu_env, o->in1, o->in2);
return NO_EXIT;
}
static ExitStatus op_rpcit(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
check_privileged(s);
gen_helper_rpcit(cpu_env, r1, r2);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_pcistb(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2));
check_privileged(s);
gen_helper_pcistb(cpu_env, r1, r3, o->addr1, ar);
tcg_temp_free_i32(ar);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_mpcifc(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2));
check_privileged(s);
gen_helper_mpcifc(cpu_env, r1, o->addr1, ar);
tcg_temp_free_i32(ar);
tcg_temp_free_i32(r1);
set_cc_static(s);
return NO_EXIT;
}
#endif
/* ====================================================================== */ /* ====================================================================== */
/* The "Cc OUTput" generators. Given the generated output (and in some cases /* The "Cc OUTput" generators. Given the generated output (and in some cases
the original inputs), update the various cc data structures in order to the original inputs), update the various cc data structures in order to
@ -5708,6 +5816,8 @@ enum DisasInsnEnum {
#define FAC_MSA4 S390_FEAT_MSA_EXT_4 /* msa-extension-4 facility */ #define FAC_MSA4 S390_FEAT_MSA_EXT_4 /* msa-extension-4 facility */
#define FAC_MSA5 S390_FEAT_MSA_EXT_5 /* msa-extension-5 facility */ #define FAC_MSA5 S390_FEAT_MSA_EXT_5 /* msa-extension-5 facility */
#define FAC_ECT S390_FEAT_EXTRACT_CPU_TIME #define FAC_ECT S390_FEAT_EXTRACT_CPU_TIME
#define FAC_PCI S390_FEAT_ZPCI /* z/PCI facility */
#define FAC_AIS S390_FEAT_ADAPTER_INT_SUPPRESSION
static const DisasInsn insn_info[] = { static const DisasInsn insn_info[] = {
#include "insn-data.def" #include "insn-data.def"