diff options
-rw-r--r-- | hw/net/e1000.c | 103 | ||||
-rw-r--r-- | hw/s390x/ipl.c | 112 | ||||
-rw-r--r-- | hw/vfio/ccw.c | 2 | ||||
-rw-r--r-- | include/hw/compat.h | 4 | ||||
-rw-r--r-- | linux-user/syscall.c | 27 | ||||
-rw-r--r-- | pc-bios/s390-ccw.img | bin | 30568 -> 30520 bytes | |||
-rw-r--r-- | pc-bios/s390-ccw/bootmap.c | 7 | ||||
-rw-r--r-- | pc-bios/s390-ccw/iplb.h | 15 | ||||
-rw-r--r-- | pc-bios/s390-netboot.img | bin | 83776 -> 83856 bytes | |||
-rw-r--r-- | target/s390x/helper.c | 10 | ||||
-rw-r--r-- | target/s390x/kvm.c | 2 | ||||
-rw-r--r-- | target/s390x/mmu_helper.c | 2 |
12 files changed, 220 insertions, 64 deletions
diff --git a/hw/net/e1000.c b/hw/net/e1000.c index c7f1695f57..13a9494a8d 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -123,10 +123,15 @@ typedef struct E1000State_st { #define E1000_FLAG_AUTONEG_BIT 0 #define E1000_FLAG_MIT_BIT 1 #define E1000_FLAG_MAC_BIT 2 +#define E1000_FLAG_TSO_BIT 3 #define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT) #define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT) #define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT) +#define E1000_FLAG_TSO (1 << E1000_FLAG_TSO_BIT) uint32_t compat_flags; + bool received_tx_tso; + bool use_tso_for_migration; + e1000x_txd_props mig_props; } E1000State; #define chkflag(x) (s->compat_flags & E1000_FLAG_##x) @@ -618,9 +623,11 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */ if (le32_to_cpu(xp->cmd_and_length) & E1000_TXD_CMD_TSE) { e1000x_read_tx_ctx_descr(xp, &tp->tso_props); + s->use_tso_for_migration = 1; tp->tso_frames = 0; } else { e1000x_read_tx_ctx_descr(xp, &tp->props); + s->use_tso_for_migration = 0; } return; } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) { @@ -1362,6 +1369,20 @@ static int e1000_pre_save(void *opaque) s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; } + /* Decide which set of props to migrate in the main structure */ + if (chkflag(TSO) || !s->use_tso_for_migration) { + /* Either we're migrating with the extra subsection, in which + * case the mig_props is always 'props' OR + * we've not got the subsection, but 'props' was the last + * updated. + */ + s->mig_props = s->tx.props; + } else { + /* We're not using the subsection, and 'tso_props' was + * the last updated. + */ + s->mig_props = s->tx.tso_props; + } return 0; } @@ -1390,6 +1411,21 @@ static int e1000_post_load(void *opaque, int version_id) qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); } + s->tx.props = s->mig_props; + if (!s->received_tx_tso) { + /* We received only one set of offload data (tx.props) + * and haven't got tx.tso_props. The best we can do + * is dupe the data. + */ + s->tx.tso_props = s->mig_props; + } + return 0; +} + +static int e1000_tx_tso_post_load(void *opaque, int version_id) +{ + E1000State *s = opaque; + s->received_tx_tso = true; return 0; } @@ -1407,6 +1443,13 @@ static bool e1000_full_mac_needed(void *opaque) return chkflag(MAC); } +static bool e1000_tso_state_needed(void *opaque) +{ + E1000State *s = opaque; + + return chkflag(TSO); +} + static const VMStateDescription vmstate_e1000_mit_state = { .name = "e1000/mit_state", .version_id = 1, @@ -1433,9 +1476,31 @@ static const VMStateDescription vmstate_e1000_full_mac_state = { } }; +static const VMStateDescription vmstate_e1000_tx_tso_state = { + .name = "e1000/tx_tso_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = e1000_tso_state_needed, + .post_load = e1000_tx_tso_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(tx.tso_props.ipcss, E1000State), + VMSTATE_UINT8(tx.tso_props.ipcso, E1000State), + VMSTATE_UINT16(tx.tso_props.ipcse, E1000State), + VMSTATE_UINT8(tx.tso_props.tucss, E1000State), + VMSTATE_UINT8(tx.tso_props.tucso, E1000State), + VMSTATE_UINT16(tx.tso_props.tucse, E1000State), + VMSTATE_UINT32(tx.tso_props.paylen, E1000State), + VMSTATE_UINT8(tx.tso_props.hdr_len, E1000State), + VMSTATE_UINT16(tx.tso_props.mss, E1000State), + VMSTATE_INT8(tx.tso_props.ip, E1000State), + VMSTATE_INT8(tx.tso_props.tcp, E1000State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_e1000 = { .name = "e1000", - .version_id = 3, + .version_id = 2, .minimum_version_id = 1, .pre_save = e1000_pre_save, .post_load = e1000_post_load, @@ -1450,20 +1515,20 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT16(eecd_state.bitnum_out, E1000State), VMSTATE_UINT16(eecd_state.reading, E1000State), VMSTATE_UINT32(eecd_state.old_eecd, E1000State), - VMSTATE_UINT8(tx.props.ipcss, E1000State), - VMSTATE_UINT8(tx.props.ipcso, E1000State), - VMSTATE_UINT16(tx.props.ipcse, E1000State), - VMSTATE_UINT8(tx.props.tucss, E1000State), - VMSTATE_UINT8(tx.props.tucso, E1000State), - VMSTATE_UINT16(tx.props.tucse, E1000State), - VMSTATE_UINT32(tx.props.paylen, E1000State), - VMSTATE_UINT8(tx.props.hdr_len, E1000State), - VMSTATE_UINT16(tx.props.mss, E1000State), + VMSTATE_UINT8(mig_props.ipcss, E1000State), + VMSTATE_UINT8(mig_props.ipcso, E1000State), + VMSTATE_UINT16(mig_props.ipcse, E1000State), + VMSTATE_UINT8(mig_props.tucss, E1000State), + VMSTATE_UINT8(mig_props.tucso, E1000State), + VMSTATE_UINT16(mig_props.tucse, E1000State), + VMSTATE_UINT32(mig_props.paylen, E1000State), + VMSTATE_UINT8(mig_props.hdr_len, E1000State), + VMSTATE_UINT16(mig_props.mss, E1000State), VMSTATE_UINT16(tx.size, E1000State), VMSTATE_UINT16(tx.tso_frames, E1000State), VMSTATE_UINT8(tx.sum_needed, E1000State), - VMSTATE_INT8(tx.props.ip, E1000State), - VMSTATE_INT8(tx.props.tcp, E1000State), + VMSTATE_INT8(mig_props.ip, E1000State), + VMSTATE_INT8(mig_props.tcp, E1000State), VMSTATE_BUFFER(tx.header, E1000State), VMSTATE_BUFFER(tx.data, E1000State), VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64), @@ -1508,22 +1573,12 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32), VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128), VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128), - VMSTATE_UINT8_V(tx.tso_props.ipcss, E1000State, 3), - VMSTATE_UINT8_V(tx.tso_props.ipcso, E1000State, 3), - VMSTATE_UINT16_V(tx.tso_props.ipcse, E1000State, 3), - VMSTATE_UINT8_V(tx.tso_props.tucss, E1000State, 3), - VMSTATE_UINT8_V(tx.tso_props.tucso, E1000State, 3), - VMSTATE_UINT16_V(tx.tso_props.tucse, E1000State, 3), - VMSTATE_UINT32_V(tx.tso_props.paylen, E1000State, 3), - VMSTATE_UINT8_V(tx.tso_props.hdr_len, E1000State, 3), - VMSTATE_UINT16_V(tx.tso_props.mss, E1000State, 3), - VMSTATE_INT8_V(tx.tso_props.ip, E1000State, 3), - VMSTATE_INT8_V(tx.tso_props.tcp, E1000State, 3), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription*[]) { &vmstate_e1000_mit_state, &vmstate_e1000_full_mac_state, + &vmstate_e1000_tx_tso_state, NULL } }; @@ -1651,6 +1706,8 @@ static Property e1000_properties[] = { compat_flags, E1000_FLAG_MIT_BIT, true), DEFINE_PROP_BIT("extra_mac_registers", E1000State, compat_flags, E1000_FLAG_MAC_BIT, true), + DEFINE_PROP_BIT("migrate_tso_props", E1000State, + compat_flags, E1000_FLAG_TSO_BIT, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index fdeaec3a58..fb554ab156 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -279,44 +279,52 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) *timeout = cpu_to_be32(splash_time); } +static CcwDevice *s390_get_ccw_device(DeviceState *dev_st) +{ + CcwDevice *ccw_dev = NULL; + + if (dev_st) { + VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *) + object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent), + TYPE_VIRTIO_CCW_DEVICE); + if (virtio_ccw_dev) { + ccw_dev = CCW_DEVICE(virtio_ccw_dev); + } else { + SCSIDevice *sd = (SCSIDevice *) + object_dynamic_cast(OBJECT(dev_st), + TYPE_SCSI_DEVICE); + if (sd) { + SCSIBus *bus = scsi_bus_from_device(sd); + VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); + VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, + vdev); + + ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw), + TYPE_CCW_DEVICE); + } + } + } + return ccw_dev; +} + static bool s390_gen_initial_iplb(S390IPLState *ipl) { DeviceState *dev_st; + CcwDevice *ccw_dev = NULL; dev_st = get_boot_device(0); if (dev_st) { - VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *) - object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent), - TYPE_VIRTIO_CCW_DEVICE); + ccw_dev = s390_get_ccw_device(dev_st); + } + + /* + * Currently allow IPL only from CCW devices. + */ + if (ccw_dev) { SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st), TYPE_SCSI_DEVICE); - VirtIONet *vn = (VirtIONet *) object_dynamic_cast(OBJECT(dev_st), - TYPE_VIRTIO_NET); - - if (vn) { - ipl->netboot = true; - } - if (virtio_ccw_dev) { - CcwDevice *ccw_dev = CCW_DEVICE(virtio_ccw_dev); - - 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; - } else if (sd) { - SCSIBus *bus = scsi_bus_from_device(sd); - VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); - VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); - CcwDevice *ccw_dev; - - ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw), - TYPE_CCW_DEVICE); - if (!ccw_dev) { /* It might be a PCI device instead */ - return false; - } + if (sd) { ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); ipl->iplb.blk0_len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); @@ -327,12 +335,25 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; } else { - return false; /* unknown device */ + VirtIONet *vn = (VirtIONet *) object_dynamic_cast(OBJECT(dev_st), + TYPE_VIRTIO_NET); + + 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; + + if (vn) { + ipl->netboot = true; + } } if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) { ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID; } + return true; } @@ -406,7 +427,8 @@ unref_mr: return img_size; } -static bool is_virtio_net_device(IplParameterBlock *iplb) +static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb, + int virtio_id) { uint8_t cssid; uint8_t ssid; @@ -426,13 +448,23 @@ static bool is_virtio_net_device(IplParameterBlock *iplb) sch = css_find_subch(1, cssid, ssid, schid); if (sch && sch->devno == devno) { - return sch->id.cu_model == VIRTIO_ID_NET; + return sch->id.cu_model == virtio_id; } } } return false; } +static bool is_virtio_net_device(IplParameterBlock *iplb) +{ + return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET); +} + +static bool is_virtio_scsi_device(IplParameterBlock *iplb) +{ + return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); +} + void s390_ipl_update_diag308(IplParameterBlock *iplb) { S390IPLState *ipl = get_ipl_device(); @@ -457,6 +489,22 @@ void s390_reipl_request(void) S390IPLState *ipl = get_ipl_device(); ipl->reipl_requested = true; + if (ipl->iplb_valid && + !ipl->netboot && + ipl->iplb.pbt == S390_IPL_TYPE_CCW && + is_virtio_scsi_device(&ipl->iplb)) { + CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0)); + + if (ccw_dev && + cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno && + (ccw_dev->sch->ssid & 3) == ipl->iplb.ccw.ssid) { + /* + * this is the original boot device's SCSI + * so restore IPL parameter info from it + */ + ipl->iplb_valid = s390_gen_initial_iplb(ipl); + } + } qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 4e5855741a..fe34b50769 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -357,11 +357,13 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) { error_setg(&err, "vfio: subchannel %s has already been attached", vcdev->vdev.name); + g_free(vcdev->vdev.name); goto out_device_err; } } if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) { + g_free(vcdev->vdev.name); goto out_device_err; } diff --git a/include/hw/compat.h b/include/hw/compat.h index bc9e3a6627..13242b831a 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -14,6 +14,10 @@ .driver = "vhost-user-blk-pci",\ .property = "vectors",\ .value = "2",\ + },{\ + .driver = "e1000",\ + .property = "migrate_tso_props",\ + .value = "off",\ }, #define HW_COMPAT_2_10 \ diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 8671447aca..643b8833de 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3386,6 +3386,23 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, return ret; } +/* Convert target low/high pair representing file offset into the host + * low/high pair. This function doesn't handle offsets bigger than 64 bits + * as the kernel doesn't handle them either. + */ +static void target_to_host_low_high(abi_ulong tlow, + abi_ulong thigh, + unsigned long *hlow, + unsigned long *hhigh) +{ + uint64_t off = tlow | + ((unsigned long long)thigh << TARGET_LONG_BITS / 2) << + TARGET_LONG_BITS / 2; + + *hlow = off; + *hhigh = (off >> HOST_LONG_BITS / 2) >> HOST_LONG_BITS / 2; +} + static struct iovec *lock_iovec(int type, abi_ulong target_addr, abi_ulong count, int copy) { @@ -10452,7 +10469,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0); if (vec != NULL) { - ret = get_errno(safe_preadv(arg1, vec, arg3, arg4, arg5)); + unsigned long low, high; + + target_to_host_low_high(arg4, arg5, &low, &high); + ret = get_errno(safe_preadv(arg1, vec, arg3, low, high)); unlock_iovec(vec, arg2, arg3, 1); } else { ret = -host_to_target_errno(errno); @@ -10465,7 +10485,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); if (vec != NULL) { - ret = get_errno(safe_pwritev(arg1, vec, arg3, arg4, arg5)); + unsigned long low, high; + + target_to_host_low_high(arg4, arg5, &low, &high); + ret = get_errno(safe_pwritev(arg1, vec, arg3, low, high)); unlock_iovec(vec, arg2, arg3, 0); } else { ret = -host_to_target_errno(errno); diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex d17e85995d..fdd6809c70 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index fc2a9fe33b..9287b7a70f 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -70,6 +70,13 @@ static void jump_to_IPL_code(uint64_t address) { /* store the subsystem information _after_ the bootmap was loaded */ write_subsystem_identification(); + + /* prevent unknown IPL types in the guest */ + if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { + iplb.pbt = S390_IPL_TYPE_CCW; + set_iplb(&iplb); + } + /* * The IPL PSW is at address 0. We also must not overwrite the * content of non-BIOS memory after we loaded the guest, so we diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 7dfce4fbcf..5357a36d51 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -97,16 +97,27 @@ extern QemuIplParameters qipl; #define S390_IPL_TYPE_CCW 0x02 #define S390_IPL_TYPE_QEMU_SCSI 0xff -static inline bool store_iplb(IplParameterBlock *iplb) +static inline bool manage_iplb(IplParameterBlock *iplb, bool store) { 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) + : "d" (store ? 6 : 5) : "memory", "cc"); return rc == 0x01; } + +static inline bool store_iplb(IplParameterBlock *iplb) +{ + return manage_iplb(iplb, true); +} + +static inline bool set_iplb(IplParameterBlock *iplb) +{ + return manage_iplb(iplb, false); +} + #endif /* IPLB_H */ diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img Binary files differindex 9f5926b534..31f3d141cd 100644 --- a/pc-bios/s390-netboot.img +++ b/pc-bios/s390-netboot.img diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 615fa24ab9..e8548f340a 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -103,16 +103,18 @@ void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) env->psw.addr = addr; env->psw.mask = mask; - if (tcg_enabled()) { - env->cc_op = (mask >> 44) & 3; + + /* KVM will handle all WAITs and trigger a WAIT exit on disabled_wait */ + if (!tcg_enabled()) { + return; } + env->cc_op = (mask >> 44) & 3; if ((old_mask ^ mask) & PSW_MASK_PER) { s390_cpu_recompute_watchpoints(CPU(s390_env_get_cpu(env))); } - /* KVM will handle all WAITs and trigger a WAIT exit on disabled_wait */ - if (tcg_enabled() && (mask & PSW_MASK_WAIT)) { + if (mask & PSW_MASK_WAIT) { s390_handle_wait(s390_env_get_cpu(env)); } } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index f570896dc1..fb59d92def 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1778,6 +1778,8 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) qemu_mutex_lock_iothread(); + cpu_synchronize_state(cs); + switch (run->exit_reason) { case KVM_EXIT_S390_SIEIC: ret = handle_intercept(cpu); diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 1deeb6e6e4..a25deef5dd 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -325,7 +325,7 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw, exc); - if (rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE)) { + if (!r && rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE)) { trigger_prot_fault(env, vaddr, asc, rw, exc); return -1; } |