diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 7d4ff8120a817c2cf1cc2f6eaaa9f7dc862b3bff..a22ad8d016a101390779a8c66176fff0a4362859 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -44,6 +44,10 @@ typedef struct SCLPConsoleLM { uint8_t buf[SIZE_CONSOLE_BUFFER]; } SCLPConsoleLM; +#define TYPE_SCLPLM_CONSOLE "sclplmconsole" +#define SCLPLM_CONSOLE(obj) \ + OBJECT_CHECK(SCLPConsoleLM, (obj), TYPE_SCLPLM_CONSOLE) + /* * Character layer call-back functions * @@ -116,7 +120,7 @@ static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, { int len; - SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *cons = SCLPLM_CONSOLE(event); len = cons->length; /* data need to fit into provided SCLP buffer */ @@ -190,7 +194,7 @@ static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) int ret = 0; const uint8_t *buf_offset; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); if (!scon->chr) { /* If there's no backend, we can just say we consumed all data. */ @@ -244,7 +248,7 @@ static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) int errors = 0; MDBO *mdbo; SclpMsg *data = (SclpMsg *) ebh; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); len = be16_to_cpu(data->mdb.header.length); if (len < sizeof(data->mdb.header)) { @@ -313,7 +317,7 @@ static int console_init(SCLPEvent *event) { static bool console_available; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); if (console_available) { error_report("Multiple line-mode operator consoles are not supported"); @@ -336,7 +340,7 @@ static int console_exit(SCLPEvent *event) static void console_reset(DeviceState *dev) { SCLPEvent *event = SCLP_EVENT(dev); - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); event->event_pending = false; scon->length = 0; diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 45997ff4ae77eb4e6f2bba70bf1e6b1dc932c57b..15a5b2b2b58b697d54dc6ee7e1bc76ace74b4342 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -40,6 +40,10 @@ typedef struct SCLPConsole { bool notify; /* qemu_notify_event() req'd if true */ } SCLPConsole; +#define TYPE_SCLP_CONSOLE "sclpconsole" +#define SCLP_CONSOLE(obj) \ + OBJECT_CHECK(SCLPConsole, (obj), TYPE_SCLP_CONSOLE) + /* character layer call-back functions */ /* Return number of bytes that fit into iov buffer */ @@ -95,7 +99,7 @@ static unsigned int receive_mask(void) static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, int avail) { - SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *cons = SCLP_CONSOLE(event); /* first byte is hex 0 saying an ascii string follows */ *buf++ = '\0'; @@ -157,7 +161,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, size_t len) { - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *scon = SCLP_CONSOLE(event); if (!scon->chr) { /* If there's no backend, we can just say we consumed all data. */ @@ -214,7 +218,7 @@ static int console_init(SCLPEvent *event) { static bool console_available; - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *scon = SCLP_CONSOLE(event); if (console_available) { error_report("Multiple VT220 operator consoles are not supported"); @@ -232,7 +236,7 @@ static int console_init(SCLPEvent *event) static void console_reset(DeviceState *dev) { SCLPEvent *event = SCLP_EVENT(dev); - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *scon = SCLP_CONSOLE(event); event->event_pending = false; scon->iov_sclp = 0; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index f1042002731139a10348339d5db977713c8deaa9..5786b5e451ba4cb4da46e343bcb4a9b57891d994 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -30,6 +30,24 @@ #define ZIPL_IMAGE_START 0x009000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) +static bool iplb_extended_needed(void *opaque) +{ + S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); + + return ipl->iplbext_migration; +} + +static const VMStateDescription vmstate_iplb_extended = { + .name = "ipl/iplb_extended", + .version_id = 0, + .minimum_version_id = 0, + .needed = iplb_extended_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_iplb = { .name = "ipl/iplb", .version_id = 0, @@ -39,6 +57,10 @@ static const VMStateDescription vmstate_iplb = { VMSTATE_UINT16(devno, IplParameterBlock), VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_iplb_extended, + NULL } }; @@ -181,46 +203,32 @@ static Property s390_ipl_properties[] = { DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), DEFINE_PROP_STRING("firmware", S390IPLState, firmware), DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), + DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, + true), DEFINE_PROP_END_OF_LIST(), }; -/* - * In addition to updating the iplstate, this function returns: - * - 0 if system was ipled with external kernel - * - -1 if no valid boot device was found - * - ccw id of the boot device otherwise - */ -static uint64_t s390_update_iplstate(S390IPLState *ipl) +static bool s390_gen_initial_iplb(S390IPLState *ipl) { DeviceState *dev_st; - if (ipl->iplb_valid) { - ipl->cssid = 0; - ipl->ssid = 0; - ipl->devno = ipl->iplb.devno; - goto out; - } - - if (ipl->kernel) { - return 0; - } - dev_st = get_boot_device(0); if (dev_st) { VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast( OBJECT(qdev_get_parent_bus(dev_st)->parent), TYPE_VIRTIO_CCW_DEVICE); if (ccw_dev) { - ipl->cssid = ccw_dev->sch->cssid; - ipl->ssid = ccw_dev->sch->ssid; - ipl->devno = ccw_dev->sch->devno; - goto out; + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); + ipl->iplb.blk0_len = + cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); + ipl->iplb.pbt = S390_IPL_TYPE_CCW; + ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); + ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; + return true; } } - return -1; -out: - return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno); + return false; } void s390_ipl_update_diag308(IplParameterBlock *iplb) @@ -258,7 +266,9 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) if (!ipl->kernel || ipl->iplb_valid) { cpu->env.psw.addr = ipl->bios_start_addr; - cpu->env.regs[7] = s390_update_iplstate(ipl); + if (!ipl->iplb_valid) { + ipl->iplb_valid = s390_gen_initial_iplb(ipl); + } } } @@ -268,6 +278,7 @@ static void s390_ipl_reset(DeviceState *dev) if (!ipl->reipl_requested) { ipl->iplb_valid = false; + memset(&ipl->iplb, 0, sizeof(IplParameterBlock)); } ipl->reipl_requested = false; } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 6b48ed7b93a46e66763284b0de1892c9bf5d5e93..9aa4d942a740522699a548b2d2bfb4320ebc3e77 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -15,11 +15,60 @@ #include "hw/qdev.h" #include "cpu.h" -typedef struct IplParameterBlock { - uint8_t reserved1[110]; - uint16_t devno; - uint8_t reserved2[88]; -} IplParameterBlock; +struct IplBlockCcw { + uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; + uint8_t reserved3[3]; + uint32_t vm_parm_len; + uint8_t nss_name[8]; + uint8_t vm_parm[64]; + uint8_t reserved4[8]; +} QEMU_PACKED; +typedef struct IplBlockCcw IplBlockCcw; + +struct IplBlockFcp { + uint8_t reserved1[305 - 1]; + uint8_t opt; + uint8_t reserved2[3]; + uint16_t reserved3; + uint16_t devno; + uint8_t reserved4[4]; + uint64_t wwpn; + uint64_t lun; + uint32_t bootprog; + uint8_t reserved5[12]; + uint64_t br_lba; + uint32_t scp_data_len; + uint8_t reserved6[260]; + uint8_t scp_data[]; +} QEMU_PACKED; +typedef struct IplBlockFcp IplBlockFcp; + +union IplParameterBlock { + struct { + uint32_t len; + uint8_t reserved0[3]; + uint8_t version; + uint32_t blk0_len; + uint8_t pbt; + uint8_t flags; + uint16_t reserved01; + uint8_t loadparm[8]; + union { + IplBlockCcw ccw; + IplBlockFcp fcp; + }; + } QEMU_PACKED; + struct { + uint8_t reserved1[110]; + uint16_t devno; + uint8_t reserved2[88]; + uint8_t reserved_ext[4096 - 200]; + } QEMU_PACKED; +} QEMU_PACKED; +typedef union IplParameterBlock IplParameterBlock; void s390_ipl_update_diag308(IplParameterBlock *iplb); void s390_ipl_prepare_cpu(S390CPU *cpu); @@ -47,7 +96,32 @@ struct S390IPLState { uint8_t cssid; uint8_t ssid; uint16_t devno; + bool iplbext_migration; }; typedef struct S390IPLState S390IPLState; +#define S390_IPL_TYPE_FCP 0x00 +#define S390_IPL_TYPE_CCW 0x02 + +#define S390_IPLB_HEADER_LEN 8 +#define S390_IPLB_MIN_CCW_LEN 200 +#define S390_IPLB_MIN_FCP_LEN 384 + +static inline bool iplb_valid_len(IplParameterBlock *iplb) +{ + return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock); +} + +static inline bool iplb_valid_ccw(IplParameterBlock *iplb) +{ + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN && + iplb->pbt == S390_IPL_TYPE_CCW; +} + +static inline bool iplb_valid_fcp(IplParameterBlock *iplb) +{ + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN && + iplb->pbt == S390_IPL_TYPE_FCP; +} + #endif diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 918b58543e75ca5f18c84f01222775b6f8011c18..a77c10ce9e724a4413acd03962a9acd3830bc344 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -15,6 +15,7 @@ #include "qemu-common.h" #include "cpu.h" #include "s390-pci-bus.h" +#include "s390-pci-inst.h" #include #include #include @@ -106,25 +107,61 @@ S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) return NULL; } -void s390_pci_sclp_configure(int configure, SCCB *sccb) +void s390_pci_sclp_configure(SCCB *sccb) { PciCfgSccb *psccb = (PciCfgSccb *)sccb; S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); uint16_t rc; + if (be16_to_cpu(sccb->h.length) < 16) { + rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; + goto out; + } + if (pbdev) { - if ((configure == 1 && pbdev->configured == true) || - (configure == 0 && pbdev->configured == false)) { + if (pbdev->configured) { rc = SCLP_RC_NO_ACTION_REQUIRED; } else { - pbdev->configured = !pbdev->configured; + pbdev->configured = true; rc = SCLP_RC_NORMAL_COMPLETION; } } else { - DPRINTF("sclp config %d no dev found\n", configure); + DPRINTF("sclp config no dev found\n"); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; } +out: + psccb->header.response_code = cpu_to_be16(rc); +} + +void s390_pci_sclp_deconfigure(SCCB *sccb) +{ + PciCfgSccb *psccb = (PciCfgSccb *)sccb; + S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); + uint16_t rc; + + if (be16_to_cpu(sccb->h.length) < 16) { + rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; + goto out; + } + if (pbdev) { + if (!pbdev->configured) { + rc = SCLP_RC_NO_ACTION_REQUIRED; + } else { + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + pbdev->configured = false; + rc = SCLP_RC_NORMAL_COMPLETION; + } + } else { + DPRINTF("sclp deconfig no dev found\n"); + rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + } +out: psccb->header.response_code = cpu_to_be16(rc); } @@ -320,7 +357,8 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - if (!pbdev->configured || !pbdev->pdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev->configured || !pbdev->pdev || + !(pbdev->fh & FH_ENABLED) || !pbdev->iommu_enabled) { return ret; } @@ -458,20 +496,21 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable) +void s390_pci_iommu_enable(S390PCIBusDevice *pbdev) { - pbdev->configured = false; + uint64_t size = pbdev->pal - pbdev->pba + 1; - if (enable) { - uint64_t size = pbdev->pal - pbdev->pba + 1; - memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), - &s390_iommu_ops, "iommu-s390", size); - memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr); - } else { - memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr); - } + memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), + &s390_iommu_ops, "iommu-s390", size); + memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr); + pbdev->iommu_enabled = true; +} - pbdev->configured = true; +void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) +{ + memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr); + object_unparent(OBJECT(&pbdev->iommu_mr)); + pbdev->iommu_enabled = false; } static void s390_pcihost_init_as(S390pciState *s) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 59fd5c9583c185c0967bf52b5775be44589fd182..2c852d47fae7e79f8373f403082e17346112c07a 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -198,11 +198,11 @@ typedef struct ChscSeiNt2Res { } QEMU_PACKED ChscSeiNt2Res; typedef struct PciCfgSccb { - SCCBHeader header; - uint8_t atype; - uint8_t reserved1; - uint16_t reserved2; - uint32_t aid; + SCCBHeader header; + uint8_t atype; + uint8_t reserved1; + uint16_t reserved2; + uint32_t aid; } QEMU_PACKED PciCfgSccb; typedef struct S390MsixInfo { @@ -219,6 +219,7 @@ typedef struct S390PCIBusDevice { bool configured; bool error_state; bool lgstg_blocked; + bool iommu_enabled; uint32_t fh; uint32_t fid; uint64_t g_iota; @@ -247,8 +248,10 @@ typedef struct 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); -void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable); +void s390_pci_sclp_configure(SCCB *sccb); +void s390_pci_sclp_deconfigure(SCCB *sccb); +void s390_pci_iommu_enable(S390PCIBusDevice *pbdev); +void s390_pci_iommu_disable(S390PCIBusDevice *pbdev); 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); diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index b28e7d14f87dae6f3abac9c8733763a073c3aca7..479375f65d5b619192c2a15d824f018460706b95 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -634,8 +634,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long); pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len); - map_indicator(&pbdev->routes.adapter, pbdev->summary_ind); - map_indicator(&pbdev->routes.adapter, pbdev->indicator); + ret = map_indicator(&pbdev->routes.adapter, pbdev->summary_ind); + if (ret) { + goto out; + } + + ret = map_indicator(&pbdev->routes.adapter, pbdev->indicator); + if (ret) { + goto out; + } pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb); pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); @@ -647,9 +654,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); return 0; +out: + release_indicator(&pbdev->routes.adapter, pbdev->summary_ind); + release_indicator(&pbdev->routes.adapter, pbdev->indicator); + pbdev->summary_ind = NULL; + pbdev->indicator = NULL; + return ret; } -static int dereg_irqs(S390PCIBusDevice *pbdev) +int pci_dereg_irqs(S390PCIBusDevice *pbdev) { release_indicator(&pbdev->routes.adapter, pbdev->summary_ind); release_indicator(&pbdev->routes.adapter, pbdev->indicator); @@ -692,24 +705,23 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) pbdev->pal = pal; pbdev->g_iota = g_iota; - s390_pcihost_iommu_configure(pbdev, true); + s390_pci_iommu_enable(pbdev); return 0; } -static void dereg_ioat(S390PCIBusDevice *pbdev) +void pci_dereg_ioat(S390PCIBusDevice *pbdev) { + s390_pci_iommu_disable(pbdev); pbdev->pba = 0; pbdev->pal = 0; pbdev->g_iota = 0; - - s390_pcihost_iommu_configure(pbdev, false); } int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) { CPUS390XState *env = &cpu->env; - uint8_t oc; + uint8_t oc, dmaas; uint32_t fh; ZpciFib fib; S390PCIBusDevice *pbdev; @@ -721,6 +733,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } oc = env->regs[r1] & 0xff; + dmaas = (env->regs[r1] >> 16) & 0xff; fh = env->regs[r1] >> 32; if (fiba & 0x7) { @@ -739,27 +752,65 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) return 0; } + if (fib.fmt != 0) { + program_interrupt(env, PGM_OPERAND, 6); + return 0; + } + switch (oc) { case ZPCI_MOD_FC_REG_INT: - if (reg_irqs(env, pbdev, fib)) { + if (pbdev->summary_ind) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else if (reg_irqs(env, pbdev, fib)) { cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_RES_NOT_AVAIL); } break; case ZPCI_MOD_FC_DEREG_INT: - dereg_irqs(pbdev); + if (!pbdev->summary_ind) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else { + pci_dereg_irqs(pbdev); + } break; case ZPCI_MOD_FC_REG_IOAT: - if (reg_ioat(env, pbdev, fib)) { + if (dmaas != 0) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); + } else if (pbdev->iommu_enabled) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else if (reg_ioat(env, pbdev, fib)) { cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES); } break; case ZPCI_MOD_FC_DEREG_IOAT: - dereg_ioat(pbdev); + if (dmaas != 0) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); + } else if (!pbdev->iommu_enabled) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else { + pci_dereg_ioat(pbdev); + } break; case ZPCI_MOD_FC_REREG_IOAT: - dereg_ioat(pbdev); - if (reg_ioat(env, pbdev, fib)) { + if (dmaas != 0) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); + } else if (!pbdev->iommu_enabled) { cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else { + pci_dereg_ioat(pbdev); + if (reg_ioat(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES); + } } break; case ZPCI_MOD_FC_RESET_ERROR: diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index 70fa71395f865f3faeed20de615dd3a358dd3569..b084f2346bc63279733ac6cb63e1cc06a7566910 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -14,6 +14,7 @@ #ifndef HW_S390_PCI_INST_H #define HW_S390_PCI_INST_H +#include "s390-pci-bus.h" #include /* CLP common request & response block size */ @@ -230,6 +231,14 @@ typedef struct ClpReqRspQueryPciGrp { #define ZPCI_PCI_LS_BUSY 2 #define ZPCI_PCI_LS_INVAL_HANDLE 3 +/* Modify PCI status codes */ +#define ZPCI_MOD_ST_RES_NOT_AVAIL 4 +#define ZPCI_MOD_ST_INSUF_RES 16 +#define ZPCI_MOD_ST_SEQUENCE 24 +#define ZPCI_MOD_ST_DMAAS_INVAL 28 +#define ZPCI_MOD_ST_FRAME_INVAL 32 +#define ZPCI_MOD_ST_ERROR_RECOVER 40 + /* Modify PCI Function Controls */ #define ZPCI_MOD_FC_REG_INT 2 #define ZPCI_MOD_FC_DEREG_INT 3 @@ -277,6 +286,8 @@ typedef struct ZpciFib { uint32_t gd; } QEMU_PACKED ZpciFib; +int pci_dereg_irqs(S390PCIBusDevice *pbdev); +void pci_dereg_ioat(S390PCIBusDevice *pbdev); 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); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e3df9c78ba80e07943d01b8c5215f4b6c9cfc802..4456fce9f1db42f2686a7a9db7858fca2aed8e78 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -25,6 +25,7 @@ #include "s390-pci-bus.h" #include "hw/s390x/storage-keys.h" #include "hw/compat.h" +#include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" static const char *const reset_dev_types[] = { @@ -190,7 +191,9 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + s390mc->ri_allowed = true; mc->init = ccw_init; mc->reset = s390_machine_reset; mc->hot_add_cpu = s390_hot_add_cpu; @@ -237,6 +240,20 @@ static inline void machine_set_dea_key_wrap(Object *obj, bool value, ms->dea_key_wrap = value; } +bool ri_allowed(void) +{ + if (kvm_enabled()) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (object_class_dynamic_cast(OBJECT_CLASS(mc), + TYPE_S390_CCW_MACHINE)) { + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + + return s390mc->ri_allowed; + } + } + return 0; +} + static inline void s390_machine_initfn(Object *obj) { object_property_add_bool(obj, "aes-key-wrap", @@ -262,6 +279,7 @@ static const TypeInfo ccw_machine_info = { .abstract = true, .instance_size = sizeof(S390CcwMachineState), .instance_init = s390_machine_initfn, + .class_size = sizeof(S390CcwMachineClass), .class_init = ccw_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, @@ -299,7 +317,16 @@ static const TypeInfo ccw_machine_info = { } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_6 \ + HW_COMPAT_2_6 \ + {\ + .driver = TYPE_S390_IPL,\ + .property = "iplbext_migration",\ + .value = "off",\ + }, + #define CCW_COMPAT_2_5 \ + CCW_COMPAT_2_6 \ HW_COMPAT_2_5 #define CCW_COMPAT_2_4 \ @@ -343,21 +370,38 @@ static const TypeInfo ccw_machine_info = { .value = "0",\ }, +static void ccw_machine_2_7_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_7_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(2_7, "2.7", true); + static void ccw_machine_2_6_instance_options(MachineState *machine) { + ccw_machine_2_7_instance_options(machine); } static void ccw_machine_2_6_class_options(MachineClass *mc) { + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + + s390mc->ri_allowed = false; + ccw_machine_2_7_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_6); } -DEFINE_CCW_MACHINE(2_6, "2.6", true); +DEFINE_CCW_MACHINE(2_6, "2.6", false); static void ccw_machine_2_5_instance_options(MachineState *machine) { + ccw_machine_2_6_instance_options(machine); } static void ccw_machine_2_5_class_options(MachineClass *mc) { + ccw_machine_2_6_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5); } DEFINE_CCW_MACHINE(2_5, "2.5", false); @@ -369,6 +413,7 @@ static void ccw_machine_2_4_instance_options(MachineState *machine) static void ccw_machine_2_4_class_options(MachineClass *mc) { + ccw_machine_2_5_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4); } DEFINE_CCW_MACHINE(2_4, "2.4", false); diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 85dbe1b600f368d855964e2001ab73f6964091b3..fca37f511e1dd48313c4484d39f07843a2f568d9 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -357,10 +357,10 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) sclp_c->unassign_storage(sclp, sccb); break; case SCLP_CMDW_CONFIGURE_PCI: - s390_pci_sclp_configure(1, sccb); + s390_pci_sclp_configure(sccb); break; case SCLP_CMDW_DECONFIGURE_PCI: - s390_pci_sclp_configure(0, sccb); + s390_pci_sclp_deconfigure(sccb); break; default: efc->command_handler(ef, sccb, code); diff --git a/include/hw/compat.h b/include/hw/compat.h index a5dbbf8984b1dd1ecf184e286f45ddd9d9e592d5..636befedb4978758759abcb89322f3df015c8c2c 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -1,6 +1,9 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H +#define HW_COMPAT_2_6 \ + /* empty */ + #define HW_COMPAT_2_5 \ {\ .driver = "isa-fdc",\ diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index ab08332fe13673bf3e4b0b3a1485cabe2865683f..a0c1fc808392026679acc48aa342db340ee9433b 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -35,6 +35,10 @@ typedef struct S390CcwMachineClass { MachineClass parent_class; /*< public >*/ + bool ri_allowed; } S390CcwMachineClass; +/* runtime-instrumentation allowed by the machine */ +bool ri_allowed(void); + #endif diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index d3978ba0506cb24050f596b2e4e710dec3486fba..ea5fd9a4797530e79e88f591f66c4de8e93a604e 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h new file mode 100644 index 0000000000000000000000000000000000000000..1cf509f49746bb61778eb37b4f65f36b08b916a1 --- /dev/null +++ b/pc-bios/s390-ccw/iplb.h @@ -0,0 +1,79 @@ +/* + * QEMU S390 IPL Block + * + * Copyright 2015 IBM Corp. + * Author(s): Alexander Yarygin + * + * 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 IPLB_H +#define IPLB_H + +struct IplBlockCcw { + uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; + uint8_t reserved3[3]; + uint32_t vm_parm_len; + uint8_t nss_name[8]; + uint8_t vm_parm[64]; + uint8_t reserved4[8]; +} __attribute__ ((packed)); +typedef struct IplBlockCcw IplBlockCcw; + +struct IplBlockFcp { + uint8_t reserved1[305 - 1]; + uint8_t opt; + uint8_t reserved2[3]; + uint16_t reserved3; + uint16_t devno; + uint8_t reserved4[4]; + uint64_t wwpn; + uint64_t lun; + uint32_t bootprog; + uint8_t reserved5[12]; + uint64_t br_lba; + uint32_t scp_data_len; + uint8_t reserved6[260]; + uint8_t scp_data[]; +} __attribute__ ((packed)); +typedef struct IplBlockFcp IplBlockFcp; + +struct IplParameterBlock { + uint32_t len; + uint8_t reserved0[3]; + uint8_t version; + uint32_t blk0_len; + uint8_t pbt; + uint8_t flags; + uint16_t reserved01; + uint8_t loadparm[8]; + union { + IplBlockCcw ccw; + IplBlockFcp fcp; + }; +} __attribute__ ((packed)); +typedef struct IplParameterBlock IplParameterBlock; + +extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); + +#define S390_IPL_TYPE_FCP 0x00 +#define S390_IPL_TYPE_CCW 0x02 + +static inline bool store_iplb(IplParameterBlock *iplb) +{ + register unsigned long addr asm("0") = (unsigned long) iplb; + register unsigned long rc asm("1") = 0; + + asm volatile ("diag %0,%2,0x308\n" + : "+d" (addr), "+d" (rc) + : "d" (6) + : "memory", "cc"); + return rc == 0x01; +} + +#endif /* IPLB_H */ diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 1c9e0791abd09fa7231d000bbf340bda44cc9909..9446ecc235a65fb9dd6b8574b8d0eda6f1428fd7 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -12,8 +12,8 @@ #include "virtio.h" char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -uint64_t boot_value; static SubChannelId blk_schid = { .one = 1 }; +IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that @@ -61,7 +61,7 @@ static bool find_dev(Schib *schib, int dev_no) return false; } -static void virtio_setup(uint64_t dev_info) +static void virtio_setup(void) { Schib schib; int ssid; @@ -75,12 +75,18 @@ static void virtio_setup(uint64_t dev_info) */ enable_mss_facility(); - if (dev_info != -1) { - dev_no = dev_info & 0xffff; - debug_print_int("device no. ", dev_no); - blk_schid.ssid = (dev_info >> 16) & 0x3; - debug_print_int("ssid ", blk_schid.ssid); - found = find_dev(&schib, dev_no); + if (store_iplb(&iplb)) { + switch (iplb.pbt) { + case S390_IPL_TYPE_CCW: + dev_no = iplb.ccw.devno; + debug_print_int("device no. ", dev_no); + blk_schid.ssid = iplb.ccw.ssid & 0x3; + debug_print_int("ssid ", blk_schid.ssid); + found = find_dev(&schib, dev_no); + break; + default: + panic("List-directed IPL not supported yet!\n"); + } } else { for (ssid = 0; ssid < 0x3; ssid++) { blk_schid.ssid = ssid; @@ -101,8 +107,7 @@ static void virtio_setup(uint64_t dev_info) int main(void) { sclp_setup(); - debug_print_int("boot reg[7] ", boot_value); - virtio_setup(boot_value); + virtio_setup(); zipl_load(); /* no return */ diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 616d96738de85e7e09f8fa45770497c9757ea231..ded67bcbc60ff9c3fabdf66bed53c0e822a90364 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -44,6 +44,7 @@ typedef unsigned long long __u64; #endif #include "cio.h" +#include "iplb.h" typedef struct irb Irb; typedef struct ccw1 Ccw1; @@ -61,7 +62,6 @@ void consume_sclp_int(void); void panic(const char *string); void write_subsystem_identification(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -extern uint64_t boot_value; /* sclp-ascii.c */ void sclp_print(const char *string); diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index b6dd8c2fbebd1b999d58a3422c88a94e68505a58..43f9bd243e143ab386308ebb2cb1f5ecd80f302c 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -14,8 +14,6 @@ _start: larl %r15, stack + 0x8000 /* Set up stack */ -larl %r6, boot_value -stg %r7, 0(%r6) /* save the boot_value before any function calls */ j main /* And call C */ /* diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 07f76ad884700df5323d7c90134f643fd8f401c6..996b79023eead356bb76044d1b0eb93e04f348de 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -135,6 +135,8 @@ typedef struct CPUS390XState { uint64_t gbea; uint64_t pp; + uint8_t riccb[64]; + CPU_COMMON /* reset does memset(0) up to here */ @@ -1159,6 +1161,7 @@ void kvm_s390_reset_vcpu(S390CPU *cpu); int kvm_s390_set_mem_limit(KVMState *s, uint64_t new_limit, uint64_t *hw_limit); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); +int kvm_s390_get_ri(void); void kvm_s390_crypto_reset(void); #else static inline void kvm_s390_io_interrupt(uint16_t subchannel_id, @@ -1209,6 +1212,10 @@ static inline int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) { return 0; } +static inline int kvm_s390_get_ri(void) +{ + return 0; +} static inline void kvm_s390_crypto_reset(void) { } @@ -1272,11 +1279,22 @@ static inline bool vregs_needed(void *opaque) } return 0; } +static inline bool riccb_needed(void *opaque) +{ + if (kvm_enabled()) { + return kvm_s390_get_ri(); + } + return 0; +} #else static inline bool vregs_needed(void *opaque) { return 0; } +static inline bool riccb_needed(void *opaque) +{ + return 0; +} #endif /* machine check interruption code */ diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c index 142ff9384472e3d26d33f9e7442be7c4ed3f9181..f5498aa023df4fe86bd536cd9c8e970981197ad7 100644 --- a/target-s390x/ioinst.c +++ b/target-s390x/ioinst.c @@ -509,6 +509,7 @@ static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res) general_chars[0] = cpu_to_be32(0x03000000); general_chars[1] = cpu_to_be32(0x00059000); + general_chars[3] = cpu_to_be32(0x00080000); chsc_chars[0] = cpu_to_be32(0x40000000); chsc_chars[3] = cpu_to_be32(0x00040000); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index e1859cae0492a4e9d09858c01262a2a6f905ba36..55ae6d3304586912bf2554d905113afe2ff0dd06 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -46,6 +46,7 @@ #include "hw/s390x/ipl.h" #include "hw/s390x/ebcdic.h" #include "exec/memattrs.h" +#include "hw/s390x/s390-virtio-ccw.h" /* #define DEBUG_KVM */ @@ -135,6 +136,7 @@ static int cap_sync_regs; static int cap_async_pf; static int cap_mem_op; static int cap_s390_irq; +static int cap_ri; static void *legacy_s390_alloc(size_t size, uint64_t *align); @@ -270,6 +272,11 @@ int kvm_arch_init(MachineState *ms, KVMState *s) kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0); + if (ri_allowed()) { + if (kvm_vm_enable_cap(s, KVM_CAP_S390_RI, 0) == 0) { + cap_ri = 1; + } + } return 0; } @@ -386,6 +393,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) kvm_set_one_reg(cs, KVM_REG_S390_PP, &env->pp); } + if (can_sync_regs(cs, KVM_SYNC_RICCB)) { + memcpy(cs->kvm_run->s.regs.riccb, env->riccb, 64); + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_RICCB; + } + /* pfault parameters */ if (can_sync_regs(cs, KVM_SYNC_PFAULT)) { cs->kvm_run->s.regs.pft = env->pfault_token; @@ -528,6 +540,10 @@ int kvm_arch_get_registers(CPUState *cs) kvm_get_one_reg(cs, KVM_REG_S390_PP, &env->pp); } + if (can_sync_regs(cs, KVM_SYNC_RICCB)) { + memcpy(env->riccb, cs->kvm_run->s.regs.riccb, 64); + } + /* pfault parameters */ if (can_sync_regs(cs, KVM_SYNC_PFAULT)) { env->pfault_token = cs->kvm_run->s.regs.pft; @@ -2136,6 +2152,11 @@ int kvm_s390_get_memslot_count(KVMState *s) return kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); } +int kvm_s390_get_ri(void) +{ + return cap_ri; +} + int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) { struct kvm_mp_state mp_state = {}; diff --git a/target-s390x/machine.c b/target-s390x/machine.c index 6b2609054b0586893b1e2ce3b024fc040eb76dfc..a30b16fe3d51e5d8f01042b93b455a5e16ae4a8a 100644 --- a/target-s390x/machine.c +++ b/target-s390x/machine.c @@ -135,6 +135,17 @@ static const VMStateDescription vmstate_vregs = { } }; +const VMStateDescription vmstate_riccb = { + .name = "cpu/riccb", + .version_id = 1, + .minimum_version_id = 1, + .needed = riccb_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(env.riccb, S390CPU, 64), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_s390_cpu = { .name = "cpu", .post_load = cpu_post_load, @@ -166,6 +177,7 @@ const VMStateDescription vmstate_s390_cpu = { .subsections = (const VMStateDescription*[]) { &vmstate_fpu, &vmstate_vregs, + &vmstate_riccb, NULL }, }; diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 71cbe34e05be8fbdcb459994dfbef9fb8d8832ec..462cfc85fcc5ad98f73bb9a28a489bc77d434d1e 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -232,10 +232,23 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) program_interrupt(env, PGM_ADDRESSING, ILEN_LATER_INC); return; } - iplb = g_malloc0(sizeof(struct IplParameterBlock)); - cpu_physical_memory_read(addr, iplb, sizeof(struct IplParameterBlock)); + iplb = g_malloc0(sizeof(IplParameterBlock)); + cpu_physical_memory_read(addr, iplb, sizeof(iplb->len)); + if (!iplb_valid_len(iplb)) { + env->regs[r1 + 1] = DIAG_308_RC_INVALID; + goto out; + } + + cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); + + if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { + env->regs[r1 + 1] = DIAG_308_RC_INVALID; + goto out; + } + s390_ipl_update_diag308(iplb); env->regs[r1 + 1] = DIAG_308_RC_OK; +out: g_free(iplb); return; case 6: @@ -250,8 +263,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) } iplb = s390_ipl_get_iplb(); if (iplb) { - cpu_physical_memory_write(addr, iplb, - sizeof(struct IplParameterBlock)); + cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len)); env->regs[r1 + 1] = DIAG_308_RC_OK; } else { env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;