mirror of https://github.com/xqemu/xqemu.git
s390x patches for 2.3.
Highlight is support for PCI devices on s390x. Otherwise, performance improvements (register sync) and small cleanups. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJUs5irAAoJEN7Pa5PG8C+vP2EP/RMQVXd3uLAwwEW03F/bYFH9 3rOjxoO7+uLZn4N0LPS5APlokX+F5UEyR9KZJlM7NlSpusuivxkd4f5CRfe1/OWK IFXenJ0XxvnLfCI36UbD9X5IsxGFtDPHtta9IN/isBoIs53IUvYDN9YM4UQxmjVU NODgaJrxmqi38yN4W6b/me3/HMEz13IfUl7LcCY5bV6ehqAdh7a35uf7hytkgDjy 8mz2uBs/IKRBEh92HjZjlOXIuMiwvsKHMVjUGfAMmypMuipO/D1/NiWynFm+PnQU QZxRINvCN47J8A7A4umnxWzMI2YqnrBpPIQ9Bl/Butc/R8Ls+kYZ7LpU1w6h+t2t 6qDsx/3Y4ELDSIKlmz0p8MllDM2/4+lA4xkVF1XHPTWHASSpYaWmJxrkLK273WJt eVytBFtb3XQ3r5lXpQg6G1pxetplJy2mELhVneq116ZvzT9YvLhouetuHxt5RZ6f P2vyzuVExrkHXitad8xz4qViPF+u75LA93FGMav8zrxqH6GV2pHCVLY34/daSBEG LXrPG1p8OfF4sPqYyaSGEz3A6uUJTnHo6srEaJZRYxQxAlclGLYLdl1CXsOrjP0g q83+Z6kS/XBO0E55z/f25vSBak4CXeRvikZWKBuoGQ8F0ikHgTvXg1o6sYiOzPnN XBMK4o2pL4ZoiIv/q8pF =WEEj -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20150112-v3' into staging s390x patches for 2.3. Highlight is support for PCI devices on s390x. Otherwise, performance improvements (register sync) and small cleanups. # gpg: Signature made Mon 12 Jan 2015 09:49:31 GMT using RSA key ID C6F02FAF # gpg: Good signature from "Cornelia Huck <huckc@linux.vnet.ibm.com>" # gpg: aka "Cornelia Huck <cornelia.huck@de.ibm.com>" * remotes/cohuck/tags/s390x-20150112-v3: kvm: extend kvm_irqchip_add_msi_route to work on s390 s390: implement pci instructions s390: Add PCI bus support s390x/kvm: avoid syscalls by syncing registers with kvm_run s390x/kvm: sync register support helper function s390x/css: Clean up unnecessary CONFIG_USER_ONLY wrappers s390x/ccw: fix oddity in machine class init Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5435f1d77e
|
@ -538,6 +538,7 @@ S390 Virtio
|
||||||
M: Alexander Graf <agraf@suse.de>
|
M: Alexander Graf <agraf@suse.de>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: hw/s390x/s390-*.c
|
F: hw/s390x/s390-*.c
|
||||||
|
X: hw/s390x/*pci*.[hc]
|
||||||
|
|
||||||
S390 Virtio-ccw
|
S390 Virtio-ccw
|
||||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
|
@ -548,6 +549,7 @@ F: hw/s390x/s390-virtio-ccw.c
|
||||||
F: hw/s390x/css.[hc]
|
F: hw/s390x/css.[hc]
|
||||||
F: hw/s390x/sclp*.[hc]
|
F: hw/s390x/sclp*.[hc]
|
||||||
F: hw/s390x/ipl*.[hc]
|
F: hw/s390x/ipl*.[hc]
|
||||||
|
F: hw/s390x/*pci*.[hc]
|
||||||
F: include/hw/s390x/
|
F: include/hw/s390x/
|
||||||
F: pc-bios/s390-ccw/
|
F: pc-bios/s390-ccw/
|
||||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
include pci.mak
|
||||||
CONFIG_VIRTIO=y
|
CONFIG_VIRTIO=y
|
||||||
CONFIG_SCLPCONSOLE=y
|
CONFIG_SCLPCONSOLE=y
|
||||||
CONFIG_S390_FLIC=y
|
CONFIG_S390_FLIC=y
|
||||||
|
|
|
@ -8,3 +8,4 @@ obj-y += ipl.o
|
||||||
obj-y += css.o
|
obj-y += css.o
|
||||||
obj-y += s390-virtio-ccw.o
|
obj-y += s390-virtio-ccw.o
|
||||||
obj-y += virtio-ccw.o
|
obj-y += virtio-ccw.o
|
||||||
|
obj-y += s390-pci-bus.o s390-pci-inst.o
|
||||||
|
|
|
@ -1299,6 +1299,11 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid)
|
||||||
/* TODO */
|
/* TODO */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void css_generate_css_crws(uint8_t cssid)
|
||||||
|
{
|
||||||
|
css_queue_crw(CRW_RSC_CSS, 0, 0, cssid);
|
||||||
|
}
|
||||||
|
|
||||||
int css_enable_mcsse(void)
|
int css_enable_mcsse(void)
|
||||||
{
|
{
|
||||||
trace_css_enable_facility("mcsse");
|
trace_css_enable_facility("mcsse");
|
||||||
|
|
|
@ -101,6 +101,7 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid);
|
||||||
void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
|
void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
|
||||||
int hotplugged, int add);
|
int hotplugged, int add);
|
||||||
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
|
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
|
||||||
|
void css_generate_css_crws(uint8_t cssid);
|
||||||
void css_adapter_interrupt(uint8_t isc);
|
void css_adapter_interrupt(uint8_t isc);
|
||||||
|
|
||||||
#define CSS_IO_ADAPTER_VIRTIO 1
|
#define CSS_IO_ADAPTER_VIRTIO 1
|
||||||
|
|
|
@ -0,0 +1,591 @@
|
||||||
|
/*
|
||||||
|
* s390 PCI BUS
|
||||||
|
*
|
||||||
|
* Copyright 2014 IBM Corp.
|
||||||
|
* Author(s): Frank Blaschka <frank.blaschka@de.ibm.com>
|
||||||
|
* Hong Bo Li <lihbbj@cn.ibm.com>
|
||||||
|
* Yi Min Zhao <zyimin@cn.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "s390-pci-bus.h"
|
||||||
|
#include <hw/pci/pci_bus.h>
|
||||||
|
#include <hw/pci/msi.h>
|
||||||
|
#include <qemu/error-report.h>
|
||||||
|
|
||||||
|
/* #define DEBUG_S390PCI_BUS */
|
||||||
|
#ifdef DEBUG_S390PCI_BUS
|
||||||
|
#define DPRINTF(fmt, ...) \
|
||||||
|
do { fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); } while (0)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(fmt, ...) \
|
||||||
|
do { } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int chsc_sei_nt2_get_event(void *res)
|
||||||
|
{
|
||||||
|
ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res;
|
||||||
|
PciCcdfAvail *accdf;
|
||||||
|
PciCcdfErr *eccdf;
|
||||||
|
int rc = 1;
|
||||||
|
SeiContainer *sei_cont;
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(
|
||||||
|
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sei_cont = QTAILQ_FIRST(&s->pending_sei);
|
||||||
|
if (sei_cont) {
|
||||||
|
QTAILQ_REMOVE(&s->pending_sei, sei_cont, link);
|
||||||
|
nt2_res->nt = 2;
|
||||||
|
nt2_res->cc = sei_cont->cc;
|
||||||
|
switch (sei_cont->cc) {
|
||||||
|
case 1: /* error event */
|
||||||
|
eccdf = (PciCcdfErr *)nt2_res->ccdf;
|
||||||
|
eccdf->fid = cpu_to_be32(sei_cont->fid);
|
||||||
|
eccdf->fh = cpu_to_be32(sei_cont->fh);
|
||||||
|
eccdf->e = cpu_to_be32(sei_cont->e);
|
||||||
|
eccdf->faddr = cpu_to_be64(sei_cont->faddr);
|
||||||
|
eccdf->pec = cpu_to_be16(sei_cont->pec);
|
||||||
|
break;
|
||||||
|
case 2: /* availability event */
|
||||||
|
accdf = (PciCcdfAvail *)nt2_res->ccdf;
|
||||||
|
accdf->fid = cpu_to_be32(sei_cont->fid);
|
||||||
|
accdf->fh = cpu_to_be32(sei_cont->fh);
|
||||||
|
accdf->pec = cpu_to_be16(sei_cont->pec);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
g_free(sei_cont);
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chsc_sei_nt2_have_event(void)
|
||||||
|
{
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(
|
||||||
|
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !QTAILQ_EMPTY(&s->pending_sei);
|
||||||
|
}
|
||||||
|
|
||||||
|
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid)
|
||||||
|
{
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
int i;
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(
|
||||||
|
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PCI_SLOT_MAX; i++) {
|
||||||
|
pbdev = &s->pbdev[i];
|
||||||
|
if ((pbdev->fh != 0) && (pbdev->fid == fid)) {
|
||||||
|
return pbdev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void s390_pci_sclp_configure(int configure, SCCB *sccb)
|
||||||
|
{
|
||||||
|
PciCfgSccb *psccb = (PciCfgSccb *)sccb;
|
||||||
|
S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid));
|
||||||
|
uint16_t rc;
|
||||||
|
|
||||||
|
if (pbdev) {
|
||||||
|
if ((configure == 1 && pbdev->configured == true) ||
|
||||||
|
(configure == 0 && pbdev->configured == false)) {
|
||||||
|
rc = SCLP_RC_NO_ACTION_REQUIRED;
|
||||||
|
} else {
|
||||||
|
pbdev->configured = !pbdev->configured;
|
||||||
|
rc = SCLP_RC_NORMAL_COMPLETION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DPRINTF("sclp config %d no dev found\n", configure);
|
||||||
|
rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
psccb->header.response_code = cpu_to_be16(rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t s390_pci_get_pfid(PCIDevice *pdev)
|
||||||
|
{
|
||||||
|
return PCI_SLOT(pdev->devfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t s390_pci_get_pfh(PCIDevice *pdev)
|
||||||
|
{
|
||||||
|
return PCI_SLOT(pdev->devfn) | FH_VIRT;
|
||||||
|
}
|
||||||
|
|
||||||
|
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx)
|
||||||
|
{
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
int i;
|
||||||
|
int j = 0;
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(
|
||||||
|
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PCI_SLOT_MAX; i++) {
|
||||||
|
pbdev = &s->pbdev[i];
|
||||||
|
|
||||||
|
if (pbdev->fh == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == idx) {
|
||||||
|
return pbdev;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh)
|
||||||
|
{
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
int i;
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(
|
||||||
|
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PCI_SLOT_MAX; i++) {
|
||||||
|
pbdev = &s->pbdev[i];
|
||||||
|
if (pbdev->fh == fh) {
|
||||||
|
return pbdev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh,
|
||||||
|
uint32_t fid, uint64_t faddr, uint32_t e)
|
||||||
|
{
|
||||||
|
SeiContainer *sei_cont = g_malloc0(sizeof(SeiContainer));
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(
|
||||||
|
object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL));
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sei_cont->fh = fh;
|
||||||
|
sei_cont->fid = fid;
|
||||||
|
sei_cont->cc = cc;
|
||||||
|
sei_cont->pec = pec;
|
||||||
|
sei_cont->faddr = faddr;
|
||||||
|
sei_cont->e = e;
|
||||||
|
|
||||||
|
QTAILQ_INSERT_TAIL(&s->pending_sei, sei_cont, link);
|
||||||
|
css_generate_css_crws(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
|
||||||
|
uint32_t fid)
|
||||||
|
{
|
||||||
|
s390_pci_generate_event(2, pec, fh, fid, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh,
|
||||||
|
uint32_t fid, uint64_t faddr,
|
||||||
|
uint32_t e)
|
||||||
|
{
|
||||||
|
s390_pci_generate_event(1, pec, fh, fid, faddr, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pci_set_irq(void *opaque, int irq, int level)
|
||||||
|
{
|
||||||
|
/* nothing to do */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s390_pci_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||||
|
{
|
||||||
|
/* nothing to do */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t s390_pci_get_table_origin(uint64_t iota)
|
||||||
|
{
|
||||||
|
return iota & ~ZPCI_IOTA_RTTO_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int calc_rtx(dma_addr_t ptr)
|
||||||
|
{
|
||||||
|
return ((unsigned long) ptr >> ZPCI_RT_SHIFT) & ZPCI_INDEX_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int calc_sx(dma_addr_t ptr)
|
||||||
|
{
|
||||||
|
return ((unsigned long) ptr >> ZPCI_ST_SHIFT) & ZPCI_INDEX_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int calc_px(dma_addr_t ptr)
|
||||||
|
{
|
||||||
|
return ((unsigned long) ptr >> PAGE_SHIFT) & ZPCI_PT_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_rt_sto(uint64_t entry)
|
||||||
|
{
|
||||||
|
return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_RTX)
|
||||||
|
? (entry & ZPCI_RTE_ADDR_MASK)
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_st_pto(uint64_t entry)
|
||||||
|
{
|
||||||
|
return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_SX)
|
||||||
|
? (entry & ZPCI_STE_ADDR_MASK)
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t s390_guest_io_table_walk(uint64_t guest_iota,
|
||||||
|
uint64_t guest_dma_address)
|
||||||
|
{
|
||||||
|
uint64_t sto_a, pto_a, px_a;
|
||||||
|
uint64_t sto, pto, pte;
|
||||||
|
uint32_t rtx, sx, px;
|
||||||
|
|
||||||
|
rtx = calc_rtx(guest_dma_address);
|
||||||
|
sx = calc_sx(guest_dma_address);
|
||||||
|
px = calc_px(guest_dma_address);
|
||||||
|
|
||||||
|
sto_a = guest_iota + rtx * sizeof(uint64_t);
|
||||||
|
sto = ldq_phys(&address_space_memory, sto_a);
|
||||||
|
sto = get_rt_sto(sto);
|
||||||
|
if (!sto) {
|
||||||
|
pte = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pto_a = sto + sx * sizeof(uint64_t);
|
||||||
|
pto = ldq_phys(&address_space_memory, pto_a);
|
||||||
|
pto = get_st_pto(pto);
|
||||||
|
if (!pto) {
|
||||||
|
pte = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
px_a = pto + px * sizeof(uint64_t);
|
||||||
|
pte = ldq_phys(&address_space_memory, px_a);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return pte;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
uint64_t pte;
|
||||||
|
uint32_t flags;
|
||||||
|
S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, mr);
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)
|
||||||
|
->qbus.parent);
|
||||||
|
IOMMUTLBEntry ret = {
|
||||||
|
.target_as = &address_space_memory,
|
||||||
|
.iova = 0,
|
||||||
|
.translated_addr = 0,
|
||||||
|
.addr_mask = ~(hwaddr)0,
|
||||||
|
.perm = IOMMU_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr);
|
||||||
|
|
||||||
|
/* s390 does not have an APIC mapped to main storage so we use
|
||||||
|
* a separate AddressSpace only for msix notifications
|
||||||
|
*/
|
||||||
|
if (addr == ZPCI_MSI_ADDR) {
|
||||||
|
ret.target_as = &s->msix_notify_as;
|
||||||
|
ret.iova = addr;
|
||||||
|
ret.translated_addr = addr;
|
||||||
|
ret.addr_mask = 0xfff;
|
||||||
|
ret.perm = IOMMU_RW;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pbdev->g_iota) {
|
||||||
|
pbdev->error_state = true;
|
||||||
|
pbdev->lgstg_blocked = true;
|
||||||
|
s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
|
||||||
|
addr, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr < pbdev->pba || addr > pbdev->pal) {
|
||||||
|
pbdev->error_state = true;
|
||||||
|
pbdev->lgstg_blocked = true;
|
||||||
|
s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
|
||||||
|
addr, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
|
||||||
|
addr);
|
||||||
|
|
||||||
|
if (!pte) {
|
||||||
|
pbdev->error_state = true;
|
||||||
|
pbdev->lgstg_blocked = true;
|
||||||
|
s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
|
||||||
|
addr, ERR_EVENT_Q_BIT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
ret.perm = IOMMU_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionIOMMUOps s390_iommu_ops = {
|
||||||
|
.translate = s390_translate_iommu,
|
||||||
|
};
|
||||||
|
|
||||||
|
static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
||||||
|
{
|
||||||
|
S390pciState *s = opaque;
|
||||||
|
|
||||||
|
return &s->pbdev[PCI_SLOT(devfn)].as;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set)
|
||||||
|
{
|
||||||
|
uint8_t ind_old, ind_new;
|
||||||
|
hwaddr len = 1;
|
||||||
|
uint8_t *ind_addr;
|
||||||
|
|
||||||
|
ind_addr = cpu_physical_memory_map(ind_loc, &len, 1);
|
||||||
|
if (!ind_addr) {
|
||||||
|
s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
ind_old = *ind_addr;
|
||||||
|
ind_new = ind_old | to_be_set;
|
||||||
|
} while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old);
|
||||||
|
cpu_physical_memory_unmap(ind_addr, len, 1, len);
|
||||||
|
|
||||||
|
return ind_old;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint32_t io_int_word;
|
||||||
|
uint32_t fid = data >> ZPCI_MSI_VEC_BITS;
|
||||||
|
uint32_t vec = data & ZPCI_MSI_VEC_MASK;
|
||||||
|
uint64_t ind_bit;
|
||||||
|
uint32_t sum_bit;
|
||||||
|
uint32_t e = 0;
|
||||||
|
|
||||||
|
DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec);
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fid(fid);
|
||||||
|
if (!pbdev) {
|
||||||
|
e |= (vec << ERR_EVENT_MVN_OFFSET);
|
||||||
|
s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ind_bit = pbdev->routes.adapter.ind_offset;
|
||||||
|
sum_bit = pbdev->routes.adapter.summary_offset;
|
||||||
|
|
||||||
|
set_ind_atomic(pbdev->routes.adapter.ind_addr + (ind_bit + vec) / 8,
|
||||||
|
0x80 >> ((ind_bit + vec) % 8));
|
||||||
|
if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8,
|
||||||
|
0x80 >> (sum_bit % 8))) {
|
||||||
|
io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI;
|
||||||
|
s390_io_interrupt(0, 0, 0, io_int_word);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
|
{
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps s390_msi_ctrl_ops = {
|
||||||
|
.write = s390_msi_ctrl_write,
|
||||||
|
.read = s390_msi_ctrl_read,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void s390_pcihost_init_as(S390pciState *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < PCI_SLOT_MAX; i++) {
|
||||||
|
memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s),
|
||||||
|
&s390_iommu_ops, "iommu-s390", UINT64_MAX);
|
||||||
|
address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci");
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_init_io(&s->msix_notify_mr, OBJECT(s),
|
||||||
|
&s390_msi_ctrl_ops, s, "msix-s390", UINT64_MAX);
|
||||||
|
address_space_init(&s->msix_notify_as, &s->msix_notify_mr, "msix-pci");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s390_pcihost_init(SysBusDevice *dev)
|
||||||
|
{
|
||||||
|
PCIBus *b;
|
||||||
|
BusState *bus;
|
||||||
|
PCIHostState *phb = PCI_HOST_BRIDGE(dev);
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(dev);
|
||||||
|
|
||||||
|
DPRINTF("host_init\n");
|
||||||
|
|
||||||
|
b = pci_register_bus(DEVICE(dev), NULL,
|
||||||
|
s390_pci_set_irq, s390_pci_map_irq, NULL,
|
||||||
|
get_system_memory(), get_system_io(), 0, 64,
|
||||||
|
TYPE_PCI_BUS);
|
||||||
|
s390_pcihost_init_as(s);
|
||||||
|
pci_setup_iommu(b, s390_pci_dma_iommu, s);
|
||||||
|
|
||||||
|
bus = BUS(b);
|
||||||
|
qbus_set_hotplug_handler(bus, DEVICE(dev), NULL);
|
||||||
|
phb->bus = b;
|
||||||
|
QTAILQ_INIT(&s->pending_sei);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev)
|
||||||
|
{
|
||||||
|
uint8_t pos;
|
||||||
|
uint16_t ctrl;
|
||||||
|
uint32_t table, pba;
|
||||||
|
|
||||||
|
pos = pci_find_capability(pbdev->pdev, PCI_CAP_ID_MSIX);
|
||||||
|
if (!pos) {
|
||||||
|
pbdev->msix.available = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl = pci_host_config_read_common(pbdev->pdev, pos + PCI_CAP_FLAGS,
|
||||||
|
pci_config_size(pbdev->pdev), sizeof(ctrl));
|
||||||
|
table = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_TABLE,
|
||||||
|
pci_config_size(pbdev->pdev), sizeof(table));
|
||||||
|
pba = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_PBA,
|
||||||
|
pci_config_size(pbdev->pdev), sizeof(pba));
|
||||||
|
|
||||||
|
pbdev->msix.table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
|
||||||
|
pbdev->msix.table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
|
||||||
|
pbdev->msix.pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
|
||||||
|
pbdev->msix.pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
|
||||||
|
pbdev->msix.entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
|
||||||
|
pbdev->msix.available = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,
|
||||||
|
DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev)
|
||||||
|
->qbus.parent);
|
||||||
|
|
||||||
|
pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)];
|
||||||
|
|
||||||
|
pbdev->fid = s390_pci_get_pfid(pci_dev);
|
||||||
|
pbdev->pdev = pci_dev;
|
||||||
|
pbdev->configured = true;
|
||||||
|
pbdev->fh = s390_pci_get_pfh(pci_dev);
|
||||||
|
|
||||||
|
s390_pcihost_setup_msix(pbdev);
|
||||||
|
|
||||||
|
if (dev->hotplugged) {
|
||||||
|
s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY,
|
||||||
|
pbdev->fh, pbdev->fid);
|
||||||
|
s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED,
|
||||||
|
pbdev->fh, pbdev->fid);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
|
||||||
|
DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
PCIDevice *pci_dev = PCI_DEVICE(dev);
|
||||||
|
S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev)
|
||||||
|
->qbus.parent);
|
||||||
|
S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)];
|
||||||
|
|
||||||
|
if (pbdev->configured) {
|
||||||
|
pbdev->configured = false;
|
||||||
|
s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
|
||||||
|
pbdev->fh, pbdev->fid);
|
||||||
|
}
|
||||||
|
|
||||||
|
s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
|
||||||
|
pbdev->fh, pbdev->fid);
|
||||||
|
pbdev->fh = 0;
|
||||||
|
pbdev->fid = 0;
|
||||||
|
pbdev->pdev = NULL;
|
||||||
|
object_unparent(OBJECT(pci_dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_pcihost_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||||
|
|
||||||
|
dc->cannot_instantiate_with_device_add_yet = true;
|
||||||
|
k->init = s390_pcihost_init;
|
||||||
|
hc->plug = s390_pcihost_hot_plug;
|
||||||
|
hc->unplug = s390_pcihost_hot_unplug;
|
||||||
|
msi_supported = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo s390_pcihost_info = {
|
||||||
|
.name = TYPE_S390_PCI_HOST_BRIDGE,
|
||||||
|
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||||
|
.instance_size = sizeof(S390pciState),
|
||||||
|
.class_init = s390_pcihost_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_HOTPLUG_HANDLER },
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void s390_pci_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&s390_pcihost_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(s390_pci_register_types)
|
|
@ -0,0 +1,251 @@
|
||||||
|
/*
|
||||||
|
* s390 PCI BUS definitions
|
||||||
|
*
|
||||||
|
* Copyright 2014 IBM Corp.
|
||||||
|
* Author(s): Frank Blaschka <frank.blaschka@de.ibm.com>
|
||||||
|
* Hong Bo Li <lihbbj@cn.ibm.com>
|
||||||
|
* Yi Min Zhao <zyimin@cn.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_S390_PCI_BUS_H
|
||||||
|
#define HW_S390_PCI_BUS_H
|
||||||
|
|
||||||
|
#include <hw/pci/pci.h>
|
||||||
|
#include <hw/pci/pci_host.h>
|
||||||
|
#include "hw/s390x/sclp.h"
|
||||||
|
#include "hw/s390x/s390_flic.h"
|
||||||
|
#include "hw/s390x/css.h"
|
||||||
|
|
||||||
|
#define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost"
|
||||||
|
#define FH_VIRT 0x00ff0000
|
||||||
|
#define ENABLE_BIT_OFFSET 31
|
||||||
|
#define S390_PCIPT_ADAPTER 2
|
||||||
|
|
||||||
|
#define S390_PCI_HOST_BRIDGE(obj) \
|
||||||
|
OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE)
|
||||||
|
|
||||||
|
#define HP_EVENT_TO_CONFIGURED 0x0301
|
||||||
|
#define HP_EVENT_RESERVED_TO_STANDBY 0x0302
|
||||||
|
#define HP_EVENT_CONFIGURED_TO_STBRES 0x0304
|
||||||
|
#define HP_EVENT_STANDBY_TO_RESERVED 0x0308
|
||||||
|
|
||||||
|
#define ERR_EVENT_INVALAS 0x1
|
||||||
|
#define ERR_EVENT_OORANGE 0x2
|
||||||
|
#define ERR_EVENT_INVALTF 0x3
|
||||||
|
#define ERR_EVENT_TPROTE 0x4
|
||||||
|
#define ERR_EVENT_APROTE 0x5
|
||||||
|
#define ERR_EVENT_KEYE 0x6
|
||||||
|
#define ERR_EVENT_INVALTE 0x7
|
||||||
|
#define ERR_EVENT_INVALTL 0x8
|
||||||
|
#define ERR_EVENT_TT 0x9
|
||||||
|
#define ERR_EVENT_INVALMS 0xa
|
||||||
|
#define ERR_EVENT_SERR 0xb
|
||||||
|
#define ERR_EVENT_NOMSI 0x10
|
||||||
|
#define ERR_EVENT_INVALBV 0x11
|
||||||
|
#define ERR_EVENT_AIBV 0x12
|
||||||
|
#define ERR_EVENT_AIRERR 0x13
|
||||||
|
#define ERR_EVENT_FMBA 0x2a
|
||||||
|
#define ERR_EVENT_FMBUP 0x2b
|
||||||
|
#define ERR_EVENT_FMBPRO 0x2c
|
||||||
|
#define ERR_EVENT_CCONF 0x30
|
||||||
|
#define ERR_EVENT_SERVAC 0x3a
|
||||||
|
#define ERR_EVENT_PERMERR 0x3b
|
||||||
|
|
||||||
|
#define ERR_EVENT_Q_BIT 0x2
|
||||||
|
#define ERR_EVENT_MVN_OFFSET 16
|
||||||
|
|
||||||
|
#define ZPCI_MSI_VEC_BITS 11
|
||||||
|
#define ZPCI_MSI_VEC_MASK 0x7ff
|
||||||
|
|
||||||
|
#define ZPCI_MSI_ADDR 0xfe00000000000000ULL
|
||||||
|
#define ZPCI_SDMA_ADDR 0x100000000ULL
|
||||||
|
#define ZPCI_EDMA_ADDR 0x1ffffffffffffffULL
|
||||||
|
|
||||||
|
#define PAGE_SHIFT 12
|
||||||
|
#define PAGE_MASK (~(PAGE_SIZE-1))
|
||||||
|
#define PAGE_DEFAULT_ACC 0
|
||||||
|
#define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4)
|
||||||
|
|
||||||
|
/* I/O Translation Anchor (IOTA) */
|
||||||
|
enum ZpciIoatDtype {
|
||||||
|
ZPCI_IOTA_STO = 0,
|
||||||
|
ZPCI_IOTA_RTTO = 1,
|
||||||
|
ZPCI_IOTA_RSTO = 2,
|
||||||
|
ZPCI_IOTA_RFTO = 3,
|
||||||
|
ZPCI_IOTA_PFAA = 4,
|
||||||
|
ZPCI_IOTA_IOPFAA = 5,
|
||||||
|
ZPCI_IOTA_IOPTO = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ZPCI_IOTA_IOT_ENABLED 0x800ULL
|
||||||
|
#define ZPCI_IOTA_DT_ST (ZPCI_IOTA_STO << 2)
|
||||||
|
#define ZPCI_IOTA_DT_RT (ZPCI_IOTA_RTTO << 2)
|
||||||
|
#define ZPCI_IOTA_DT_RS (ZPCI_IOTA_RSTO << 2)
|
||||||
|
#define ZPCI_IOTA_DT_RF (ZPCI_IOTA_RFTO << 2)
|
||||||
|
#define ZPCI_IOTA_DT_PF (ZPCI_IOTA_PFAA << 2)
|
||||||
|
#define ZPCI_IOTA_FS_4K 0
|
||||||
|
#define ZPCI_IOTA_FS_1M 1
|
||||||
|
#define ZPCI_IOTA_FS_2G 2
|
||||||
|
#define ZPCI_KEY (PAGE_DEFAULT_KEY << 5)
|
||||||
|
|
||||||
|
#define ZPCI_IOTA_STO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_ST)
|
||||||
|
#define ZPCI_IOTA_RTTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RT)
|
||||||
|
#define ZPCI_IOTA_RSTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RS)
|
||||||
|
#define ZPCI_IOTA_RFTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RF)
|
||||||
|
#define ZPCI_IOTA_RFAA_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY |\
|
||||||
|
ZPCI_IOTA_DT_PF | ZPCI_IOTA_FS_2G)
|
||||||
|
|
||||||
|
/* I/O Region and segment tables */
|
||||||
|
#define ZPCI_INDEX_MASK 0x7ffULL
|
||||||
|
|
||||||
|
#define ZPCI_TABLE_TYPE_MASK 0xc
|
||||||
|
#define ZPCI_TABLE_TYPE_RFX 0xc
|
||||||
|
#define ZPCI_TABLE_TYPE_RSX 0x8
|
||||||
|
#define ZPCI_TABLE_TYPE_RTX 0x4
|
||||||
|
#define ZPCI_TABLE_TYPE_SX 0x0
|
||||||
|
|
||||||
|
#define ZPCI_TABLE_LEN_RFX 0x3
|
||||||
|
#define ZPCI_TABLE_LEN_RSX 0x3
|
||||||
|
#define ZPCI_TABLE_LEN_RTX 0x3
|
||||||
|
|
||||||
|
#define ZPCI_TABLE_OFFSET_MASK 0xc0
|
||||||
|
#define ZPCI_TABLE_SIZE 0x4000
|
||||||
|
#define ZPCI_TABLE_ALIGN ZPCI_TABLE_SIZE
|
||||||
|
#define ZPCI_TABLE_ENTRY_SIZE (sizeof(unsigned long))
|
||||||
|
#define ZPCI_TABLE_ENTRIES (ZPCI_TABLE_SIZE / ZPCI_TABLE_ENTRY_SIZE)
|
||||||
|
|
||||||
|
#define ZPCI_TABLE_BITS 11
|
||||||
|
#define ZPCI_PT_BITS 8
|
||||||
|
#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + PAGE_SHIFT)
|
||||||
|
#define ZPCI_RT_SHIFT (ZPCI_ST_SHIFT + ZPCI_TABLE_BITS)
|
||||||
|
|
||||||
|
#define ZPCI_RTE_FLAG_MASK 0x3fffULL
|
||||||
|
#define ZPCI_RTE_ADDR_MASK (~ZPCI_RTE_FLAG_MASK)
|
||||||
|
#define ZPCI_STE_FLAG_MASK 0x7ffULL
|
||||||
|
#define ZPCI_STE_ADDR_MASK (~ZPCI_STE_FLAG_MASK)
|
||||||
|
|
||||||
|
/* I/O Page tables */
|
||||||
|
#define ZPCI_PTE_VALID_MASK 0x400
|
||||||
|
#define ZPCI_PTE_INVALID 0x400
|
||||||
|
#define ZPCI_PTE_VALID 0x000
|
||||||
|
#define ZPCI_PT_SIZE 0x800
|
||||||
|
#define ZPCI_PT_ALIGN ZPCI_PT_SIZE
|
||||||
|
#define ZPCI_PT_ENTRIES (ZPCI_PT_SIZE / ZPCI_TABLE_ENTRY_SIZE)
|
||||||
|
#define ZPCI_PT_MASK (ZPCI_PT_ENTRIES - 1)
|
||||||
|
|
||||||
|
#define ZPCI_PTE_FLAG_MASK 0xfffULL
|
||||||
|
#define ZPCI_PTE_ADDR_MASK (~ZPCI_PTE_FLAG_MASK)
|
||||||
|
|
||||||
|
/* Shared bits */
|
||||||
|
#define ZPCI_TABLE_VALID 0x00
|
||||||
|
#define ZPCI_TABLE_INVALID 0x20
|
||||||
|
#define ZPCI_TABLE_PROTECTED 0x200
|
||||||
|
#define ZPCI_TABLE_UNPROTECTED 0x000
|
||||||
|
|
||||||
|
#define ZPCI_TABLE_VALID_MASK 0x20
|
||||||
|
#define ZPCI_TABLE_PROT_MASK 0x200
|
||||||
|
|
||||||
|
typedef struct SeiContainer {
|
||||||
|
QTAILQ_ENTRY(SeiContainer) link;
|
||||||
|
uint32_t fid;
|
||||||
|
uint32_t fh;
|
||||||
|
uint8_t cc;
|
||||||
|
uint16_t pec;
|
||||||
|
uint64_t faddr;
|
||||||
|
uint32_t e;
|
||||||
|
} SeiContainer;
|
||||||
|
|
||||||
|
typedef struct PciCcdfErr {
|
||||||
|
uint32_t reserved1;
|
||||||
|
uint32_t fh;
|
||||||
|
uint32_t fid;
|
||||||
|
uint32_t e;
|
||||||
|
uint64_t faddr;
|
||||||
|
uint32_t reserved3;
|
||||||
|
uint16_t reserved4;
|
||||||
|
uint16_t pec;
|
||||||
|
} QEMU_PACKED PciCcdfErr;
|
||||||
|
|
||||||
|
typedef struct PciCcdfAvail {
|
||||||
|
uint32_t reserved1;
|
||||||
|
uint32_t fh;
|
||||||
|
uint32_t fid;
|
||||||
|
uint32_t reserved2;
|
||||||
|
uint32_t reserved3;
|
||||||
|
uint32_t reserved4;
|
||||||
|
uint32_t reserved5;
|
||||||
|
uint16_t reserved6;
|
||||||
|
uint16_t pec;
|
||||||
|
} QEMU_PACKED PciCcdfAvail;
|
||||||
|
|
||||||
|
typedef struct ChscSeiNt2Res {
|
||||||
|
uint16_t length;
|
||||||
|
uint16_t code;
|
||||||
|
uint16_t reserved1;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint8_t nt;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t reserved3;
|
||||||
|
uint8_t reserved4;
|
||||||
|
uint8_t cc;
|
||||||
|
uint32_t reserved5[13];
|
||||||
|
uint8_t ccdf[4016];
|
||||||
|
} QEMU_PACKED ChscSeiNt2Res;
|
||||||
|
|
||||||
|
typedef struct PciCfgSccb {
|
||||||
|
SCCBHeader header;
|
||||||
|
uint8_t atype;
|
||||||
|
uint8_t reserved1;
|
||||||
|
uint16_t reserved2;
|
||||||
|
uint32_t aid;
|
||||||
|
} QEMU_PACKED PciCfgSccb;
|
||||||
|
|
||||||
|
typedef struct S390MsixInfo {
|
||||||
|
bool available;
|
||||||
|
uint8_t table_bar;
|
||||||
|
uint8_t pba_bar;
|
||||||
|
uint16_t entries;
|
||||||
|
uint32_t table_offset;
|
||||||
|
uint32_t pba_offset;
|
||||||
|
} S390MsixInfo;
|
||||||
|
|
||||||
|
typedef struct S390PCIBusDevice {
|
||||||
|
PCIDevice *pdev;
|
||||||
|
bool configured;
|
||||||
|
bool error_state;
|
||||||
|
bool lgstg_blocked;
|
||||||
|
uint32_t fh;
|
||||||
|
uint32_t fid;
|
||||||
|
uint64_t g_iota;
|
||||||
|
uint64_t pba;
|
||||||
|
uint64_t pal;
|
||||||
|
uint64_t fmb_addr;
|
||||||
|
uint8_t isc;
|
||||||
|
uint16_t noi;
|
||||||
|
uint8_t sum;
|
||||||
|
S390MsixInfo msix;
|
||||||
|
AdapterRoutes routes;
|
||||||
|
AddressSpace as;
|
||||||
|
MemoryRegion mr;
|
||||||
|
} S390PCIBusDevice;
|
||||||
|
|
||||||
|
typedef struct S390pciState {
|
||||||
|
PCIHostState parent_obj;
|
||||||
|
S390PCIBusDevice pbdev[PCI_SLOT_MAX];
|
||||||
|
AddressSpace msix_notify_as;
|
||||||
|
MemoryRegion msix_notify_mr;
|
||||||
|
QTAILQ_HEAD(, SeiContainer) pending_sei;
|
||||||
|
} S390pciState;
|
||||||
|
|
||||||
|
int chsc_sei_nt2_get_event(void *res);
|
||||||
|
int chsc_sei_nt2_have_event(void);
|
||||||
|
void s390_pci_sclp_configure(int configure, SCCB *sccb);
|
||||||
|
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
|
||||||
|
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
|
||||||
|
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,811 @@
|
||||||
|
/*
|
||||||
|
* s390 PCI instructions
|
||||||
|
*
|
||||||
|
* Copyright 2014 IBM Corp.
|
||||||
|
* Author(s): Frank Blaschka <frank.blaschka@de.ibm.com>
|
||||||
|
* Hong Bo Li <lihbbj@cn.ibm.com>
|
||||||
|
* Yi Min Zhao <zyimin@cn.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "s390-pci-inst.h"
|
||||||
|
#include "s390-pci-bus.h"
|
||||||
|
#include <exec/memory-internal.h>
|
||||||
|
#include <qemu/error-report.h>
|
||||||
|
|
||||||
|
/* #define DEBUG_S390PCI_INST */
|
||||||
|
#ifdef DEBUG_S390PCI_INST
|
||||||
|
#define DPRINTF(fmt, ...) \
|
||||||
|
do { fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); } while (0)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(fmt, ...) \
|
||||||
|
do { } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void s390_set_status_code(CPUS390XState *env,
|
||||||
|
uint8_t r, uint64_t status_code)
|
||||||
|
{
|
||||||
|
env->regs[r] &= ~0xff000000ULL;
|
||||||
|
env->regs[r] |= (status_code & 0xff) << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
|
||||||
|
{
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint32_t res_code, initial_l2, g_l2, finish;
|
||||||
|
int rc, idx;
|
||||||
|
uint64_t resume_token;
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
if (lduw_p(&rrb->request.hdr.len) != 32) {
|
||||||
|
res_code = CLP_RC_LEN;
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ldl_p(&rrb->request.fmt) & CLP_MASK_FMT) != 0) {
|
||||||
|
res_code = CLP_RC_FMT;
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 ||
|
||||||
|
ldq_p(&rrb->request.reserved1) != 0 ||
|
||||||
|
ldq_p(&rrb->request.reserved2) != 0) {
|
||||||
|
res_code = CLP_RC_RESNOT0;
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
resume_token = ldq_p(&rrb->request.resume_token);
|
||||||
|
|
||||||
|
if (resume_token) {
|
||||||
|
pbdev = s390_pci_find_dev_by_idx(resume_token);
|
||||||
|
if (!pbdev) {
|
||||||
|
res_code = CLP_RC_LISTPCI_BADRT;
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lduw_p(&rrb->response.hdr.len) < 48) {
|
||||||
|
res_code = CLP_RC_8K;
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
initial_l2 = lduw_p(&rrb->response.hdr.len);
|
||||||
|
if ((initial_l2 - LIST_PCI_HDR_LEN) % sizeof(ClpFhListEntry)
|
||||||
|
!= 0) {
|
||||||
|
res_code = CLP_RC_LEN;
|
||||||
|
rc = -EINVAL;
|
||||||
|
*cc = 3;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
stl_p(&rrb->response.fmt, 0);
|
||||||
|
stq_p(&rrb->response.reserved1, 0);
|
||||||
|
stq_p(&rrb->response.reserved2, 0);
|
||||||
|
stl_p(&rrb->response.mdd, FH_VIRT);
|
||||||
|
stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS);
|
||||||
|
rrb->response.entry_size = sizeof(ClpFhListEntry);
|
||||||
|
finish = 0;
|
||||||
|
idx = resume_token;
|
||||||
|
g_l2 = LIST_PCI_HDR_LEN;
|
||||||
|
do {
|
||||||
|
pbdev = s390_pci_find_dev_by_idx(idx);
|
||||||
|
if (!pbdev) {
|
||||||
|
finish = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stw_p(&rrb->response.fh_list[idx - resume_token].device_id,
|
||||||
|
pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID));
|
||||||
|
stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id,
|
||||||
|
pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID));
|
||||||
|
stl_p(&rrb->response.fh_list[idx - resume_token].config, 0x80000000);
|
||||||
|
stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid);
|
||||||
|
stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh);
|
||||||
|
|
||||||
|
g_l2 += sizeof(ClpFhListEntry);
|
||||||
|
/* Add endian check for DPRINTF? */
|
||||||
|
DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n",
|
||||||
|
g_l2,
|
||||||
|
lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id),
|
||||||
|
lduw_p(&rrb->response.fh_list[idx - resume_token].device_id),
|
||||||
|
ldl_p(&rrb->response.fh_list[idx - resume_token].fid),
|
||||||
|
ldl_p(&rrb->response.fh_list[idx - resume_token].fh));
|
||||||
|
idx++;
|
||||||
|
} while (g_l2 < initial_l2);
|
||||||
|
|
||||||
|
if (finish == 1) {
|
||||||
|
resume_token = 0;
|
||||||
|
} else {
|
||||||
|
resume_token = idx;
|
||||||
|
}
|
||||||
|
stq_p(&rrb->response.resume_token, resume_token);
|
||||||
|
stw_p(&rrb->response.hdr.len, g_l2);
|
||||||
|
stw_p(&rrb->response.hdr.rsp, CLP_RC_OK);
|
||||||
|
out:
|
||||||
|
if (rc) {
|
||||||
|
DPRINTF("list pci failed rc 0x%x\n", rc);
|
||||||
|
stw_p(&rrb->response.hdr.rsp, res_code);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clp_service_call(S390CPU *cpu, uint8_t r2)
|
||||||
|
{
|
||||||
|
ClpReqHdr *reqh;
|
||||||
|
ClpRspHdr *resh;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint32_t req_len;
|
||||||
|
uint32_t res_len;
|
||||||
|
uint8_t buffer[4096 * 2];
|
||||||
|
uint8_t cc = 0;
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_physical_memory_read(env->regs[r2], buffer, sizeof(*reqh));
|
||||||
|
reqh = (ClpReqHdr *)buffer;
|
||||||
|
req_len = lduw_p(&reqh->len);
|
||||||
|
if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_physical_memory_read(env->regs[r2], buffer, req_len + sizeof(*resh));
|
||||||
|
resh = (ClpRspHdr *)(buffer + req_len);
|
||||||
|
res_len = lduw_p(&resh->len);
|
||||||
|
if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((req_len + res_len) > 8192) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_physical_memory_read(env->regs[r2], buffer, req_len + res_len);
|
||||||
|
|
||||||
|
if (req_len != 32) {
|
||||||
|
stw_p(&resh->rsp, CLP_RC_LEN);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lduw_p(&reqh->cmd)) {
|
||||||
|
case CLP_LIST_PCI: {
|
||||||
|
ClpReqRspListPci *rrb = (ClpReqRspListPci *)buffer;
|
||||||
|
list_pci(rrb, &cc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLP_SET_PCI_FN: {
|
||||||
|
ClpReqSetPci *reqsetpci = (ClpReqSetPci *)reqh;
|
||||||
|
ClpRspSetPci *ressetpci = (ClpRspSetPci *)resh;
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqsetpci->fh));
|
||||||
|
if (!pbdev) {
|
||||||
|
stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reqsetpci->oc) {
|
||||||
|
case CLP_SET_ENABLE_PCI_FN:
|
||||||
|
pbdev->fh = pbdev->fh | 1 << ENABLE_BIT_OFFSET;
|
||||||
|
stl_p(&ressetpci->fh, pbdev->fh);
|
||||||
|
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
|
||||||
|
break;
|
||||||
|
case CLP_SET_DISABLE_PCI_FN:
|
||||||
|
pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET);
|
||||||
|
pbdev->error_state = false;
|
||||||
|
pbdev->lgstg_blocked = false;
|
||||||
|
stl_p(&ressetpci->fh, pbdev->fh);
|
||||||
|
stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DPRINTF("unknown set pci command\n");
|
||||||
|
stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLP_QUERY_PCI_FN: {
|
||||||
|
ClpReqQueryPci *reqquery = (ClpReqQueryPci *)reqh;
|
||||||
|
ClpRspQueryPci *resquery = (ClpRspQueryPci *)resh;
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqquery->fh));
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("query pci no pci dev\n");
|
||||||
|
stw_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PCI_BAR_COUNT; i++) {
|
||||||
|
uint32_t data = pci_get_long(pbdev->pdev->config +
|
||||||
|
PCI_BASE_ADDRESS_0 + (i * 4));
|
||||||
|
|
||||||
|
stl_p(&resquery->bar[i], data);
|
||||||
|
resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ?
|
||||||
|
ctz64(pbdev->pdev->io_regions[i].size) : 0;
|
||||||
|
DPRINTF("bar %d addr 0x%x size 0x%" PRIx64 "barsize 0x%x\n", i,
|
||||||
|
ldl_p(&resquery->bar[i]),
|
||||||
|
pbdev->pdev->io_regions[i].size,
|
||||||
|
resquery->bar_size[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
stq_p(&resquery->sdma, ZPCI_SDMA_ADDR);
|
||||||
|
stq_p(&resquery->edma, ZPCI_EDMA_ADDR);
|
||||||
|
stw_p(&resquery->pchid, 0);
|
||||||
|
stw_p(&resquery->ug, 1);
|
||||||
|
stl_p(&resquery->uid, pbdev->fid);
|
||||||
|
stw_p(&resquery->hdr.rsp, CLP_RC_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLP_QUERY_PCI_FNGRP: {
|
||||||
|
ClpRspQueryPciGrp *resgrp = (ClpRspQueryPciGrp *)resh;
|
||||||
|
resgrp->fr = 1;
|
||||||
|
stq_p(&resgrp->dasm, 0);
|
||||||
|
stq_p(&resgrp->msia, ZPCI_MSI_ADDR);
|
||||||
|
stw_p(&resgrp->mui, 0);
|
||||||
|
stw_p(&resgrp->i, 128);
|
||||||
|
resgrp->version = 0;
|
||||||
|
|
||||||
|
stw_p(&resgrp->hdr.rsp, CLP_RC_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
DPRINTF("unknown clp command\n");
|
||||||
|
stw_p(&resh->rsp, CLP_RC_CMD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
cpu_physical_memory_write(env->regs[r2], buffer, req_len + res_len);
|
||||||
|
setcc(cpu, cc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t data;
|
||||||
|
uint8_t len;
|
||||||
|
uint32_t fh;
|
||||||
|
uint8_t pcias;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r2 & 0x1) {
|
||||||
|
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = env->regs[r2] >> 32;
|
||||||
|
pcias = (env->regs[r2] >> 16) & 0xf;
|
||||||
|
len = env->regs[r2] & 0xf;
|
||||||
|
offset = env->regs[r2 + 1];
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(fh);
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("pcilg no pci dev\n");
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbdev->lgstg_blocked) {
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcias < 6) {
|
||||||
|
if ((8 - (offset & 0x7)) < len) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
MemoryRegion *mr = pbdev->pdev->io_regions[pcias].memory;
|
||||||
|
io_mem_read(mr, offset, &data, len);
|
||||||
|
} else if (pcias == 15) {
|
||||||
|
if ((4 - (offset & 0x3)) < len) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
data = pci_host_config_read_common(
|
||||||
|
pbdev->pdev, offset, pci_config_size(pbdev->pdev), len);
|
||||||
|
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
data = bswap16(data);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
data = bswap32(data);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
data = bswap64(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DPRINTF("invalid space\n");
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->regs[r1] = data;
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_OK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset,
|
||||||
|
uint64_t *data, uint8_t len)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
uint8_t *msg_data;
|
||||||
|
|
||||||
|
if (offset % PCI_MSIX_ENTRY_SIZE != 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != 4) {
|
||||||
|
DPRINTF("access msix table msg data but len is %d\n", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE +
|
||||||
|
PCI_MSIX_ENTRY_VECTOR_CTRL;
|
||||||
|
val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS);
|
||||||
|
pci_set_long(msg_data, val);
|
||||||
|
DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trap_msix(S390PCIBusDevice *pbdev, uint64_t offset, uint8_t pcias)
|
||||||
|
{
|
||||||
|
if (pbdev->msix.available && pbdev->msix.table_bar == pcias &&
|
||||||
|
offset >= pbdev->msix.table_offset &&
|
||||||
|
offset <= pbdev->msix.table_offset +
|
||||||
|
(pbdev->msix.entries - 1) * PCI_MSIX_ENTRY_SIZE) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
uint64_t offset, data;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint8_t len;
|
||||||
|
uint32_t fh;
|
||||||
|
uint8_t pcias;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r2 & 0x1) {
|
||||||
|
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = env->regs[r2] >> 32;
|
||||||
|
pcias = (env->regs[r2] >> 16) & 0xf;
|
||||||
|
len = env->regs[r2] & 0xf;
|
||||||
|
offset = env->regs[r2 + 1];
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(fh);
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("pcistg no pci dev\n");
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbdev->lgstg_blocked) {
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = env->regs[r1];
|
||||||
|
if (pcias < 6) {
|
||||||
|
if ((8 - (offset & 0x7)) < len) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
MemoryRegion *mr;
|
||||||
|
if (trap_msix(pbdev, offset, pcias)) {
|
||||||
|
offset = offset - pbdev->msix.table_offset;
|
||||||
|
mr = &pbdev->pdev->msix_table_mmio;
|
||||||
|
update_msix_table_msg_data(pbdev, offset, &data, len);
|
||||||
|
} else {
|
||||||
|
mr = pbdev->pdev->io_regions[pcias].memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_mem_write(mr, offset, data, len);
|
||||||
|
} else if (pcias == 15) {
|
||||||
|
if ((4 - (offset & 0x3)) < len) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
switch (len) {
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
data = bswap16(data);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
data = bswap32(data);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
data = bswap64(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
program_interrupt(env, PGM_OPERAND, 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_host_config_write_common(pbdev->pdev, offset,
|
||||||
|
pci_config_size(pbdev->pdev),
|
||||||
|
data, len);
|
||||||
|
} else {
|
||||||
|
DPRINTF("pcistg invalid space\n");
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_OK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
uint32_t fh;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
ram_addr_t size;
|
||||||
|
IOMMUTLBEntry entry;
|
||||||
|
MemoryRegion *mr;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 4);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r2 & 0x1) {
|
||||||
|
program_interrupt(env, PGM_SPECIFICATION, 4);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = env->regs[r1] >> 32;
|
||||||
|
size = env->regs[r2 + 1];
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(fh);
|
||||||
|
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("rpcit no pci dev\n");
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mr = pci_device_iommu_address_space(pbdev->pdev)->root;
|
||||||
|
entry = mr->iommu_ops->translate(mr, env->regs[r2], 0);
|
||||||
|
|
||||||
|
if (!entry.translated_addr) {
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.addr_mask = size - 1;
|
||||||
|
memory_region_notify_iommu(mr, entry);
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_OK);
|
||||||
|
out:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
MemoryRegion *mr;
|
||||||
|
int i;
|
||||||
|
uint64_t val;
|
||||||
|
uint32_t fh;
|
||||||
|
uint8_t pcias;
|
||||||
|
uint8_t len;
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = env->regs[r1] >> 32;
|
||||||
|
pcias = (env->regs[r1] >> 16) & 0xf;
|
||||||
|
len = env->regs[r1] & 0xff;
|
||||||
|
|
||||||
|
if (pcias > 5) {
|
||||||
|
DPRINTF("pcistb invalid space\n");
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
s390_set_status_code(env, r1, ZPCI_PCI_ST_INVAL_AS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (len) {
|
||||||
|
case 16:
|
||||||
|
case 32:
|
||||||
|
case 64:
|
||||||
|
case 128:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
program_interrupt(env, PGM_SPECIFICATION, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(fh);
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbdev->lgstg_blocked) {
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_ERR);
|
||||||
|
s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mr = pbdev->pdev->io_regions[pcias].memory;
|
||||||
|
if (!memory_region_access_valid(mr, env->regs[r3], len, true)) {
|
||||||
|
program_interrupt(env, PGM_ADDRESSING, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len / 8; i++) {
|
||||||
|
val = ldq_phys(&address_space_memory, gaddr + i * 8);
|
||||||
|
io_mem_write(mr, env->regs[r3] + i * 8, val, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_OK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
S390FLICState *fs = s390_get_flic();
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||||
|
|
||||||
|
ret = css_register_io_adapter(S390_PCIPT_ADAPTER,
|
||||||
|
FIB_DATA_ISC(ldl_p(&fib.data)), true, false,
|
||||||
|
&pbdev->routes.adapter.adapter_id);
|
||||||
|
assert(ret == 0);
|
||||||
|
|
||||||
|
fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
|
||||||
|
ldq_p(&fib.aisb), true);
|
||||||
|
fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
|
||||||
|
ldq_p(&fib.aibv), true);
|
||||||
|
|
||||||
|
pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb);
|
||||||
|
pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data));
|
||||||
|
pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv);
|
||||||
|
pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data));
|
||||||
|
pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data));
|
||||||
|
pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data));
|
||||||
|
pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data));
|
||||||
|
|
||||||
|
DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dereg_irqs(S390PCIBusDevice *pbdev)
|
||||||
|
{
|
||||||
|
S390FLICState *fs = s390_get_flic();
|
||||||
|
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||||
|
|
||||||
|
fsc->io_adapter_map(fs, pbdev->routes.adapter.adapter_id,
|
||||||
|
pbdev->routes.adapter.ind_addr, false);
|
||||||
|
|
||||||
|
pbdev->routes.adapter.summary_addr = 0;
|
||||||
|
pbdev->routes.adapter.summary_offset = 0;
|
||||||
|
pbdev->routes.adapter.ind_addr = 0;
|
||||||
|
pbdev->routes.adapter.ind_offset = 0;
|
||||||
|
pbdev->isc = 0;
|
||||||
|
pbdev->noi = 0;
|
||||||
|
pbdev->sum = 0;
|
||||||
|
|
||||||
|
DPRINTF("dereg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
|
||||||
|
{
|
||||||
|
uint64_t pba = ldq_p(&fib.pba);
|
||||||
|
uint64_t pal = ldq_p(&fib.pal);
|
||||||
|
uint64_t g_iota = ldq_p(&fib.iota);
|
||||||
|
uint8_t dt = (g_iota >> 2) & 0x7;
|
||||||
|
uint8_t t = (g_iota >> 11) & 0x1;
|
||||||
|
|
||||||
|
if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
|
||||||
|
program_interrupt(env, PGM_OPERAND, 6);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* currently we only support designation type 1 with translation */
|
||||||
|
if (!(dt == ZPCI_IOTA_RTTO && t)) {
|
||||||
|
error_report("unsupported ioat dt %d t %d", dt, t);
|
||||||
|
program_interrupt(env, PGM_OPERAND, 6);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbdev->pba = pba;
|
||||||
|
pbdev->pal = pal;
|
||||||
|
pbdev->g_iota = g_iota;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dereg_ioat(S390PCIBusDevice *pbdev)
|
||||||
|
{
|
||||||
|
pbdev->pba = 0;
|
||||||
|
pbdev->pal = 0;
|
||||||
|
pbdev->g_iota = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
uint8_t oc;
|
||||||
|
uint32_t fh;
|
||||||
|
ZpciFib fib;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint64_t cc = ZPCI_PCI_LS_OK;
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
oc = env->regs[r1] & 0xff;
|
||||||
|
fh = env->regs[r1] >> 32;
|
||||||
|
|
||||||
|
if (fiba & 0x7) {
|
||||||
|
program_interrupt(env, PGM_SPECIFICATION, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(fh);
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("mpcifc no pci dev fh 0x%x\n", fh);
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_physical_memory_read(fiba, (uint8_t *)&fib, sizeof(fib));
|
||||||
|
|
||||||
|
switch (oc) {
|
||||||
|
case ZPCI_MOD_FC_REG_INT:
|
||||||
|
if (reg_irqs(env, pbdev, fib)) {
|
||||||
|
cc = ZPCI_PCI_LS_ERR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_DEREG_INT:
|
||||||
|
dereg_irqs(pbdev);
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_REG_IOAT:
|
||||||
|
if (reg_ioat(env, pbdev, fib)) {
|
||||||
|
cc = ZPCI_PCI_LS_ERR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_DEREG_IOAT:
|
||||||
|
dereg_ioat(pbdev);
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_REREG_IOAT:
|
||||||
|
dereg_ioat(pbdev);
|
||||||
|
if (reg_ioat(env, pbdev, fib)) {
|
||||||
|
cc = ZPCI_PCI_LS_ERR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_RESET_ERROR:
|
||||||
|
pbdev->error_state = false;
|
||||||
|
pbdev->lgstg_blocked = false;
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_RESET_BLOCK:
|
||||||
|
pbdev->lgstg_blocked = false;
|
||||||
|
break;
|
||||||
|
case ZPCI_MOD_FC_SET_MEASURE:
|
||||||
|
pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
program_interrupt(&cpu->env, PGM_OPERAND, 6);
|
||||||
|
cc = ZPCI_PCI_LS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
setcc(cpu, cc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
uint32_t fh;
|
||||||
|
ZpciFib fib;
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint32_t data;
|
||||||
|
uint64_t cc = ZPCI_PCI_LS_OK;
|
||||||
|
|
||||||
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
||||||
|
program_interrupt(env, PGM_PRIVILEGED, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = env->regs[r1] >> 32;
|
||||||
|
|
||||||
|
if (fiba & 0x7) {
|
||||||
|
program_interrupt(env, PGM_SPECIFICATION, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fh(fh);
|
||||||
|
if (!pbdev) {
|
||||||
|
setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&fib, 0, sizeof(fib));
|
||||||
|
stq_p(&fib.pba, pbdev->pba);
|
||||||
|
stq_p(&fib.pal, pbdev->pal);
|
||||||
|
stq_p(&fib.iota, pbdev->g_iota);
|
||||||
|
stq_p(&fib.aibv, pbdev->routes.adapter.ind_addr);
|
||||||
|
stq_p(&fib.aisb, pbdev->routes.adapter.summary_addr);
|
||||||
|
stq_p(&fib.fmb_addr, pbdev->fmb_addr);
|
||||||
|
|
||||||
|
data = (pbdev->isc << 28) | (pbdev->noi << 16) |
|
||||||
|
(pbdev->routes.adapter.ind_offset << 8) | (pbdev->sum << 7) |
|
||||||
|
pbdev->routes.adapter.summary_offset;
|
||||||
|
stw_p(&fib.data, data);
|
||||||
|
|
||||||
|
if (pbdev->fh >> ENABLE_BIT_OFFSET) {
|
||||||
|
fib.fc |= 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbdev->error_state) {
|
||||||
|
fib.fc |= 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbdev->lgstg_blocked) {
|
||||||
|
fib.fc |= 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbdev->g_iota) {
|
||||||
|
fib.fc |= 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_physical_memory_write(fiba, (uint8_t *)&fib, sizeof(fib));
|
||||||
|
setcc(cpu, cc);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* s390 PCI instruction definitions
|
||||||
|
*
|
||||||
|
* Copyright 2014 IBM Corp.
|
||||||
|
* Author(s): Frank Blaschka <frank.blaschka@de.ibm.com>
|
||||||
|
* Hong Bo Li <lihbbj@cn.ibm.com>
|
||||||
|
* Yi Min Zhao <zyimin@cn.ibm.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||||
|
* your option) any later version. See the COPYING file in the top-level
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_S390_PCI_INST_H
|
||||||
|
#define HW_S390_PCI_INST_H
|
||||||
|
|
||||||
|
#include <sysemu/dma.h>
|
||||||
|
|
||||||
|
/* CLP common request & response block size */
|
||||||
|
#define CLP_BLK_SIZE 4096
|
||||||
|
#define PCI_BAR_COUNT 6
|
||||||
|
#define PCI_MAX_FUNCTIONS 4096
|
||||||
|
|
||||||
|
typedef struct ClpReqHdr {
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t cmd;
|
||||||
|
} QEMU_PACKED ClpReqHdr;
|
||||||
|
|
||||||
|
typedef struct ClpRspHdr {
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t rsp;
|
||||||
|
} QEMU_PACKED ClpRspHdr;
|
||||||
|
|
||||||
|
/* CLP Response Codes */
|
||||||
|
#define CLP_RC_OK 0x0010 /* Command request successfully */
|
||||||
|
#define CLP_RC_CMD 0x0020 /* Command code not recognized */
|
||||||
|
#define CLP_RC_PERM 0x0030 /* Command not authorized */
|
||||||
|
#define CLP_RC_FMT 0x0040 /* Invalid command request format */
|
||||||
|
#define CLP_RC_LEN 0x0050 /* Invalid command request length */
|
||||||
|
#define CLP_RC_8K 0x0060 /* Command requires 8K LPCB */
|
||||||
|
#define CLP_RC_RESNOT0 0x0070 /* Reserved field not zero */
|
||||||
|
#define CLP_RC_NODATA 0x0080 /* No data available */
|
||||||
|
#define CLP_RC_FC_UNKNOWN 0x0100 /* Function code not recognized */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call Logical Processor - Command Codes
|
||||||
|
*/
|
||||||
|
#define CLP_LIST_PCI 0x0002
|
||||||
|
#define CLP_QUERY_PCI_FN 0x0003
|
||||||
|
#define CLP_QUERY_PCI_FNGRP 0x0004
|
||||||
|
#define CLP_SET_PCI_FN 0x0005
|
||||||
|
|
||||||
|
/* PCI function handle list entry */
|
||||||
|
typedef struct ClpFhListEntry {
|
||||||
|
uint16_t device_id;
|
||||||
|
uint16_t vendor_id;
|
||||||
|
#define CLP_FHLIST_MASK_CONFIG 0x80000000
|
||||||
|
uint32_t config;
|
||||||
|
uint32_t fid;
|
||||||
|
uint32_t fh;
|
||||||
|
} QEMU_PACKED ClpFhListEntry;
|
||||||
|
|
||||||
|
#define CLP_RC_SETPCIFN_FH 0x0101 /* Invalid PCI fn handle */
|
||||||
|
#define CLP_RC_SETPCIFN_FHOP 0x0102 /* Fn handle not valid for op */
|
||||||
|
#define CLP_RC_SETPCIFN_DMAAS 0x0103 /* Invalid DMA addr space */
|
||||||
|
#define CLP_RC_SETPCIFN_RES 0x0104 /* Insufficient resources */
|
||||||
|
#define CLP_RC_SETPCIFN_ALRDY 0x0105 /* Fn already in requested state */
|
||||||
|
#define CLP_RC_SETPCIFN_ERR 0x0106 /* Fn in permanent error state */
|
||||||
|
#define CLP_RC_SETPCIFN_RECPND 0x0107 /* Error recovery pending */
|
||||||
|
#define CLP_RC_SETPCIFN_BUSY 0x0108 /* Fn busy */
|
||||||
|
#define CLP_RC_LISTPCI_BADRT 0x010a /* Resume token not recognized */
|
||||||
|
#define CLP_RC_QUERYPCIFG_PFGID 0x010b /* Unrecognized PFGID */
|
||||||
|
|
||||||
|
/* request or response block header length */
|
||||||
|
#define LIST_PCI_HDR_LEN 32
|
||||||
|
|
||||||
|
/* Number of function handles fitting in response block */
|
||||||
|
#define CLP_FH_LIST_NR_ENTRIES \
|
||||||
|
((CLP_BLK_SIZE - 2 * LIST_PCI_HDR_LEN) \
|
||||||
|
/ sizeof(ClpFhListEntry))
|
||||||
|
|
||||||
|
#define CLP_SET_ENABLE_PCI_FN 0 /* Yes, 0 enables it */
|
||||||
|
#define CLP_SET_DISABLE_PCI_FN 1 /* Yes, 1 disables it */
|
||||||
|
|
||||||
|
#define CLP_UTIL_STR_LEN 64
|
||||||
|
|
||||||
|
#define CLP_MASK_FMT 0xf0000000
|
||||||
|
|
||||||
|
/* List PCI functions request */
|
||||||
|
typedef struct ClpReqListPci {
|
||||||
|
ClpReqHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint64_t resume_token;
|
||||||
|
uint64_t reserved2;
|
||||||
|
} QEMU_PACKED ClpReqListPci;
|
||||||
|
|
||||||
|
/* List PCI functions response */
|
||||||
|
typedef struct ClpRspListPci {
|
||||||
|
ClpRspHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint64_t resume_token;
|
||||||
|
uint32_t mdd;
|
||||||
|
uint16_t max_fn;
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint8_t entry_size;
|
||||||
|
ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES];
|
||||||
|
} QEMU_PACKED ClpRspListPci;
|
||||||
|
|
||||||
|
/* Query PCI function request */
|
||||||
|
typedef struct ClpReqQueryPci {
|
||||||
|
ClpReqHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint32_t fh; /* function handle */
|
||||||
|
uint32_t reserved2;
|
||||||
|
uint64_t reserved3;
|
||||||
|
} QEMU_PACKED ClpReqQueryPci;
|
||||||
|
|
||||||
|
/* Query PCI function response */
|
||||||
|
typedef struct ClpRspQueryPci {
|
||||||
|
ClpRspHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint16_t vfn; /* virtual fn number */
|
||||||
|
#define CLP_RSP_QPCI_MASK_UTIL 0x100
|
||||||
|
#define CLP_RSP_QPCI_MASK_PFGID 0xff
|
||||||
|
uint16_t ug;
|
||||||
|
uint32_t fid; /* pci function id */
|
||||||
|
uint8_t bar_size[PCI_BAR_COUNT];
|
||||||
|
uint16_t pchid;
|
||||||
|
uint32_t bar[PCI_BAR_COUNT];
|
||||||
|
uint64_t reserved2;
|
||||||
|
uint64_t sdma; /* start dma as */
|
||||||
|
uint64_t edma; /* end dma as */
|
||||||
|
uint32_t reserved3[11];
|
||||||
|
uint32_t uid;
|
||||||
|
uint8_t util_str[CLP_UTIL_STR_LEN]; /* utility string */
|
||||||
|
} QEMU_PACKED ClpRspQueryPci;
|
||||||
|
|
||||||
|
/* Query PCI function group request */
|
||||||
|
typedef struct ClpReqQueryPciGrp {
|
||||||
|
ClpReqHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
#define CLP_REQ_QPCIG_MASK_PFGID 0xff
|
||||||
|
uint32_t g;
|
||||||
|
uint32_t reserved2;
|
||||||
|
uint64_t reserved3;
|
||||||
|
} QEMU_PACKED ClpReqQueryPciGrp;
|
||||||
|
|
||||||
|
/* Query PCI function group response */
|
||||||
|
typedef struct ClpRspQueryPciGrp {
|
||||||
|
ClpRspHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
#define CLP_RSP_QPCIG_MASK_NOI 0xfff
|
||||||
|
uint16_t i;
|
||||||
|
uint8_t version;
|
||||||
|
#define CLP_RSP_QPCIG_MASK_FRAME 0x2
|
||||||
|
#define CLP_RSP_QPCIG_MASK_REFRESH 0x1
|
||||||
|
uint8_t fr;
|
||||||
|
uint16_t reserved2;
|
||||||
|
uint16_t mui;
|
||||||
|
uint64_t reserved3;
|
||||||
|
uint64_t dasm; /* dma address space mask */
|
||||||
|
uint64_t msia; /* MSI address */
|
||||||
|
uint64_t reserved4;
|
||||||
|
uint64_t reserved5;
|
||||||
|
} QEMU_PACKED ClpRspQueryPciGrp;
|
||||||
|
|
||||||
|
/* Set PCI function request */
|
||||||
|
typedef struct ClpReqSetPci {
|
||||||
|
ClpReqHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint32_t fh; /* function handle */
|
||||||
|
uint16_t reserved2;
|
||||||
|
uint8_t oc; /* operation controls */
|
||||||
|
uint8_t ndas; /* number of dma spaces */
|
||||||
|
uint64_t reserved3;
|
||||||
|
} QEMU_PACKED ClpReqSetPci;
|
||||||
|
|
||||||
|
/* Set PCI function response */
|
||||||
|
typedef struct ClpRspSetPci {
|
||||||
|
ClpRspHdr hdr;
|
||||||
|
uint32_t fmt;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint32_t fh; /* function handle */
|
||||||
|
uint32_t reserved3;
|
||||||
|
uint64_t reserved4;
|
||||||
|
} QEMU_PACKED ClpRspSetPci;
|
||||||
|
|
||||||
|
typedef struct ClpReqRspListPci {
|
||||||
|
ClpReqListPci request;
|
||||||
|
ClpRspListPci response;
|
||||||
|
} QEMU_PACKED ClpReqRspListPci;
|
||||||
|
|
||||||
|
typedef struct ClpReqRspSetPci {
|
||||||
|
ClpReqSetPci request;
|
||||||
|
ClpRspSetPci response;
|
||||||
|
} QEMU_PACKED ClpReqRspSetPci;
|
||||||
|
|
||||||
|
typedef struct ClpReqRspQueryPci {
|
||||||
|
ClpReqQueryPci request;
|
||||||
|
ClpRspQueryPci response;
|
||||||
|
} QEMU_PACKED ClpReqRspQueryPci;
|
||||||
|
|
||||||
|
typedef struct ClpReqRspQueryPciGrp {
|
||||||
|
ClpReqQueryPciGrp request;
|
||||||
|
ClpRspQueryPciGrp response;
|
||||||
|
} QEMU_PACKED ClpReqRspQueryPciGrp;
|
||||||
|
|
||||||
|
/* Load/Store status codes */
|
||||||
|
#define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4
|
||||||
|
#define ZPCI_PCI_ST_FUNC_IN_ERR 8
|
||||||
|
#define ZPCI_PCI_ST_BLOCKED 12
|
||||||
|
#define ZPCI_PCI_ST_INSUF_RES 16
|
||||||
|
#define ZPCI_PCI_ST_INVAL_AS 20
|
||||||
|
#define ZPCI_PCI_ST_FUNC_ALREADY_ENABLED 24
|
||||||
|
#define ZPCI_PCI_ST_DMA_AS_NOT_ENABLED 28
|
||||||
|
#define ZPCI_PCI_ST_2ND_OP_IN_INV_AS 36
|
||||||
|
#define ZPCI_PCI_ST_FUNC_NOT_AVAIL 40
|
||||||
|
#define ZPCI_PCI_ST_ALREADY_IN_RQ_STATE 44
|
||||||
|
|
||||||
|
/* Load/Store return codes */
|
||||||
|
#define ZPCI_PCI_LS_OK 0
|
||||||
|
#define ZPCI_PCI_LS_ERR 1
|
||||||
|
#define ZPCI_PCI_LS_BUSY 2
|
||||||
|
#define ZPCI_PCI_LS_INVAL_HANDLE 3
|
||||||
|
|
||||||
|
/* Modify PCI Function Controls */
|
||||||
|
#define ZPCI_MOD_FC_REG_INT 2
|
||||||
|
#define ZPCI_MOD_FC_DEREG_INT 3
|
||||||
|
#define ZPCI_MOD_FC_REG_IOAT 4
|
||||||
|
#define ZPCI_MOD_FC_DEREG_IOAT 5
|
||||||
|
#define ZPCI_MOD_FC_REREG_IOAT 6
|
||||||
|
#define ZPCI_MOD_FC_RESET_ERROR 7
|
||||||
|
#define ZPCI_MOD_FC_RESET_BLOCK 9
|
||||||
|
#define ZPCI_MOD_FC_SET_MEASURE 10
|
||||||
|
|
||||||
|
/* FIB function controls */
|
||||||
|
#define ZPCI_FIB_FC_ENABLED 0x80
|
||||||
|
#define ZPCI_FIB_FC_ERROR 0x40
|
||||||
|
#define ZPCI_FIB_FC_LS_BLOCKED 0x20
|
||||||
|
#define ZPCI_FIB_FC_DMAAS_REG 0x10
|
||||||
|
|
||||||
|
/* FIB function controls */
|
||||||
|
#define ZPCI_FIB_FC_ENABLED 0x80
|
||||||
|
#define ZPCI_FIB_FC_ERROR 0x40
|
||||||
|
#define ZPCI_FIB_FC_LS_BLOCKED 0x20
|
||||||
|
#define ZPCI_FIB_FC_DMAAS_REG 0x10
|
||||||
|
|
||||||
|
/* Function Information Block */
|
||||||
|
typedef struct ZpciFib {
|
||||||
|
uint8_t fmt; /* format */
|
||||||
|
uint8_t reserved1[7];
|
||||||
|
uint8_t fc; /* function controls */
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint16_t reserved3;
|
||||||
|
uint32_t reserved4;
|
||||||
|
uint64_t pba; /* PCI base address */
|
||||||
|
uint64_t pal; /* PCI address limit */
|
||||||
|
uint64_t iota; /* I/O Translation Anchor */
|
||||||
|
#define FIB_DATA_ISC(x) (((x) >> 28) & 0x7)
|
||||||
|
#define FIB_DATA_NOI(x) (((x) >> 16) & 0xfff)
|
||||||
|
#define FIB_DATA_AIBVO(x) (((x) >> 8) & 0x3f)
|
||||||
|
#define FIB_DATA_SUM(x) (((x) >> 7) & 0x1)
|
||||||
|
#define FIB_DATA_AISBO(x) ((x) & 0x3f)
|
||||||
|
uint32_t data;
|
||||||
|
uint32_t reserved5;
|
||||||
|
uint64_t aibv; /* Adapter int bit vector address */
|
||||||
|
uint64_t aisb; /* Adapter int summary bit address */
|
||||||
|
uint64_t fmb_addr; /* Function measurement address and key */
|
||||||
|
uint32_t reserved6;
|
||||||
|
uint32_t gd;
|
||||||
|
} QEMU_PACKED ZpciFib;
|
||||||
|
|
||||||
|
int clp_service_call(S390CPU *cpu, uint8_t r2);
|
||||||
|
int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
|
||||||
|
int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
|
||||||
|
int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
|
||||||
|
int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr);
|
||||||
|
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba);
|
||||||
|
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba);
|
||||||
|
|
||||||
|
#endif
|
|
@ -18,6 +18,7 @@
|
||||||
#include "css.h"
|
#include "css.h"
|
||||||
#include "virtio-ccw.h"
|
#include "virtio-ccw.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
|
#include "s390-pci-bus.h"
|
||||||
|
|
||||||
#define TYPE_S390_CCW_MACHINE "s390-ccw-machine"
|
#define TYPE_S390_CCW_MACHINE "s390-ccw-machine"
|
||||||
|
|
||||||
|
@ -91,6 +92,7 @@ static void ccw_init(MachineState *machine)
|
||||||
uint8_t *storage_keys;
|
uint8_t *storage_keys;
|
||||||
int ret;
|
int ret;
|
||||||
VirtualCssBus *css_bus;
|
VirtualCssBus *css_bus;
|
||||||
|
DeviceState *dev;
|
||||||
QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
|
QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
|
||||||
ram_addr_t pad_size = 0;
|
ram_addr_t pad_size = 0;
|
||||||
ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
|
ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
|
||||||
|
@ -127,6 +129,11 @@ static void ccw_init(MachineState *machine)
|
||||||
machine->initrd_filename, "s390-ccw.img");
|
machine->initrd_filename, "s390-ccw.img");
|
||||||
s390_flic_init();
|
s390_flic_init();
|
||||||
|
|
||||||
|
dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE);
|
||||||
|
object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE,
|
||||||
|
OBJECT(dev), NULL);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
/* register hypercalls */
|
/* register hypercalls */
|
||||||
virtio_ccw_register_hcalls();
|
virtio_ccw_register_hcalls();
|
||||||
|
|
||||||
|
@ -181,7 +188,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
|
||||||
mc->no_serial = 1;
|
mc->no_serial = 1;
|
||||||
mc->no_parallel = 1;
|
mc->no_parallel = 1;
|
||||||
mc->no_sdcard = 1;
|
mc->no_sdcard = 1;
|
||||||
mc->use_sclp = 1,
|
mc->use_sclp = 1;
|
||||||
mc->max_cpus = 255;
|
mc->max_cpus = 255;
|
||||||
nc->nmi_monitor_handler = s390_nmi;
|
nc->nmi_monitor_handler = s390_nmi;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "hw/s390x/sclp.h"
|
#include "hw/s390x/sclp.h"
|
||||||
#include "hw/s390x/event-facility.h"
|
#include "hw/s390x/event-facility.h"
|
||||||
|
#include "hw/s390x/s390-pci-bus.h"
|
||||||
|
|
||||||
static inline SCLPEventFacility *get_event_facility(void)
|
static inline SCLPEventFacility *get_event_facility(void)
|
||||||
{
|
{
|
||||||
|
@ -62,7 +63,8 @@ static void read_SCP_info(SCCB *sccb)
|
||||||
read_info->entries[i].type = 0;
|
read_info->entries[i].type = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO);
|
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
|
||||||
|
SCLP_HAS_PCI_RECONFIG);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The storage increment size is a multiple of 1M and is a power of 2.
|
* The storage increment size is a multiple of 1M and is a power of 2.
|
||||||
|
@ -350,6 +352,12 @@ static void sclp_execute(SCCB *sccb, uint32_t code)
|
||||||
case SCLP_UNASSIGN_STORAGE:
|
case SCLP_UNASSIGN_STORAGE:
|
||||||
unassign_storage(sccb);
|
unassign_storage(sccb);
|
||||||
break;
|
break;
|
||||||
|
case SCLP_CMDW_CONFIGURE_PCI:
|
||||||
|
s390_pci_sclp_configure(1, sccb);
|
||||||
|
break;
|
||||||
|
case SCLP_CMDW_DECONFIGURE_PCI:
|
||||||
|
s390_pci_sclp_configure(0, sccb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
efc->command_handler(ef, sccb, code);
|
efc->command_handler(ef, sccb, code);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -43,14 +43,22 @@
|
||||||
#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
|
#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
|
||||||
#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
|
#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
|
||||||
|
|
||||||
|
/* SCLP PCI codes */
|
||||||
|
#define SCLP_HAS_PCI_RECONFIG 0x0000000040000000ULL
|
||||||
|
#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001
|
||||||
|
#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001
|
||||||
|
#define SCLP_RECONFIG_PCI_ATPYE 2
|
||||||
|
|
||||||
/* SCLP response codes */
|
/* SCLP response codes */
|
||||||
#define SCLP_RC_NORMAL_READ_COMPLETION 0x0010
|
#define SCLP_RC_NORMAL_READ_COMPLETION 0x0010
|
||||||
#define SCLP_RC_NORMAL_COMPLETION 0x0020
|
#define SCLP_RC_NORMAL_COMPLETION 0x0020
|
||||||
#define SCLP_RC_SCCB_BOUNDARY_VIOLATION 0x0100
|
#define SCLP_RC_SCCB_BOUNDARY_VIOLATION 0x0100
|
||||||
|
#define SCLP_RC_NO_ACTION_REQUIRED 0x0120
|
||||||
#define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0
|
#define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0
|
||||||
#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340
|
#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340
|
||||||
#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300
|
#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300
|
||||||
#define SCLP_RC_STANDBY_READ_COMPLETION 0x0410
|
#define SCLP_RC_STANDBY_READ_COMPLETION 0x0410
|
||||||
|
#define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED 0x09f0
|
||||||
#define SCLP_RC_INVALID_FUNCTION 0x40f0
|
#define SCLP_RC_INVALID_FUNCTION 0x40f0
|
||||||
#define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0
|
#define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0
|
||||||
#define SCLP_RC_INVALID_SELECTION_MASK 0x70f0
|
#define SCLP_RC_INVALID_SELECTION_MASK 0x70f0
|
||||||
|
|
|
@ -158,6 +158,7 @@ extern bool kvm_readonly_mem_allowed;
|
||||||
|
|
||||||
struct kvm_run;
|
struct kvm_run;
|
||||||
struct kvm_lapic_state;
|
struct kvm_lapic_state;
|
||||||
|
struct kvm_irq_routing_entry;
|
||||||
|
|
||||||
typedef struct KVMCapabilityInfo {
|
typedef struct KVMCapabilityInfo {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -270,6 +271,9 @@ int kvm_arch_on_sigbus(int code, void *addr);
|
||||||
|
|
||||||
void kvm_arch_init_irq_routing(KVMState *s);
|
void kvm_arch_init_irq_routing(KVMState *s);
|
||||||
|
|
||||||
|
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||||
|
uint64_t address, uint32_t data);
|
||||||
|
|
||||||
int kvm_set_irq(KVMState *s, int irq, int level);
|
int kvm_set_irq(KVMState *s, int irq, int level);
|
||||||
int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg);
|
int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg);
|
||||||
|
|
||||||
|
|
|
@ -1225,6 +1225,10 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg)
|
||||||
kroute.u.msi.address_lo = (uint32_t)msg.address;
|
kroute.u.msi.address_lo = (uint32_t)msg.address;
|
||||||
kroute.u.msi.address_hi = msg.address >> 32;
|
kroute.u.msi.address_hi = msg.address >> 32;
|
||||||
kroute.u.msi.data = le32_to_cpu(msg.data);
|
kroute.u.msi.data = le32_to_cpu(msg.data);
|
||||||
|
if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data)) {
|
||||||
|
kvm_irqchip_release_virq(s, virq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
kvm_add_routing_entry(s, &kroute);
|
kvm_add_routing_entry(s, &kroute);
|
||||||
kvm_irqchip_commit_routes(s);
|
kvm_irqchip_commit_routes(s);
|
||||||
|
@ -1250,6 +1254,9 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg)
|
||||||
kroute.u.msi.address_lo = (uint32_t)msg.address;
|
kroute.u.msi.address_lo = (uint32_t)msg.address;
|
||||||
kroute.u.msi.address_hi = msg.address >> 32;
|
kroute.u.msi.address_hi = msg.address >> 32;
|
||||||
kroute.u.msi.data = le32_to_cpu(msg.data);
|
kroute.u.msi.data = le32_to_cpu(msg.data);
|
||||||
|
if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return kvm_update_routing_entry(s, &kroute);
|
return kvm_update_routing_entry(s, &kroute);
|
||||||
}
|
}
|
||||||
|
|
|
@ -548,3 +548,9 @@ int kvm_arch_irqchip_create(KVMState *s)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||||
|
uint64_t address, uint32_t data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -2733,3 +2733,9 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id)
|
||||||
return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSIX |
|
return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSIX |
|
||||||
KVM_DEV_IRQ_HOST_MSIX);
|
KVM_DEV_IRQ_HOST_MSIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||||
|
uint64_t address, uint32_t data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -688,3 +688,9 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||||
|
uint64_t address, uint32_t data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -2402,3 +2402,9 @@ out_close:
|
||||||
error_out:
|
error_out:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||||
|
uint64_t address, uint32_t data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -457,87 +457,6 @@ int css_enable_mss(void);
|
||||||
int css_do_rsch(SubchDev *sch);
|
int css_do_rsch(SubchDev *sch);
|
||||||
int css_do_rchp(uint8_t cssid, uint8_t chpid);
|
int css_do_rchp(uint8_t cssid, uint8_t chpid);
|
||||||
bool css_present(uint8_t cssid);
|
bool css_present(uint8_t cssid);
|
||||||
#else
|
|
||||||
static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
|
|
||||||
uint16_t schid)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
static inline bool css_subch_visible(SubchDev *sch)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static inline void css_conditional_io_interrupt(SubchDev *sch)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_xsch(SubchDev *sch)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_csch(SubchDev *sch)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_hsch(SubchDev *sch)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_ssch(SubchDev *sch, ORB *orb)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_tsch(SubchDev *sch, IRB *irb)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_stcrw(CRW *crw)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
static inline int css_do_tpi(IOIntCode *int_code, int lowcore)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
|
|
||||||
int rfmt, uint8_t l_chpid, void *buf)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static inline int css_enable_mss(void)
|
|
||||||
{
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
static inline int css_enable_mcsse(void)
|
|
||||||
{
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
static inline int css_do_rsch(SubchDev *sch)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
static inline bool css_present(uint8_t cssid)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define cpu_init(model) (&cpu_s390x_init(model)->env)
|
#define cpu_init(model) (&cpu_s390x_init(model)->env)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "ioinst.h"
|
#include "ioinst.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "hw/s390x/s390-pci-bus.h"
|
||||||
|
|
||||||
int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
|
int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
|
||||||
int *schid)
|
int *schid)
|
||||||
|
@ -398,6 +399,7 @@ typedef struct ChscResp {
|
||||||
#define CHSC_SCPD 0x0002
|
#define CHSC_SCPD 0x0002
|
||||||
#define CHSC_SCSC 0x0010
|
#define CHSC_SCSC 0x0010
|
||||||
#define CHSC_SDA 0x0031
|
#define CHSC_SDA 0x0031
|
||||||
|
#define CHSC_SEI 0x000e
|
||||||
|
|
||||||
#define CHSC_SCPD_0_M 0x20000000
|
#define CHSC_SCPD_0_M 0x20000000
|
||||||
#define CHSC_SCPD_0_C 0x10000000
|
#define CHSC_SCPD_0_C 0x10000000
|
||||||
|
@ -566,6 +568,53 @@ out:
|
||||||
res->param = 0;
|
res->param = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int chsc_sei_nt0_get_event(void *res)
|
||||||
|
{
|
||||||
|
/* no events yet */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chsc_sei_nt0_have_event(void)
|
||||||
|
{
|
||||||
|
/* no events yet */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHSC_SEI_NT0 (1ULL << 63)
|
||||||
|
#define CHSC_SEI_NT2 (1ULL << 61)
|
||||||
|
static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res)
|
||||||
|
{
|
||||||
|
uint64_t selection_mask = ldq_p(&req->param1);
|
||||||
|
uint8_t *res_flags = (uint8_t *)res->data;
|
||||||
|
int have_event = 0;
|
||||||
|
int have_more = 0;
|
||||||
|
|
||||||
|
/* regarding architecture nt0 can not be masked */
|
||||||
|
have_event = !chsc_sei_nt0_get_event(res);
|
||||||
|
have_more = chsc_sei_nt0_have_event();
|
||||||
|
|
||||||
|
if (selection_mask & CHSC_SEI_NT2) {
|
||||||
|
if (!have_event) {
|
||||||
|
have_event = !chsc_sei_nt2_get_event(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_more) {
|
||||||
|
have_more = chsc_sei_nt2_have_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_event) {
|
||||||
|
res->code = cpu_to_be16(0x0001);
|
||||||
|
if (have_more) {
|
||||||
|
(*res_flags) |= 0x80;
|
||||||
|
} else {
|
||||||
|
(*res_flags) &= ~0x80;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res->code = cpu_to_be16(0x0004);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ioinst_handle_chsc_unimplemented(ChscResp *res)
|
static void ioinst_handle_chsc_unimplemented(ChscResp *res)
|
||||||
{
|
{
|
||||||
res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
|
res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
|
||||||
|
@ -617,6 +666,9 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb)
|
||||||
case CHSC_SDA:
|
case CHSC_SDA:
|
||||||
ioinst_handle_chsc_sda(req, res);
|
ioinst_handle_chsc_sda(req, res);
|
||||||
break;
|
break;
|
||||||
|
case CHSC_SEI:
|
||||||
|
ioinst_handle_chsc_sei(req, res);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ioinst_handle_chsc_unimplemented(res);
|
ioinst_handle_chsc_unimplemented(res);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -204,6 +204,7 @@ typedef struct CRW {
|
||||||
|
|
||||||
#define CRW_RSC_SUBCH 0x3
|
#define CRW_RSC_SUBCH 0x3
|
||||||
#define CRW_RSC_CHP 0x4
|
#define CRW_RSC_CHP 0x4
|
||||||
|
#define CRW_RSC_CSS 0xb
|
||||||
|
|
||||||
/* I/O interruption code */
|
/* I/O interruption code */
|
||||||
typedef struct IOIntCode {
|
typedef struct IOIntCode {
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qapi-event.h"
|
#include "qapi-event.h"
|
||||||
|
#include "hw/s390x/s390-pci-inst.h"
|
||||||
|
#include "hw/s390x/s390-pci-bus.h"
|
||||||
|
|
||||||
/* #define DEBUG_KVM */
|
/* #define DEBUG_KVM */
|
||||||
|
|
||||||
|
@ -56,6 +58,7 @@
|
||||||
#define IPA0_B2 0xb200
|
#define IPA0_B2 0xb200
|
||||||
#define IPA0_B9 0xb900
|
#define IPA0_B9 0xb900
|
||||||
#define IPA0_EB 0xeb00
|
#define IPA0_EB 0xeb00
|
||||||
|
#define IPA0_E3 0xe300
|
||||||
|
|
||||||
#define PRIV_B2_SCLP_CALL 0x20
|
#define PRIV_B2_SCLP_CALL 0x20
|
||||||
#define PRIV_B2_CSCH 0x30
|
#define PRIV_B2_CSCH 0x30
|
||||||
|
@ -76,8 +79,17 @@
|
||||||
#define PRIV_B2_XSCH 0x76
|
#define PRIV_B2_XSCH 0x76
|
||||||
|
|
||||||
#define PRIV_EB_SQBS 0x8a
|
#define PRIV_EB_SQBS 0x8a
|
||||||
|
#define PRIV_EB_PCISTB 0xd0
|
||||||
|
#define PRIV_EB_SIC 0xd1
|
||||||
|
|
||||||
#define PRIV_B9_EQBS 0x9c
|
#define PRIV_B9_EQBS 0x9c
|
||||||
|
#define PRIV_B9_CLP 0xa0
|
||||||
|
#define PRIV_B9_PCISTG 0xd0
|
||||||
|
#define PRIV_B9_PCILG 0xd2
|
||||||
|
#define PRIV_B9_RPCIT 0xd3
|
||||||
|
|
||||||
|
#define PRIV_E3_MPCIFC 0xd0
|
||||||
|
#define PRIV_E3_STPCIFC 0xd4
|
||||||
|
|
||||||
#define DIAG_IPL 0x308
|
#define DIAG_IPL 0x308
|
||||||
#define DIAG_KVM_HYPERCALL 0x500
|
#define DIAG_KVM_HYPERCALL 0x500
|
||||||
|
@ -202,6 +214,11 @@ void kvm_s390_reset_vcpu(S390CPU *cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int can_sync_regs(CPUState *cs, int regs)
|
||||||
|
{
|
||||||
|
return cap_sync_regs && (cs->kvm_run->kvm_valid_regs & regs) == regs;
|
||||||
|
}
|
||||||
|
|
||||||
int kvm_arch_put_registers(CPUState *cs, int level)
|
int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
{
|
{
|
||||||
S390CPU *cpu = S390_CPU(cs);
|
S390CPU *cpu = S390_CPU(cs);
|
||||||
|
@ -216,7 +233,7 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
cs->kvm_run->psw_addr = env->psw.addr;
|
cs->kvm_run->psw_addr = env->psw.addr;
|
||||||
cs->kvm_run->psw_mask = env->psw.mask;
|
cs->kvm_run->psw_mask = env->psw.mask;
|
||||||
|
|
||||||
if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) {
|
if (can_sync_regs(cs, KVM_SYNC_GPRS)) {
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
cs->kvm_run->s.regs.gprs[i] = env->regs[i];
|
cs->kvm_run->s.regs.gprs[i] = env->regs[i];
|
||||||
cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS;
|
cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS;
|
||||||
|
@ -247,6 +264,14 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (can_sync_regs(cs, KVM_SYNC_ARCH0)) {
|
||||||
|
cs->kvm_run->s.regs.cputm = env->cputm;
|
||||||
|
cs->kvm_run->s.regs.ckc = env->ckc;
|
||||||
|
cs->kvm_run->s.regs.todpr = env->todpr;
|
||||||
|
cs->kvm_run->s.regs.gbea = env->gbea;
|
||||||
|
cs->kvm_run->s.regs.pp = env->pp;
|
||||||
|
cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ARCH0;
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* These ONE_REGS are not protected by a capability. As they are only
|
* These ONE_REGS are not protected by a capability. As they are only
|
||||||
* necessary for migration we just trace a possible error, but don't
|
* necessary for migration we just trace a possible error, but don't
|
||||||
|
@ -257,8 +282,15 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
kvm_set_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr);
|
kvm_set_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr);
|
||||||
kvm_set_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea);
|
kvm_set_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea);
|
||||||
kvm_set_one_reg(cs, KVM_REG_S390_PP, &env->pp);
|
kvm_set_one_reg(cs, KVM_REG_S390_PP, &env->pp);
|
||||||
|
}
|
||||||
|
|
||||||
if (cap_async_pf) {
|
/* pfault parameters */
|
||||||
|
if (can_sync_regs(cs, KVM_SYNC_PFAULT)) {
|
||||||
|
cs->kvm_run->s.regs.pft = env->pfault_token;
|
||||||
|
cs->kvm_run->s.regs.pfs = env->pfault_select;
|
||||||
|
cs->kvm_run->s.regs.pfc = env->pfault_compare;
|
||||||
|
cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PFAULT;
|
||||||
|
} else if (cap_async_pf) {
|
||||||
r = kvm_set_one_reg(cs, KVM_REG_S390_PFTOKEN, &env->pfault_token);
|
r = kvm_set_one_reg(cs, KVM_REG_S390_PFTOKEN, &env->pfault_token);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return r;
|
return r;
|
||||||
|
@ -273,9 +305,8 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cap_sync_regs &&
|
/* access registers and control registers*/
|
||||||
cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS &&
|
if (can_sync_regs(cs, KVM_SYNC_ACRS | KVM_SYNC_CRS)) {
|
||||||
cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) {
|
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
cs->kvm_run->s.regs.acrs[i] = env->aregs[i];
|
cs->kvm_run->s.regs.acrs[i] = env->aregs[i];
|
||||||
cs->kvm_run->s.regs.crs[i] = env->cregs[i];
|
cs->kvm_run->s.regs.crs[i] = env->cregs[i];
|
||||||
|
@ -294,7 +325,7 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finally the prefix */
|
/* Finally the prefix */
|
||||||
if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) {
|
if (can_sync_regs(cs, KVM_SYNC_PREFIX)) {
|
||||||
cs->kvm_run->s.regs.prefix = env->psa;
|
cs->kvm_run->s.regs.prefix = env->psa;
|
||||||
cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX;
|
cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX;
|
||||||
} else {
|
} else {
|
||||||
|
@ -317,7 +348,7 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||||
env->psw.mask = cs->kvm_run->psw_mask;
|
env->psw.mask = cs->kvm_run->psw_mask;
|
||||||
|
|
||||||
/* the GPRS */
|
/* the GPRS */
|
||||||
if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) {
|
if (can_sync_regs(cs, KVM_SYNC_GPRS)) {
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
env->regs[i] = cs->kvm_run->s.regs.gprs[i];
|
env->regs[i] = cs->kvm_run->s.regs.gprs[i];
|
||||||
}
|
}
|
||||||
|
@ -332,9 +363,7 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The ACRS and CRS */
|
/* The ACRS and CRS */
|
||||||
if (cap_sync_regs &&
|
if (can_sync_regs(cs, KVM_SYNC_ACRS | KVM_SYNC_CRS)) {
|
||||||
cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS &&
|
|
||||||
cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) {
|
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
env->aregs[i] = cs->kvm_run->s.regs.acrs[i];
|
env->aregs[i] = cs->kvm_run->s.regs.acrs[i];
|
||||||
env->cregs[i] = cs->kvm_run->s.regs.crs[i];
|
env->cregs[i] = cs->kvm_run->s.regs.crs[i];
|
||||||
|
@ -361,10 +390,17 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||||
env->fpc = fpu.fpc;
|
env->fpc = fpu.fpc;
|
||||||
|
|
||||||
/* The prefix */
|
/* The prefix */
|
||||||
if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) {
|
if (can_sync_regs(cs, KVM_SYNC_PREFIX)) {
|
||||||
env->psa = cs->kvm_run->s.regs.prefix;
|
env->psa = cs->kvm_run->s.regs.prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (can_sync_regs(cs, KVM_SYNC_ARCH0)) {
|
||||||
|
env->cputm = cs->kvm_run->s.regs.cputm;
|
||||||
|
env->ckc = cs->kvm_run->s.regs.ckc;
|
||||||
|
env->todpr = cs->kvm_run->s.regs.todpr;
|
||||||
|
env->gbea = cs->kvm_run->s.regs.gbea;
|
||||||
|
env->pp = cs->kvm_run->s.regs.pp;
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* These ONE_REGS are not protected by a capability. As they are only
|
* These ONE_REGS are not protected by a capability. As they are only
|
||||||
* necessary for migration we just trace a possible error, but don't
|
* necessary for migration we just trace a possible error, but don't
|
||||||
|
@ -375,8 +411,14 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||||
kvm_get_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr);
|
kvm_get_one_reg(cs, KVM_REG_S390_TODPR, &env->todpr);
|
||||||
kvm_get_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea);
|
kvm_get_one_reg(cs, KVM_REG_S390_GBEA, &env->gbea);
|
||||||
kvm_get_one_reg(cs, KVM_REG_S390_PP, &env->pp);
|
kvm_get_one_reg(cs, KVM_REG_S390_PP, &env->pp);
|
||||||
|
}
|
||||||
|
|
||||||
if (cap_async_pf) {
|
/* pfault parameters */
|
||||||
|
if (can_sync_regs(cs, KVM_SYNC_PFAULT)) {
|
||||||
|
env->pfault_token = cs->kvm_run->s.regs.pft;
|
||||||
|
env->pfault_select = cs->kvm_run->s.regs.pfs;
|
||||||
|
env->pfault_compare = cs->kvm_run->s.regs.pfc;
|
||||||
|
} else if (cap_async_pf) {
|
||||||
r = kvm_get_one_reg(cs, KVM_REG_S390_PFTOKEN, &env->pfault_token);
|
r = kvm_get_one_reg(cs, KVM_REG_S390_PFTOKEN, &env->pfault_token);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return r;
|
return r;
|
||||||
|
@ -809,11 +851,124 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t get_base_disp_rxy(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
uint32_t x2 = (run->s390_sieic.ipa & 0x000f);
|
||||||
|
uint32_t base2 = run->s390_sieic.ipb >> 28;
|
||||||
|
uint32_t disp2 = ((run->s390_sieic.ipb & 0x0fff0000) >> 16) +
|
||||||
|
((run->s390_sieic.ipb & 0xff00) << 4);
|
||||||
|
|
||||||
|
if (disp2 & 0x80000) {
|
||||||
|
disp2 += 0xfff00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (base2 ? env->regs[base2] : 0) +
|
||||||
|
(x2 ? env->regs[x2] : 0) + (long)(int)disp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_base_disp_rsy(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
CPUS390XState *env = &cpu->env;
|
||||||
|
uint32_t base2 = run->s390_sieic.ipb >> 28;
|
||||||
|
uint32_t disp2 = ((run->s390_sieic.ipb & 0x0fff0000) >> 16) +
|
||||||
|
((run->s390_sieic.ipb & 0xff00) << 4);
|
||||||
|
|
||||||
|
if (disp2 & 0x80000) {
|
||||||
|
disp2 += 0xfff00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (base2 ? env->regs[base2] : 0) + (long)(int)disp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_clp_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16;
|
||||||
|
|
||||||
|
return clp_service_call(cpu, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_pcilg_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20;
|
||||||
|
uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16;
|
||||||
|
|
||||||
|
return pcilg_service_call(cpu, r1, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_pcistg_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20;
|
||||||
|
uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16;
|
||||||
|
|
||||||
|
return pcistg_service_call(cpu, r1, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_stpcifc_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
|
||||||
|
uint64_t fiba;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
fiba = get_base_disp_rxy(cpu, run);
|
||||||
|
|
||||||
|
return stpcifc_service_call(cpu, r1, fiba);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
/* NOOP */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_rpcit_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20;
|
||||||
|
uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16;
|
||||||
|
|
||||||
|
return rpcit_service_call(cpu, r1, r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_pcistb_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
|
||||||
|
uint8_t r3 = run->s390_sieic.ipa & 0x000f;
|
||||||
|
uint64_t gaddr;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
gaddr = get_base_disp_rsy(cpu, run);
|
||||||
|
|
||||||
|
return pcistb_service_call(cpu, r1, r3, gaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run)
|
||||||
|
{
|
||||||
|
uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
|
||||||
|
uint64_t fiba;
|
||||||
|
|
||||||
|
cpu_synchronize_state(CPU(cpu));
|
||||||
|
fiba = get_base_disp_rxy(cpu, run);
|
||||||
|
|
||||||
|
return mpcifc_service_call(cpu, r1, fiba);
|
||||||
|
}
|
||||||
|
|
||||||
static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
|
||||||
{
|
{
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
switch (ipa1) {
|
switch (ipa1) {
|
||||||
|
case PRIV_B9_CLP:
|
||||||
|
r = kvm_clp_service_call(cpu, run);
|
||||||
|
break;
|
||||||
|
case PRIV_B9_PCISTG:
|
||||||
|
r = kvm_pcistg_service_call(cpu, run);
|
||||||
|
break;
|
||||||
|
case PRIV_B9_PCILG:
|
||||||
|
r = kvm_pcilg_service_call(cpu, run);
|
||||||
|
break;
|
||||||
|
case PRIV_B9_RPCIT:
|
||||||
|
r = kvm_rpcit_service_call(cpu, run);
|
||||||
|
break;
|
||||||
case PRIV_B9_EQBS:
|
case PRIV_B9_EQBS:
|
||||||
/* just inject exception */
|
/* just inject exception */
|
||||||
r = -1;
|
r = -1;
|
||||||
|
@ -832,6 +987,12 @@ static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl)
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
switch (ipbl) {
|
switch (ipbl) {
|
||||||
|
case PRIV_EB_PCISTB:
|
||||||
|
r = kvm_pcistb_service_call(cpu, run);
|
||||||
|
break;
|
||||||
|
case PRIV_EB_SIC:
|
||||||
|
r = kvm_sic_service_call(cpu, run);
|
||||||
|
break;
|
||||||
case PRIV_EB_SQBS:
|
case PRIV_EB_SQBS:
|
||||||
/* just inject exception */
|
/* just inject exception */
|
||||||
r = -1;
|
r = -1;
|
||||||
|
@ -845,6 +1006,26 @@ static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_e3(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
switch (ipbl) {
|
||||||
|
case PRIV_E3_MPCIFC:
|
||||||
|
r = kvm_mpcifc_service_call(cpu, run);
|
||||||
|
break;
|
||||||
|
case PRIV_E3_STPCIFC:
|
||||||
|
r = kvm_stpcifc_service_call(cpu, run);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r = -1;
|
||||||
|
DPRINTF("KVM: unhandled PRIV: 0xe3%x\n", ipbl);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
|
static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
|
||||||
{
|
{
|
||||||
CPUS390XState *env = &cpu->env;
|
CPUS390XState *env = &cpu->env;
|
||||||
|
@ -1041,6 +1222,9 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
||||||
case IPA0_EB:
|
case IPA0_EB:
|
||||||
r = handle_eb(cpu, run, run->s390_sieic.ipb & 0xff);
|
r = handle_eb(cpu, run, run->s390_sieic.ipb & 0xff);
|
||||||
break;
|
break;
|
||||||
|
case IPA0_E3:
|
||||||
|
r = handle_e3(cpu, run, run->s390_sieic.ipb & 0xff);
|
||||||
|
break;
|
||||||
case IPA0_DIAG:
|
case IPA0_DIAG:
|
||||||
r = handle_diag(cpu, run, run->s390_sieic.ipb);
|
r = handle_diag(cpu, run, run->s390_sieic.ipb);
|
||||||
break;
|
break;
|
||||||
|
@ -1361,3 +1545,28 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
|
||||||
|
uint64_t address, uint32_t data)
|
||||||
|
{
|
||||||
|
S390PCIBusDevice *pbdev;
|
||||||
|
uint32_t fid = data >> ZPCI_MSI_VEC_BITS;
|
||||||
|
uint32_t vec = data & ZPCI_MSI_VEC_MASK;
|
||||||
|
|
||||||
|
pbdev = s390_pci_find_dev_by_fid(fid);
|
||||||
|
if (!pbdev) {
|
||||||
|
DPRINTF("add_msi_route no dev\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbdev->routes.adapter.ind_offset = vec;
|
||||||
|
|
||||||
|
route->type = KVM_IRQ_ROUTING_S390_ADAPTER;
|
||||||
|
route->flags = 0;
|
||||||
|
route->u.adapter.summary_addr = pbdev->routes.adapter.summary_addr;
|
||||||
|
route->u.adapter.ind_addr = pbdev->routes.adapter.ind_addr;
|
||||||
|
route->u.adapter.summary_offset = pbdev->routes.adapter.summary_offset;
|
||||||
|
route->u.adapter.ind_offset = pbdev->routes.adapter.ind_offset;
|
||||||
|
route->u.adapter.adapter_id = pbdev->routes.adapter.adapter_id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue