diff options
34 files changed, 1621 insertions, 764 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 0431d094b3..5c3c70c89b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -638,6 +638,7 @@ Subsystems ---------- Audio M: Vassili Karpov (malc) <av1474@comtv.ru> +M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: audio/ F: hw/audio/ diff --git a/arch_init.c b/arch_init.c index e47e1399bb..d14457da60 100644 --- a/arch_init.c +++ b/arch_init.c @@ -150,10 +150,9 @@ int qemu_read_default_config_files(bool userconfig) return 0; } -static inline bool is_zero_page(uint8_t *p) +static inline bool is_zero_range(uint8_t *p, uint64_t size) { - return buffer_find_nonzero_offset(p, TARGET_PAGE_SIZE) == - TARGET_PAGE_SIZE; + return buffer_find_nonzero_offset(p, size) == size; } /* struct contains XBZRLE cache and a static page @@ -497,7 +496,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage) acct_info.dup_pages++; } } - } else if (is_zero_page(p)) { + } else if (is_zero_range(p, TARGET_PAGE_SIZE)) { acct_info.dup_pages++; bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS); @@ -710,15 +709,20 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) */ ram_control_after_iterate(f, RAM_CONTROL_ROUND); + bytes_transferred += total_sent; + + /* + * Do not count these 8 bytes into total_sent, so that we can + * return 0 if no page had been dirtied. + */ + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + bytes_transferred += 8; + + ret = qemu_file_get_error(f); if (ret < 0) { - bytes_transferred += total_sent; return ret; } - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - total_sent += 8; - bytes_transferred += total_sent; - return total_sent; } @@ -844,13 +848,14 @@ static inline void *host_from_stream_offset(QEMUFile *f, */ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) { - if (ch != 0 || !is_zero_page(host)) { + if (ch != 0 || !is_zero_range(host, size)) { memset(host, ch, size); #ifndef _WIN32 - if (ch == 0 && - (!kvm_enabled() || kvm_has_sync_mmu()) && - getpagesize() <= TARGET_PAGE_SIZE) { - qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED); + if (ch == 0 && (!kvm_enabled() || kvm_has_sync_mmu())) { + size = size & ~(getpagesize() - 1); + if (size > 0) { + qemu_madvise(host, size, QEMU_MADV_DONTNEED); + } } #endif } diff --git a/audio/mixeng.c b/audio/mixeng.c index 02a9d9fb92..0e4976f271 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -348,7 +348,6 @@ void mixeng_clear (struct st_sample *buf, int len) void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) { -#ifdef CONFIG_MIXEMU if (vol->mute) { mixeng_clear (buf, len); return; @@ -364,9 +363,4 @@ void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) #endif buf += 1; } -#else - (void) buf; - (void) len; - (void) vol; -#endif } @@ -215,7 +215,6 @@ linux_user="no" bsd_user="no" guest_base="yes" uname_release="" -mixemu="no" aix="no" blobs="yes" pkgversion="" @@ -869,8 +868,6 @@ for opt do ;; --enable-fdt) fdt="yes" ;; - --enable-mixemu) mixemu="yes" - ;; --disable-linux-aio) linux_aio="no" ;; --enable-linux-aio) linux_aio="yes" @@ -1111,7 +1108,6 @@ echo " (affects only QEMU, not qemu-img)" echo " --block-drv-ro-whitelist=L" echo " set block driver read-only whitelist" echo " (affects only QEMU, not qemu-img)" -echo " --enable-mixemu enable mixer emulation" echo " --disable-xen disable xen backend driver support" echo " --enable-xen enable xen backend driver support" echo " --disable-xen-pci-passthrough" @@ -3702,7 +3698,6 @@ echo "mingw32 support $mingw32" echo "Audio drivers $audio_drv_list" echo "Block whitelist (rw) $block_drv_rw_whitelist" echo "Block whitelist (ro) $block_drv_ro_whitelist" -echo "Mixer emulation $mixemu" echo "VirtFS support $virtfs" echo "VNC support $vnc" if test "$vnc" = "yes" ; then @@ -3889,9 +3884,6 @@ if test "$audio_win_int" = "yes" ; then fi echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak -if test "$mixemu" = "yes" ; then - echo "CONFIG_MIXEMU=y" >> $config_host_mak -fi if test "$vnc" = "yes" ; then echo "CONFIG_VNC=y" >> $config_host_mak fi diff --git a/hw/audio/hda-codec-common.h b/hw/audio/hda-codec-common.h new file mode 100644 index 0000000000..b4fdb51e8b --- /dev/null +++ b/hw/audio/hda-codec-common.h @@ -0,0 +1,456 @@ +/* + * Common code to disable/enable mixer emulation at run time + * + * Copyright (C) 2013 Red Hat, Inc. + * + * Written by Bandan Das <bsd@redhat.com> + * with important bits picked up from hda-codec.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * HDA codec descriptions + */ + +#ifdef HDA_MIXER +#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x12) +#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x22) +#define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x32) +#define QEMU_HDA_AMP_CAPS \ + (AC_AMPCAP_MUTE | \ + (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ + (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ + (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) +#else +#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x11) +#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x21) +#define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x31) +#define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE +#endif + + +/* common: audio output widget */ +static const desc_param glue(common_params_audio_dac_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_FORMAT_OVRD | + AC_WCAP_AMP_OVRD | + AC_WCAP_OUT_AMP | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_CAPS, + }, +}; + +/* common: audio input widget */ +static const desc_param glue(common_params_audio_adc_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_CONN_LIST | + AC_WCAP_FORMAT_OVRD | + AC_WCAP_AMP_OVRD | + AC_WCAP_IN_AMP | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_CONNLIST_LEN, + .val = 1, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_CAPS, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* common: pin widget (line-out) */ +static const desc_param glue(common_params_audio_lineout_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_CONN_LIST | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PIN_CAP, + .val = AC_PINCAP_OUT, + },{ + .id = AC_PAR_CONNLIST_LEN, + .val = 1, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* common: pin widget (line-in) */ +static const desc_param glue(common_params_audio_linein_, PARAM)[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PIN_CAP, + .val = AC_PINCAP_IN, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* output: root node */ +static const desc_param glue(output_params_root_, PARAM)[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* output: audio function */ +static const desc_param glue(output_params_audio_func_, PARAM)[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020002, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* output: nodes */ +static const desc_node glue(output_nodes_, PARAM)[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = glue(output_params_root_, PARAM), + .nparams = ARRAY_SIZE(glue(output_params_root_, PARAM)), + },{ + .nid = 1, + .name = "func", + .params = glue(output_params_audio_func_, PARAM), + .nparams = ARRAY_SIZE(glue(output_params_audio_func_, PARAM)), + },{ + .nid = 2, + .name = "dac", + .params = glue(common_params_audio_dac_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = glue(common_params_audio_lineout_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + } +}; + +/* output: codec */ +static const desc_codec glue(output_, PARAM) = { + .name = "output", + .iid = QEMU_HDA_ID_OUTPUT, + .nodes = glue(output_nodes_, PARAM), + .nnodes = ARRAY_SIZE(glue(output_nodes_, PARAM)), +}; + +/* duplex: root node */ +static const desc_param glue(duplex_params_root_, PARAM)[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* duplex: audio function */ +static const desc_param glue(duplex_params_audio_func_, PARAM)[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020004, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* duplex: nodes */ +static const desc_node glue(duplex_nodes_, PARAM)[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = glue(duplex_params_root_, PARAM), + .nparams = ARRAY_SIZE(glue(duplex_params_root_, PARAM)), + },{ + .nid = 1, + .name = "func", + .params = glue(duplex_params_audio_func_, PARAM), + .nparams = ARRAY_SIZE(glue(duplex_params_audio_func_, PARAM)), + },{ + .nid = 2, + .name = "dac", + .params = glue(common_params_audio_dac_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = glue(common_params_audio_lineout_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + },{ + .nid = 4, + .name = "adc", + .params = glue(common_params_audio_adc_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_adc_, PARAM)), + .stindex = 1, + .conn = (uint32_t[]) { 5 }, + },{ + .nid = 5, + .name = "in", + .params = glue(common_params_audio_linein_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_linein_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | + 0x20), + .pinctl = AC_PINCTL_IN_EN, + } +}; + +/* duplex: codec */ +static const desc_codec glue(duplex_, PARAM) = { + .name = "duplex", + .iid = QEMU_HDA_ID_DUPLEX, + .nodes = glue(duplex_nodes_, PARAM), + .nnodes = ARRAY_SIZE(glue(duplex_nodes_, PARAM)), +}; + +/* micro: root node */ +static const desc_param glue(micro_params_root_, PARAM)[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_MICRO, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_MICRO, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* micro: audio function */ +static const desc_param glue(micro_params_audio_func_, PARAM)[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_MICRO, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020004, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* micro: nodes */ +static const desc_node glue(micro_nodes_, PARAM)[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = glue(micro_params_root_, PARAM), + .nparams = ARRAY_SIZE(glue(micro_params_root_, PARAM)), + },{ + .nid = 1, + .name = "func", + .params = glue(micro_params_audio_func_, PARAM), + .nparams = ARRAY_SIZE(glue(micro_params_audio_func_, PARAM)), + },{ + .nid = 2, + .name = "dac", + .params = glue(common_params_audio_dac_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = glue(common_params_audio_lineout_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_SPEAKER << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + },{ + .nid = 4, + .name = "adc", + .params = glue(common_params_audio_adc_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_adc_, PARAM)), + .stindex = 1, + .conn = (uint32_t[]) { 5 }, + },{ + .nid = 5, + .name = "in", + .params = glue(common_params_audio_linein_, PARAM), + .nparams = ARRAY_SIZE(glue(common_params_audio_linein_, PARAM)), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | + 0x20), + .pinctl = AC_PINCTL_IN_EN, + } +}; + +/* micro: codec */ +static const desc_codec glue(micro_, PARAM) = { + .name = "micro", + .iid = QEMU_HDA_ID_MICRO, + .nodes = glue(micro_nodes_, PARAM), + .nnodes = ARRAY_SIZE(glue(micro_nodes_, PARAM)), +}; + +#undef PARAM +#undef HDA_MIXER +#undef QEMU_HDA_ID_OUTPUT +#undef QEMU_HDA_ID_DUPLEX +#undef QEMU_HDA_ID_MICRO +#undef QEMU_HDA_AMP_CAPS diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 9550c97e65..07a43bfe89 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -118,428 +118,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) #define QEMU_HDA_AMP_NONE (0) #define QEMU_HDA_AMP_STEPS 0x4a -#ifdef CONFIG_MIXEMU -# define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x12) -# define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x22) -# define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x32) -# define QEMU_HDA_AMP_CAPS \ - (AC_AMPCAP_MUTE | \ - (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ - (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ - (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) -#else -# define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x11) -# define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x21) -# define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x31) -# define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE -#endif - -/* common: audio output widget */ -static const desc_param common_params_audio_dac[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_FORMAT_OVRD | - AC_WCAP_AMP_OVRD | - AC_WCAP_OUT_AMP | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_CAPS, - }, -}; - -/* common: audio input widget */ -static const desc_param common_params_audio_adc[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_CONN_LIST | - AC_WCAP_FORMAT_OVRD | - AC_WCAP_AMP_OVRD | - AC_WCAP_IN_AMP | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_CONNLIST_LEN, - .val = 1, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_CAPS, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* common: pin widget (line-out) */ -static const desc_param common_params_audio_lineout[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_CONN_LIST | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PIN_CAP, - .val = AC_PINCAP_OUT, - },{ - .id = AC_PAR_CONNLIST_LEN, - .val = 1, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* common: pin widget (line-in) */ -static const desc_param common_params_audio_linein[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PIN_CAP, - .val = AC_PINCAP_IN, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* output: root node */ -static const desc_param output_params_root[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; +#define PARAM mixemu +#define HDA_MIXER +#include "hda-codec-common.h" -/* output: audio function */ -static const desc_param output_params_audio_func[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020002, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* output: nodes */ -static const desc_node output_nodes[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = output_params_root, - .nparams = ARRAY_SIZE(output_params_root), - },{ - .nid = 1, - .name = "func", - .params = output_params_audio_func, - .nparams = ARRAY_SIZE(output_params_audio_func), - },{ - .nid = 2, - .name = "dac", - .params = common_params_audio_dac, - .nparams = ARRAY_SIZE(common_params_audio_dac), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = common_params_audio_lineout, - .nparams = ARRAY_SIZE(common_params_audio_lineout), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - } -}; - -/* output: codec */ -static const desc_codec output = { - .name = "output", - .iid = QEMU_HDA_ID_OUTPUT, - .nodes = output_nodes, - .nnodes = ARRAY_SIZE(output_nodes), -}; - -/* duplex: root node */ -static const desc_param duplex_params_root[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* duplex: audio function */ -static const desc_param duplex_params_audio_func[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020004, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* duplex: nodes */ -static const desc_node duplex_nodes[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = duplex_params_root, - .nparams = ARRAY_SIZE(duplex_params_root), - },{ - .nid = 1, - .name = "func", - .params = duplex_params_audio_func, - .nparams = ARRAY_SIZE(duplex_params_audio_func), - },{ - .nid = 2, - .name = "dac", - .params = common_params_audio_dac, - .nparams = ARRAY_SIZE(common_params_audio_dac), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = common_params_audio_lineout, - .nparams = ARRAY_SIZE(common_params_audio_lineout), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - },{ - .nid = 4, - .name = "adc", - .params = common_params_audio_adc, - .nparams = ARRAY_SIZE(common_params_audio_adc), - .stindex = 1, - .conn = (uint32_t[]) { 5 }, - },{ - .nid = 5, - .name = "in", - .params = common_params_audio_linein, - .nparams = ARRAY_SIZE(common_params_audio_linein), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | - 0x20), - .pinctl = AC_PINCTL_IN_EN, - } -}; - -/* duplex: codec */ -static const desc_codec duplex = { - .name = "duplex", - .iid = QEMU_HDA_ID_DUPLEX, - .nodes = duplex_nodes, - .nnodes = ARRAY_SIZE(duplex_nodes), -}; - -/* micro: root node */ -static const desc_param micro_params_root[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* micro: audio function */ -static const desc_param micro_params_audio_func[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020004, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* micro: nodes */ -static const desc_node micro_nodes[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = micro_params_root, - .nparams = ARRAY_SIZE(micro_params_root), - },{ - .nid = 1, - .name = "func", - .params = micro_params_audio_func, - .nparams = ARRAY_SIZE(micro_params_audio_func), - },{ - .nid = 2, - .name = "dac", - .params = common_params_audio_dac, - .nparams = ARRAY_SIZE(common_params_audio_dac), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = common_params_audio_lineout, - .nparams = ARRAY_SIZE(common_params_audio_lineout), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_SPEAKER << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - },{ - .nid = 4, - .name = "adc", - .params = common_params_audio_adc, - .nparams = ARRAY_SIZE(common_params_audio_adc), - .stindex = 1, - .conn = (uint32_t[]) { 5 }, - },{ - .nid = 5, - .name = "in", - .params = common_params_audio_linein, - .nparams = ARRAY_SIZE(common_params_audio_linein), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | - 0x20), - .pinctl = AC_PINCTL_IN_EN, - } -}; - -/* micro: codec */ -static const desc_codec micro = { - .name = "micro", - .iid = QEMU_HDA_ID_MICRO, - .nodes = micro_nodes, - .nnodes = ARRAY_SIZE(micro_nodes), -}; +#define PARAM nomixemu +#include "hda-codec-common.h" /* -------------------------------------------------------------------------- */ @@ -585,6 +169,7 @@ struct HDAAudioState { /* properties */ uint32_t debug; + bool mixer; }; static void hda_audio_input_cb(void *opaque, int avail) @@ -1006,23 +591,42 @@ static const VMStateDescription vmstate_hda_audio = { }; static Property hda_audio_properties[] = { - DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), + DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), + DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), DEFINE_PROP_END_OF_LIST(), }; static int hda_audio_init_output(HDACodecDevice *hda) { - return hda_audio_init(hda, &output); + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + + if (!a->mixer) { + return hda_audio_init(hda, &output_nomixemu); + } else { + return hda_audio_init(hda, &output_mixemu); + } } static int hda_audio_init_duplex(HDACodecDevice *hda) { - return hda_audio_init(hda, &duplex); + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + + if (!a->mixer) { + return hda_audio_init(hda, &duplex_nomixemu); + } else { + return hda_audio_init(hda, &duplex_mixemu); + } } static int hda_audio_init_micro(HDACodecDevice *hda) { - return hda_audio_init(hda, µ); + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + + if (!a->mixer) { + return hda_audio_init(hda, µ_nomixemu); + } else { + return hda_audio_init(hda, µ_mixemu); + } } static void hda_audio_output_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index f8f3dbca3e..cbd6a006f4 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -22,6 +22,6 @@ common-obj-$(CONFIG_IMX) += imx_serial.o common-obj-$(CONFIG_LM32) += lm32_juart.o common-obj-$(CONFIG_LM32) += lm32_uart.o common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o -common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o +common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c new file mode 100644 index 0000000000..93390675d6 --- /dev/null +++ b/hw/char/sclpconsole-lm.c @@ -0,0 +1,398 @@ +/* + * SCLP event types + * Operations Command - Line Mode input + * Message - Line Mode output + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Heinz Graalfs <graalfs@linux.vnet.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 "hw/qdev.h" +#include "qemu/thread.h" +#include "qemu/error-report.h" +#include "sysemu/char.h" + +#include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" +#include "hw/s390x/ebcdic.h" + +#define SIZE_BUFFER 4096 +#define NEWLINE "\n" + +typedef struct OprtnsCommand { + EventBufferHeader header; + MDMSU message_unit; + char data[0]; +} QEMU_PACKED OprtnsCommand; + +/* max size for line-mode data in 4K SCCB page */ +#define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand)) + +typedef struct SCLPConsoleLM { + SCLPEvent event; + CharDriverState *chr; + bool echo; /* immediate echo of input if true */ + uint32_t write_errors; /* errors writing to char layer */ + uint32_t length; /* length of byte stream in buffer */ + uint8_t buf[SIZE_CONSOLE_BUFFER]; + qemu_irq irq_console_read; +} SCLPConsoleLM; + +/* +* Character layer call-back functions + * + * Allow 1 character at a time + * + * Accumulate bytes from character layer in console buffer, + * event_pending is set when a newline character is encountered + * + * The maximum command line length is limited by the maximum + * space available in an SCCB + */ + +static int chr_can_read(void *opaque) +{ + SCLPConsoleLM *scon = opaque; + + if (scon->event.event_pending) { + return 0; + } else if (SIZE_CONSOLE_BUFFER - scon->length) { + return 1; + } + return 0; +} + +static void receive_from_chr_layer(SCLPConsoleLM *scon, const uint8_t *buf, + int size) +{ + assert(size == 1); + + if (*buf == '\r' || *buf == '\n') { + scon->event.event_pending = true; + return; + } + scon->buf[scon->length] = *buf; + scon->length += 1; + if (scon->echo) { + qemu_chr_fe_write(scon->chr, buf, size); + } +} + +/* + * Send data from a char device over to the guest + */ +static void chr_read(void *opaque, const uint8_t *buf, int size) +{ + SCLPConsoleLM *scon = opaque; + + receive_from_chr_layer(scon, buf, size); + if (scon->event.event_pending) { + /* trigger SCLP read operation */ + qemu_irq_raise(scon->irq_console_read); + } +} + +/* functions to be called by event facility */ + +static bool can_handle_event(uint8_t type) +{ + return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; +} + +static unsigned int send_mask(void) +{ + return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; +} + +static unsigned int receive_mask(void) +{ + return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; +} + +/* + * Triggered by SCLP's read_event_data + * - convert ASCII byte stream to EBCDIC and + * - copy converted data into provided (SCLP) buffer + */ +static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, + int avail) +{ + int len; + + SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); + + len = cons->length; + /* data need to fit into provided SCLP buffer */ + if (len > avail) { + return 1; + } + + ebcdic_put(buf, (char *)&cons->buf, len); + *size = len; + cons->length = 0; + /* data provided and no more data pending */ + event->event_pending = false; + return 0; +} + +static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, + int *slen) +{ + int avail, rc; + size_t src_len; + uint8_t *to; + OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; + + if (!event->event_pending) { + /* no data pending */ + return 0; + } + + to = (uint8_t *)&oc->data; + avail = *slen - sizeof(OprtnsCommand); + rc = get_console_data(event, to, &src_len, avail); + if (rc) { + /* data didn't fit, try next SCCB */ + return 1; + } + + oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; + oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); + + oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; + oc->message_unit.cpmsu.length = + cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); + + oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; + oc->message_unit.text_command.length = + cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); + + oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; + oc->message_unit.self_def_text_message.length = + cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); + + oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; + oc->message_unit.text_message.length = + cpu_to_be16(sizeof(GdsSubvector) + src_len); + + oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); + oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; + *slen = avail - src_len; + + return 1; +} + +/* + * Triggered by SCLP's write_event_data + * - write console data to character layer + * returns < 0 if an error occurred + */ +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); + + if (!scon->chr) { + /* If there's no backend, we can just say we consumed all data. */ + return len; + } + + buf_offset = buf; + while (len > 0) { + ret = qemu_chr_fe_write(scon->chr, buf, len); + if (ret == 0) { + /* a pty doesn't seem to be connected - no error */ + len = 0; + } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { + len -= ret; + buf_offset += ret; + } else { + len = 0; + } + } + + return ret; +} + +static int process_mdb(SCLPEvent *event, MDBO *mdbo) +{ + int rc; + int len; + uint8_t buffer[SIZE_BUFFER]; + + len = be16_to_cpu(mdbo->length); + len -= sizeof(mdbo->length) + sizeof(mdbo->type) + + sizeof(mdbo->mto.line_type_flags) + + sizeof(mdbo->mto.alarm_control) + + sizeof(mdbo->mto._reserved); + + assert(len <= SIZE_BUFFER); + + /* convert EBCDIC SCLP contents to ASCII console message */ + ascii_put(buffer, mdbo->mto.message, len); + rc = write_console_data(event, (uint8_t *)NEWLINE, 1); + if (rc < 0) { + return rc; + } + return write_console_data(event, buffer, len); +} + +static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) +{ + int len; + int written; + int errors = 0; + MDBO *mdbo; + SclpMsg *data = (SclpMsg *) ebh; + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + len = be16_to_cpu(data->mdb.header.length); + if (len < sizeof(data->mdb.header)) { + return SCLP_RC_INCONSISTENT_LENGTHS; + } + len -= sizeof(data->mdb.header); + + /* first check message buffers */ + mdbo = data->mdb.mdbo; + while (len > 0) { + if (be16_to_cpu(mdbo->length) > len + || be16_to_cpu(mdbo->length) == 0) { + return SCLP_RC_INCONSISTENT_LENGTHS; + } + len -= be16_to_cpu(mdbo->length); + mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); + } + + /* then execute */ + len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); + mdbo = data->mdb.mdbo; + while (len > 0) { + switch (be16_to_cpu(mdbo->type)) { + case MESSAGE_TEXT: + /* message text object */ + written = process_mdb(event, mdbo); + if (written < 0) { + /* character layer error */ + errors++; + } + break; + default: /* ignore */ + break; + } + len -= be16_to_cpu(mdbo->length); + mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); + } + if (errors) { + scon->write_errors += errors; + } + data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; + + return SCLP_RC_NORMAL_COMPLETION; +} + +static void trigger_console_data(void *opaque, int n, int level) +{ + sclp_service_interrupt(0); +} + +/* functions for live migration */ + +static const VMStateDescription vmstate_sclplmconsole = { + .name = "sclplmconsole", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), + VMSTATE_UINT32(write_errors, SCLPConsoleLM), + VMSTATE_UINT32(length, SCLPConsoleLM), + VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), + VMSTATE_END_OF_LIST() + } +}; + +/* qemu object creation and initialization functions */ + +/* tell character layer our call-back functions */ + +static int console_init(SCLPEvent *event) +{ + static bool console_available; + + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + if (console_available) { + error_report("Multiple line-mode operator consoles are not supported"); + return -1; + } + console_available = true; + + if (scon->chr) { + qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon); + } + scon->irq_console_read = *qemu_allocate_irqs(trigger_console_data, NULL, 1); + + return 0; +} + +static int console_exit(SCLPEvent *event) +{ + return 0; +} + +static void console_reset(DeviceState *dev) +{ + SCLPEvent *event = SCLP_EVENT(dev); + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + + event->event_pending = false; + scon->length = 0; + scon->write_errors = 0; +} + +static Property console_properties[] = { + DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), + DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), + DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void console_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); + + dc->props = console_properties; + dc->reset = console_reset; + dc->vmsd = &vmstate_sclplmconsole; + ec->init = console_init; + ec->exit = console_exit; + ec->get_send_mask = send_mask; + ec->get_receive_mask = receive_mask; + ec->can_handle_event = can_handle_event; + ec->read_event_data = read_event_data; + ec->write_event_data = write_event_data; +} + +static const TypeInfo sclp_console_info = { + .name = "sclplmconsole", + .parent = TYPE_SCLP_EVENT, + .instance_size = sizeof(SCLPConsoleLM), + .class_init = console_class_init, + .class_size = sizeof(SCLPEventClass), +}; + +static void register_types(void) +{ + type_register_static(&sclp_console_info); +} + +type_init(register_types) diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index eb3988c2e4..16d77c5e27 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -31,12 +31,11 @@ typedef struct ASCIIConsoleData { typedef struct SCLPConsole { SCLPEvent event; CharDriverState *chr; - /* io vector */ - uint8_t *iov; /* iov buffer pointer */ - uint8_t *iov_sclp; /* pointer to SCLP read offset */ - uint8_t *iov_bs; /* pointer byte stream read offset */ - uint32_t iov_data_len; /* length of byte stream in buffer */ - uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ + uint8_t iov[SIZE_BUFFER_VT220]; + uint32_t iov_sclp; /* offset in buf for SCLP read operation */ + uint32_t iov_bs; /* offset in buf for char layer read operation */ + uint32_t iov_data_len; /* length of byte stream in buffer */ + uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ qemu_irq irq_read_vt220; } SCLPConsole; @@ -47,7 +46,7 @@ static int chr_can_read(void *opaque) { SCLPConsole *scon = opaque; - return scon->iov ? SIZE_BUFFER_VT220 - scon->iov_data_len : 0; + return SIZE_BUFFER_VT220 - scon->iov_data_len; } /* Receive n bytes from character layer, save in iov buffer, @@ -55,13 +54,11 @@ static int chr_can_read(void *opaque) static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf, int size) { - assert(scon->iov); - /* read data must fit into current buffer */ assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len); /* put byte-stream from character layer into buffer */ - memcpy(scon->iov_bs, buf, size); + memcpy(&scon->iov[scon->iov_bs], buf, size); scon->iov_data_len += size; scon->iov_sclp_rest += size; scon->iov_bs += size; @@ -80,34 +77,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) qemu_irq_raise(scon->irq_read_vt220); } -static void chr_event(void *opaque, int event) -{ - SCLPConsole *scon = opaque; - - switch (event) { - case CHR_EVENT_OPENED: - if (!scon->iov) { - scon->iov = g_malloc0(SIZE_BUFFER_VT220); - scon->iov_sclp = scon->iov; - scon->iov_bs = scon->iov; - scon->iov_data_len = 0; - scon->iov_sclp_rest = 0; - } - break; - case CHR_EVENT_CLOSED: - if (scon->iov) { - g_free(scon->iov); - scon->iov = NULL; - } - break; - } -} - /* functions to be called by event facility */ -static int event_type(void) +static bool can_handle_event(uint8_t type) { - return SCLP_EVENT_ASCII_CONSOLE_DATA; + return type == SCLP_EVENT_ASCII_CONSOLE_DATA; } static unsigned int send_mask(void) @@ -134,17 +108,17 @@ static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, /* if all data fit into provided SCLP buffer */ if (avail >= cons->iov_sclp_rest) { /* copy character byte-stream to SCLP buffer */ - memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest); + memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest); *size = cons->iov_sclp_rest + 1; - cons->iov_sclp = cons->iov; - cons->iov_bs = cons->iov; + cons->iov_sclp = 0; + cons->iov_bs = 0; cons->iov_data_len = 0; cons->iov_sclp_rest = 0; event->event_pending = false; /* data provided and no more data pending */ } else { /* if provided buffer is too small, just copy part */ - memcpy(buf, cons->iov_sclp, avail); + memcpy(buf, &cons->iov[cons->iov_sclp], avail); *size = avail + 1; cons->iov_sclp_rest -= avail; cons->iov_sclp += avail; @@ -223,9 +197,26 @@ static void trigger_ascii_console_data(void *opaque, int n, int level) sclp_service_interrupt(0); } +static const VMStateDescription vmstate_sclpconsole = { + .name = "sclpconsole", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(event.event_pending, SCLPConsole), + VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220), + VMSTATE_UINT32(iov_sclp, SCLPConsole), + VMSTATE_UINT32(iov_bs, SCLPConsole), + VMSTATE_UINT32(iov_data_len, SCLPConsole), + VMSTATE_UINT32(iov_sclp_rest, SCLPConsole), + VMSTATE_END_OF_LIST() + } +}; + /* qemu object creation and initialization functions */ /* tell character layer our call-back functions */ + static int console_init(SCLPEvent *event) { static bool console_available; @@ -237,10 +228,9 @@ static int console_init(SCLPEvent *event) return -1; } console_available = true; - event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA; if (scon->chr) { qemu_chr_add_handlers(scon->chr, chr_can_read, - chr_read, chr_event, scon); + chr_read, NULL, scon); } scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data, NULL, 1); @@ -248,6 +238,18 @@ static int console_init(SCLPEvent *event) return 0; } +static void console_reset(DeviceState *dev) +{ + SCLPEvent *event = SCLP_EVENT(dev); + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + + event->event_pending = false; + scon->iov_sclp = 0; + scon->iov_bs = 0; + scon->iov_data_len = 0; + scon->iov_sclp_rest = 0; +} + static int console_exit(SCLPEvent *event) { return 0; @@ -264,11 +266,13 @@ static void console_class_init(ObjectClass *klass, void *data) SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); dc->props = console_properties; + dc->reset = console_reset; + dc->vmsd = &vmstate_sclpconsole; ec->init = console_init; ec->exit = console_exit; ec->get_send_mask = send_mask; ec->get_receive_mask = receive_mask; - ec->event_type = event_type; + ec->can_handle_event = can_handle_event; ec->read_event_data = read_event_data; ec->write_event_data = write_event_data; } diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index a3aceef8f5..25951a020a 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -120,7 +120,7 @@ static uint16_t handle_write_event_buf(SCLPEventFacility *ef, ec = SCLP_EVENT_GET_CLASS(event); if (ec->write_event_data && - ec->event_type() == event_buf->type) { + ec->can_handle_event(event_buf->type)) { rc = ec->write_event_data(event, event_buf); break; } @@ -183,7 +183,7 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, { uint16_t rc; int slen; - unsigned elen = 0; + unsigned elen; BusChild *kid; SCLPEvent *event; SCLPEventClass *ec; @@ -203,11 +203,11 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, if (mask & ec->get_send_mask()) { if (ec->read_event_data(event, event_buf, &slen)) { + elen = be16_to_cpu(event_buf->length); + event_buf = (EventBufferHeader *) ((char *)event_buf + elen); rc = SCLP_RC_NORMAL_COMPLETION; } } - elen = be16_to_cpu(event_buf->length); - event_buf = (void *) event_buf + elen; } if (sccb->h.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) { @@ -338,10 +338,19 @@ static int init_event_facility(S390SCLPDevice *sdev) return 0; } +static void reset_event_facility(DeviceState *dev) +{ + S390SCLPDevice *sdev = SCLP_S390_DEVICE(dev); + + sdev->ef->receive_mask = 0; +} + static void init_event_facility_class(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass); + dc->reset = reset_event_facility; k->init = init_event_facility; } diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index 5fadc86d42..a3c4bd6272 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -22,9 +22,9 @@ typedef struct SignalQuiesce { uint8_t unit; } QEMU_PACKED SignalQuiesce; -static int event_type(void) +static bool can_handle_event(uint8_t type) { - return SCLP_EVENT_SIGNAL_QUIESCE; + return type == SCLP_EVENT_SIGNAL_QUIESCE; } static unsigned int send_mask(void) @@ -65,6 +65,17 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, return 1; } +static const VMStateDescription vmstate_sclpquiesce = { + .name = "sclpquiesce", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(event_pending, SCLPEvent), + VMSTATE_END_OF_LIST() + } +}; + typedef struct QuiesceNotifier QuiesceNotifier; static struct QuiesceNotifier { @@ -84,8 +95,6 @@ static void quiesce_powerdown_req(Notifier *n, void *opaque) static int quiesce_init(SCLPEvent *event) { - event->event_type = SCLP_EVENT_SIGNAL_QUIESCE; - qn.notifier.notify = quiesce_powerdown_req; qn.event = event; @@ -94,15 +103,25 @@ static int quiesce_init(SCLPEvent *event) return 0; } +static void quiesce_reset(DeviceState *dev) +{ + SCLPEvent *event = SCLP_EVENT(dev); + + event->event_pending = false; +} + static void quiesce_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *k = SCLP_EVENT_CLASS(klass); + dc->reset = quiesce_reset; + dc->vmsd = &vmstate_sclpquiesce; k->init = quiesce_init; k->get_send_mask = send_mask; k->get_receive_mask = receive_mask; - k->event_type = event_type; + k->can_handle_event = can_handle_event; k->read_event_data = read_event_data; k->write_event_data = NULL; } diff --git a/include/hw/s390x/ebcdic.h b/include/hw/s390x/ebcdic.h new file mode 100644 index 0000000000..1d6fde9c12 --- /dev/null +++ b/include/hw/s390x/ebcdic.h @@ -0,0 +1,104 @@ +/* + * EBCDIC/ASCII conversion Support + * + * Copyright (c) 2011 Alexander Graf + * Copyright IBM, Corp. 2013 + * + * 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 EBCDIC_H_ +#define EBCDIC_H_ + +/* EBCDIC handling */ +static const uint8_t ebcdic2ascii[] = { + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, +}; + +static const uint8_t ascii2ebcdic[] = { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) +{ + int i; + + for (i = 0; i < len; i++) { + p[i] = ascii2ebcdic[(uint8_t)ascii[i]]; + } +} + +static inline void ascii_put(uint8_t *p, const char *ebcdic, int len) +{ + int i; + + for (i = 0; i < len; i++) { + p[i] = ebcdic2ascii[(uint8_t)ebcdic[i]]; + } +} + +#endif /* EBCDIC_H_ */ diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 791ab2a6de..7ce7079f9f 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -19,12 +19,18 @@ #include "qemu/thread.h" /* SCLP event types */ +#define SCLP_EVENT_OPRTNS_COMMAND 0x01 +#define SCLP_EVENT_MESSAGE 0x02 +#define SCLP_EVENT_PMSGCMD 0x09 #define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a #define SCLP_EVENT_SIGNAL_QUIESCE 0x1d /* SCLP event masks */ #define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008 #define SCLP_EVENT_MASK_MSG_ASCII 0x00000040 +#define SCLP_EVENT_MASK_OP_CMD 0x80000000 +#define SCLP_EVENT_MASK_MSG 0x40000000 +#define SCLP_EVENT_MASK_PMSGCMD 0x00800000 #define SCLP_UNCONDITIONAL_READ 0x00 #define SCLP_SELECTIVE_READ 0x01 @@ -43,8 +49,8 @@ typedef struct WriteEventMask { uint16_t mask_length; uint32_t cp_receive_mask; uint32_t cp_send_mask; - uint32_t send_mask; uint32_t receive_mask; + uint32_t send_mask; } QEMU_PACKED WriteEventMask; typedef struct EventBufferHeader { @@ -54,6 +60,80 @@ typedef struct EventBufferHeader { uint16_t _reserved; } QEMU_PACKED EventBufferHeader; +typedef struct MdbHeader { + uint16_t length; + uint16_t type; + uint32_t tag; + uint32_t revision_code; +} QEMU_PACKED MdbHeader; + +typedef struct MTO { + uint16_t line_type_flags; + uint8_t alarm_control; + uint8_t _reserved[3]; + char message[]; +} QEMU_PACKED MTO; + +typedef struct GO { + uint32_t domid; + uint8_t hhmmss_time[8]; + uint8_t th_time[3]; + uint8_t _reserved_0; + uint8_t dddyyyy_date[7]; + uint8_t _reserved_1; + uint16_t general_msg_flags; + uint8_t _reserved_2[10]; + uint8_t originating_system_name[8]; + uint8_t job_guest_name[8]; +} QEMU_PACKED GO; + +#define MESSAGE_TEXT 0x0004 + +typedef struct MDBO { + uint16_t length; + uint16_t type; + union { + GO go; + MTO mto; + }; +} QEMU_PACKED MDBO; + +typedef struct MDB { + MdbHeader header; + MDBO mdbo[0]; +} QEMU_PACKED MDB; + +typedef struct SclpMsg { + EventBufferHeader header; + MDB mdb; +} QEMU_PACKED SclpMsg; + +#define GDS_ID_MDSMU 0x1310 +#define GDS_ID_CPMSU 0x1212 +#define GDS_ID_TEXTCMD 0x1320 + +typedef struct GdsVector { + uint16_t length; + uint16_t gds_id; +} QEMU_PACKED GdsVector; + +#define GDS_KEY_SELFDEFTEXTMSG 0x31 +#define GDS_KEY_TEXTMSG 0x30 + +typedef struct GdsSubvector { + uint8_t length; + uint8_t key; +} QEMU_PACKED GdsSubvector; + +/* MDS Message Unit */ +typedef struct MDMSU { + GdsVector mdmsu; + GdsVector cpmsu; + GdsVector text_command; + GdsSubvector self_def_text_message; + GdsSubvector text_message; +} QEMU_PACKED MDMSU; + typedef struct WriteEventData { SCCBHeader h; EventBufferHeader ebh; @@ -68,7 +148,6 @@ typedef struct ReadEventData { typedef struct SCLPEvent { DeviceState qdev; bool event_pending; - uint32_t event_type; char *name; } SCLPEvent; @@ -88,9 +167,8 @@ typedef struct SCLPEventClass { int (*write_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr); - /* returns the supported event type */ - int (*event_type)(void); - + /* can we handle this event type? */ + bool (*can_handle_event)(uint8_t type); } SCLPEventClass; #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1c31b5d6fb..9d09e60419 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -310,8 +310,18 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = vmstate_offset_value(_state, _field, _type), \ } -#define VMSTATE_STRUCT_POINTER_TEST(_field, _state, _test, _vmsd, _type) { \ +#define VMSTATE_STRUCT_POINTER_V(_field, _state, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + +#define VMSTATE_STRUCT_POINTER_TEST_V(_field, _state, _test, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ .field_exists = (_test), \ .vmsd = &(_vmsd), \ .size = sizeof(_type), \ @@ -497,7 +507,10 @@ extern const VMStateInfo vmstate_info_bitmap; VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) #define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ - VMSTATE_STRUCT_POINTER_TEST(_field, _state, NULL, _vmsd, _type) + VMSTATE_STRUCT_POINTER_V(_field, _state, 0, _vmsd, _type) + +#define VMSTATE_STRUCT_POINTER_TEST(_field, _state, _test, _vmsd, _type) \ + VMSTATE_STRUCT_POINTER_TEST_V(_field, _state, _test, 0, _vmsd, _type) #define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \ VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, \ diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h index ac2b6e2c65..d52d76e08e 100644 --- a/linux-user/alpha/syscall_nr.h +++ b/linux-user/alpha/syscall_nr.h @@ -20,7 +20,7 @@ #define TARGET_NR_lseek 19 #define TARGET_NR_getxpid 20 #define TARGET_NR_osf_mount 21 -#define TARGET_NR_umount 22 +#define TARGET_NR_umount2 22 #define TARGET_NR_setuid 23 #define TARGET_NR_getxuid 24 #define TARGET_NR_exec_with_loader 25 /* not implemented */ @@ -255,7 +255,7 @@ #define TARGET_NR_sysinfo 318 #define TARGET_NR__sysctl 319 /* 320 was sys_idle. */ -#define TARGET_NR_oldumount 321 +#define TARGET_NR_umount 321 #define TARGET_NR_swapon 322 #define TARGET_NR_times 323 #define TARGET_NR_personality 324 diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 439c2a9e32..7381012d3d 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -20,6 +20,7 @@ IOCTL(TIOCSCTTY, 0, TYPE_INT) IOCTL(TIOCGPGRP, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCSPGRP, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(TIOCGSID, IOC_W, MK_PTR(TYPE_INT)) IOCTL(TIOCOUTQ, IOC_R, MK_PTR(TYPE_INT)) IOCTL(TIOCSTI, IOC_W, MK_PTR(TYPE_INT)) IOCTL(TIOCMGET, IOC_R, MK_PTR(TYPE_INT)) diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 5cd6d91554..a1fe5ed9ae 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -131,7 +131,7 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, return sp; } -int loader_exec(const char * filename, char ** argv, char ** envp, +int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *bprm) { @@ -140,11 +140,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); memset(bprm->page, 0, sizeof(bprm->page)); - retval = open(filename, O_RDONLY); - if (retval < 0) { - return -errno; - } - bprm->fd = retval; + bprm->fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); bprm->argv = argv; diff --git a/linux-user/main.c b/linux-user/main.c index 01e3cd4cc1..1561950bf5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1861,7 +1861,7 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_lseek , 3) MIPS_SYS(sys_getpid , 0) /* 4020 */ MIPS_SYS(sys_mount , 5) - MIPS_SYS(sys_oldumount , 1) + MIPS_SYS(sys_umount , 1) MIPS_SYS(sys_setuid , 1) MIPS_SYS(sys_getuid , 0) MIPS_SYS(sys_stime , 1) /* 4025 */ @@ -1891,7 +1891,7 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_geteuid , 0) MIPS_SYS(sys_getegid , 0) /* 4050 */ MIPS_SYS(sys_acct , 0) - MIPS_SYS(sys_umount , 2) + MIPS_SYS(sys_umount2 , 2) MIPS_SYS(sys_ni_syscall , 0) MIPS_SYS(sys_ioctl , 3) MIPS_SYS(sys_fcntl , 3) /* 4055 */ @@ -2400,12 +2400,31 @@ done_syscall: if (env->hflags & MIPS_HFLAG_M16) { if (env->insn_flags & ASE_MICROMIPS) { /* microMIPS mode */ - abi_ulong instr[2]; - - ret = get_user_u16(instr[0], env->active_tc.PC) || - get_user_u16(instr[1], env->active_tc.PC + 2); + ret = get_user_u16(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } - trap_instr = (instr[0] << 16) | instr[1]; + if ((trap_instr >> 10) == 0x11) { + /* 16-bit instruction */ + code = trap_instr & 0xf; + } else { + /* 32-bit instruction */ + abi_ulong instr_lo; + + ret = get_user_u16(instr_lo, + env->active_tc.PC + 2); + if (ret != 0) { + goto error; + } + trap_instr = (trap_instr << 16) | instr_lo; + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + /* Unfortunately, microMIPS also suffers from + the old assembler bug... */ + if (code >= (1 << 10)) { + code >>= 10; + } + } } else { /* MIPS16e mode */ ret = get_user_u16(trap_instr, env->active_tc.PC); @@ -2413,26 +2432,21 @@ done_syscall: goto error; } code = (trap_instr >> 6) & 0x3f; - if (do_break(env, &info, code) != 0) { - goto error; - } - break; } } else { ret = get_user_ual(trap_instr, env->active_tc.PC); - } - - if (ret != 0) { - goto error; - } + if (ret != 0) { + goto error; + } - /* As described in the original Linux kernel code, the - * below checks on 'code' are to work around an old - * assembly bug. - */ - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - if (code >= (1 << 10)) { - code >>= 10; + /* As described in the original Linux kernel code, the + * below checks on 'code' are to work around an old + * assembly bug. + */ + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + if (code >= (1 << 10)) { + code >>= 10; + } } if (do_break(env, &info, code) != 0) { @@ -3618,6 +3632,26 @@ static int parse_args(int argc, char **argv) return optind; } +static int get_execfd(char **envp) +{ + typedef struct { + long a_type; + long a_val; + } auxv_t; + auxv_t *auxv; + + while (*envp++ != NULL) { + ; + } + + for (auxv = (auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) { + if (auxv->a_type == AT_EXECFD) { + return auxv->a_val; + } + } + return -1; +} + int main(int argc, char **argv, char **envp) { struct target_pt_regs regs1, *regs = ®s1; @@ -3632,6 +3666,7 @@ int main(int argc, char **argv, char **envp) int target_argc; int i; int ret; + int execfd; module_call_init(MODULE_INIT_QOM); @@ -3809,7 +3844,16 @@ int main(int argc, char **argv, char **envp) env->opaque = ts; task_settid(ts); - ret = loader_exec(filename, target_argv, target_environ, regs, + execfd = get_execfd(envp); + if (execfd < 0) { + execfd = open(filename, O_RDONLY); + } + if (execfd < 0) { + printf("Error while loading %s: %s\n", filename, strerror(-execfd)); + _exit(1); + } + + ret = loader_exec(execfd, filename, target_argv, target_environ, regs, info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", filename, strerror(-ret)); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 617cac1775..da64e877c7 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -174,7 +174,7 @@ struct linux_binprm { void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); -int loader_exec(const char * filename, char ** argv, char ** envp, +int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *); diff --git a/linux-user/strace.list b/linux-user/strace.list index 08f115d843..cf5841af60 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -612,9 +612,6 @@ #ifdef TARGET_NR_oldstat { TARGET_NR_oldstat, "oldstat" , NULL, NULL, NULL }, #endif -#ifdef TARGET_NR_oldumount -{ TARGET_NR_oldumount, "oldumount" , NULL, NULL, NULL }, -#endif #ifdef TARGET_NR_olduname { TARGET_NR_olduname, "olduname" , NULL, NULL, NULL }, #endif @@ -1524,3 +1521,9 @@ #ifdef TARGET_NR_pipe2 { TARGET_NR_pipe2, "pipe2", NULL, NULL, NULL }, #endif +#ifdef TARGET_NR_atomic_cmpxchg_32 +{ TARGET_NR_atomic_cmpxchg_32, "atomic_cmpxchg_32", NULL, NULL, NULL }, +#endif +#ifdef TARGET_NR_atomic_barrier +{ TARGET_NR_atomic_barrier, "atomic_barrier", NULL, NULL, NULL }, +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c62d8754f0..4a14a43037 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -106,6 +106,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include <linux/dm-ioctl.h> #include <linux/reboot.h> #include <linux/route.h> +#include <linux/filter.h> #include "linux_loop.h" #include "cpu-uname.h" @@ -1149,11 +1150,15 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, break; } - cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level); + if (tswap32(target_cmsg->cmsg_level) == TARGET_SOL_SOCKET) { + cmsg->cmsg_level = SOL_SOCKET; + } else { + cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level); + } cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type); cmsg->cmsg_len = CMSG_LEN(len); - if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); memcpy(data, target_data, len); } else { @@ -1204,11 +1209,15 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, break; } - target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); + if (cmsg->cmsg_level == SOL_SOCKET) { + target_cmsg->cmsg_level = tswap32(TARGET_SOL_SOCKET); + } else { + target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); + } target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len)); - if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) { int *fd = (int *)data; int *target_fd = (int *)target_data; @@ -1216,7 +1225,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, for (i = 0; i < numfds; i++) target_fd[i] = tswap32(fd[i]); - } else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) && + } else if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP) && (len == sizeof(struct timeval))) { /* copy struct timeval to target */ @@ -1314,6 +1323,26 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, goto unimplemented; } break; + case SOL_IPV6: + switch (optname) { + case IPV6_MTU_DISCOVER: + case IPV6_MTU: + case IPV6_V6ONLY: + case IPV6_RECVPKTINFO: + val = 0; + if (optlen < sizeof(uint32_t)) { + return -TARGET_EINVAL; + } + if (get_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + ret = get_errno(setsockopt(sockfd, level, optname, + &val, sizeof(val))); + break; + default: + goto unimplemented; + } + break; case SOL_RAW: switch (optname) { case ICMP_FILTER: @@ -1357,6 +1386,49 @@ set_timeout: case TARGET_SO_SNDTIMEO: optname = SO_SNDTIMEO; goto set_timeout; + case TARGET_SO_ATTACH_FILTER: + { + struct target_sock_fprog *tfprog; + struct target_sock_filter *tfilter; + struct sock_fprog fprog; + struct sock_filter *filter; + int i; + + if (optlen != sizeof(*tfprog)) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_READ, tfprog, optval_addr, 0)) { + return -TARGET_EFAULT; + } + if (!lock_user_struct(VERIFY_READ, tfilter, + tswapal(tfprog->filter), 0)) { + unlock_user_struct(tfprog, optval_addr, 1); + return -TARGET_EFAULT; + } + + fprog.len = tswap16(tfprog->len); + filter = malloc(fprog.len * sizeof(*filter)); + if (filter == NULL) { + unlock_user_struct(tfilter, tfprog->filter, 1); + unlock_user_struct(tfprog, optval_addr, 1); + return -TARGET_ENOMEM; + } + for (i = 0; i < fprog.len; i++) { + filter[i].code = tswap16(tfilter[i].code); + filter[i].jt = tfilter[i].jt; + filter[i].jf = tfilter[i].jf; + filter[i].k = tswap32(tfilter[i].k); + } + fprog.filter = filter; + + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, + SO_ATTACH_FILTER, &fprog, sizeof(fprog))); + free(filter); + + unlock_user_struct(tfilter, tfprog->filter, 1); + unlock_user_struct(tfprog, optval_addr, 1); + return ret; + } /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -1701,7 +1773,7 @@ static void unlock_iovec(struct iovec *vec, abi_ulong target_addr, free(vec); } -static inline void target_to_host_sock_type(int *type) +static inline int target_to_host_sock_type(int *type) { int host_type = 0; int target_type = *type; @@ -1718,22 +1790,56 @@ static inline void target_to_host_sock_type(int *type) break; } if (target_type & TARGET_SOCK_CLOEXEC) { +#if defined(SOCK_CLOEXEC) host_type |= SOCK_CLOEXEC; +#else + return -TARGET_EINVAL; +#endif } if (target_type & TARGET_SOCK_NONBLOCK) { +#if defined(SOCK_NONBLOCK) host_type |= SOCK_NONBLOCK; +#elif !defined(O_NONBLOCK) + return -TARGET_EINVAL; +#endif } *type = host_type; + return 0; +} + +/* Try to emulate socket type flags after socket creation. */ +static int sock_flags_fixup(int fd, int target_type) +{ +#if !defined(SOCK_NONBLOCK) && defined(O_NONBLOCK) + if (target_type & TARGET_SOCK_NONBLOCK) { + int flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { + close(fd); + return -TARGET_EINVAL; + } + } +#endif + return fd; } /* do_socket() Must return target values and target errnos. */ static abi_long do_socket(int domain, int type, int protocol) { - target_to_host_sock_type(&type); + int target_type = type; + int ret; + + ret = target_to_host_sock_type(&type); + if (ret) { + return ret; + } if (domain == PF_NETLINK) return -EAFNOSUPPORT; /* do not NETLINK socket connections possible */ - return get_errno(socket(domain, type, protocol)); + ret = get_errno(socket(domain, type, protocol)); + if (ret >= 0) { + ret = sock_flags_fixup(ret, target_type); + } + return ret; } /* do_bind() Must return target values and target errnos. */ @@ -5071,22 +5177,70 @@ static int is_proc_myself(const char *filename, const char *entry) return 0; } +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +static int is_proc(const char *filename, const char *entry) +{ + return strcmp(filename, entry) == 0; +} + +static int open_net_route(void *cpu_env, int fd) +{ + FILE *fp; + char *line = NULL; + size_t len = 0; + ssize_t read; + + fp = fopen("/proc/net/route", "r"); + if (fp == NULL) { + return -EACCES; + } + + /* read header */ + + read = getline(&line, &len, fp); + dprintf(fd, "%s", line); + + /* read routes */ + + while ((read = getline(&line, &len, fp)) != -1) { + char iface[16]; + uint32_t dest, gw, mask; + unsigned int flags, refcnt, use, metric, mtu, window, irtt; + sscanf(line, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", + iface, &dest, &gw, &flags, &refcnt, &use, &metric, + &mask, &mtu, &window, &irtt); + dprintf(fd, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", + iface, tswap32(dest), tswap32(gw), flags, refcnt, use, + metric, tswap32(mask), mtu, window, irtt); + } + + free(line); + fclose(fp); + + return 0; +} +#endif + static int do_open(void *cpu_env, const char *pathname, int flags, mode_t mode) { struct fake_open { const char *filename; int (*fill)(void *cpu_env, int fd); + int (*cmp)(const char *s1, const char *s2); }; const struct fake_open *fake_open; static const struct fake_open fakes[] = { - { "maps", open_self_maps }, - { "stat", open_self_stat }, - { "auxv", open_self_auxv }, - { NULL, NULL } + { "maps", open_self_maps, is_proc_myself }, + { "stat", open_self_stat, is_proc_myself }, + { "auxv", open_self_auxv, is_proc_myself }, +#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) + { "/proc/net/route", open_net_route, is_proc }, +#endif + { NULL, NULL, NULL } }; for (fake_open = fakes; fake_open->filename; fake_open++) { - if (is_proc_myself(pathname, fake_open->filename)) { + if (fake_open->cmp(pathname, fake_open->filename)) { break; } } @@ -5697,7 +5851,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; -#ifdef TARGET_NR_umount2 /* not on alpha */ +#ifdef TARGET_NR_umount2 case TARGET_NR_umount2: if (!(p = lock_user_string(arg1))) goto efault; @@ -9013,6 +9167,34 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } #endif +#ifdef TARGET_NR_atomic_cmpxchg_32 + case TARGET_NR_atomic_cmpxchg_32: + { + /* should use start_exclusive from main.c */ + abi_ulong mem_value; + if (get_user_u32(mem_value, arg6)) { + target_siginfo_t info; + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = arg6; + queue_signal((CPUArchState *)cpu_env, info.si_signo, &info); + ret = 0xdeadbeef; + + } + if (mem_value == arg2) + put_user_u32(arg1, arg6); + ret = mem_value; + break; + } +#endif +#ifdef TARGET_NR_atomic_barrier + case TARGET_NR_atomic_barrier: + { + /* Like the kernel implementation and the qemu arm barrier, no-op this? */ + break; + } +#endif default: unimplemented: gemu_log("qemu: Unsupported syscall: %d\n", num); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 2ebe3560d7..5f53a28d1b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -119,6 +119,18 @@ struct target_sockaddr { uint8_t sa_data[14]; }; +struct target_sock_filter { + abi_ushort code; + uint8_t jt; + uint8_t jf; + abi_uint k; +}; + +struct target_sock_fprog { + abi_ushort len; + abi_ulong filter; +}; + struct target_in_addr { uint32_t s_addr; /* big endian */ }; diff --git a/migration-rdma.c b/migration-rdma.c index 05a155b93d..f94f3b4e3a 100644 --- a/migration-rdma.c +++ b/migration-rdma.c @@ -356,6 +356,7 @@ typedef struct RDMAContext { */ struct rdma_cm_id *cm_id; /* connection manager ID */ struct rdma_cm_id *listen_id; + bool connected; struct ibv_context *verbs; struct rdma_event_channel *channel; @@ -510,19 +511,21 @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, int *resp_idx, int (*callback)(RDMAContext *rdma)); -static inline uint64_t ram_chunk_index(uint8_t *start, uint8_t *host) +static inline uint64_t ram_chunk_index(const uint8_t *start, + const uint8_t *host) { return ((uintptr_t) host - (uintptr_t) start) >> RDMA_REG_CHUNK_SHIFT; } -static inline uint8_t *ram_chunk_start(RDMALocalBlock *rdma_ram_block, +static inline uint8_t *ram_chunk_start(const RDMALocalBlock *rdma_ram_block, uint64_t i) { return (uint8_t *) (((uintptr_t) rdma_ram_block->local_host_addr) + (i << RDMA_REG_CHUNK_SHIFT)); } -static inline uint8_t *ram_chunk_end(RDMALocalBlock *rdma_ram_block, uint64_t i) +static inline uint8_t *ram_chunk_end(const RDMALocalBlock *rdma_ram_block, + uint64_t i) { uint8_t *result = ram_chunk_start(rdma_ram_block, i) + (1UL << RDMA_REG_CHUNK_SHIFT); @@ -2194,7 +2197,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) struct rdma_cm_event *cm_event; int ret, idx; - if (rdma->cm_id) { + if (rdma->cm_id && rdma->connected) { if (rdma->error_state) { RDMAControlHeader head = { .len = 0, .type = RDMA_CONTROL_ERROR, @@ -2213,7 +2216,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) } } DDPRINTF("Disconnected.\n"); - rdma->cm_id = NULL; + rdma->connected = false; } g_free(rdma->block); @@ -2235,7 +2238,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) } if (rdma->qp) { - ibv_destroy_qp(rdma->qp); + rdma_destroy_qp(rdma->cm_id); rdma->qp = NULL; } if (rdma->cq) { @@ -2372,6 +2375,7 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp) rdma->cm_id = NULL; goto err_rdma_source_connect; } + rdma->connected = true; memcpy(&cap, cm_event->param.conn.private_data, sizeof(cap)); network_to_caps(&cap); @@ -2906,6 +2910,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) } rdma_ack_cm_event(cm_event); + rdma->connected = true; ret = qemu_rdma_post_recv_control(rdma, RDMA_WRID_READY); if (ret) { diff --git a/migration.c b/migration.c index 200d404547..b4f8462ae4 100644 --- a/migration.c +++ b/migration.c @@ -567,7 +567,8 @@ static void *migration_thread(void *opaque) if (!qemu_file_rate_limit(s->file)) { DPRINTF("iterate\n"); pending_size = qemu_savevm_state_pending(s->file, max_size); - DPRINTF("pending size %lu max %lu\n", pending_size, max_size); + DPRINTF("pending size %" PRIu64 " max %" PRIu64 "\n", + pending_size, max_size); if (pending_size && pending_size >= max_size) { qemu_savevm_state_iterate(s->file); } else { diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 05fc7c2fae..6727f0ca39 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 49f2d291fc..4d6e48fcbe 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -123,6 +123,7 @@ static void vring_init(struct vring *vr, unsigned int num, void *p, /* We're running with interrupts off anyways, so don't bother */ vr->used->flags = VRING_USED_F_NO_NOTIFY; vr->used->idx = 0; + vr->used_idx = 0; debug_print_addr("init vr", vr); } @@ -150,8 +151,6 @@ static void vring_send_buf(struct vring *vr, void *p, int len, int flags) if (!(flags & VRING_DESC_F_NEXT)) { vr->avail->idx++; } - - vr->used->idx = vr->next_idx; } static u64 get_clock(void) @@ -180,7 +179,8 @@ static int vring_wait_reply(struct vring *vr, int timeout) struct subchannel_id schid = vr->schid; int r = 0; - while (vr->used->idx == vr->next_idx) { + /* Wait until the used index has moved. */ + while (vr->used->idx == vr->used_idx) { vring_notify(schid); if (timeout && (get_second() >= target_second)) { r = 1; @@ -189,6 +189,7 @@ static int vring_wait_reply(struct vring *vr, int timeout) yield(); } + vr->used_idx = vr->used->idx; vr->next_idx = 0; vr->desc[0].len = 0; vr->desc[0].flags = 0; diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 86fdd579b4..772a63f152 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -115,6 +115,7 @@ struct vring_used { struct vring { unsigned int num; int next_idx; + int used_idx; struct vring_desc *desc; struct vring_avail *avail; struct vring_used *used; @@ -566,6 +566,13 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) return f; } +/* + * Get last error for stream f + * + * Return negative error value if there has been an error on previous + * operations, return 0 if no error happened. + * + */ int qemu_file_get_error(QEMUFile *f) { return f->last_error; @@ -642,7 +649,7 @@ void ram_control_after_iterate(QEMUFile *f, uint64_t flags) void ram_control_load_hook(QEMUFile *f, uint64_t flags) { - int ret = 0; + int ret = -EINVAL; if (f->ops->hook_ram_load) { ret = f->ops->hook_ram_load(f, f->opaque, flags); diff --git a/target-s390x/arch_dump.c b/target-s390x/arch_dump.c index 9d36116242..5cbb53ca2e 100644 --- a/target-s390x/arch_dump.c +++ b/target-s390x/arch_dump.c @@ -151,6 +151,7 @@ static int s390x_write_all_elf64_notes(const char *note_name, int ret = -1; for (nf = note_func; nf->note_contents_func; nf++) { + memset(¬e, 0, sizeof(note)); note.hdr.n_namesz = cpu_to_be32(sizeof(note.name)); note.hdr.n_descsz = cpu_to_be32(nf->contents_size); strncpy(note.name, note_name, sizeof(note.name)); diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 8be5648806..a2c077bdcd 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -148,6 +148,7 @@ typedef struct CPUS390XState { } CPUS390XState; #include "cpu-qom.h" +#include <sysemu/kvm.h> /* distinguish between 24 bit and 31 bit addressing */ #define HIGH_ORDER_BIT 0x80000000 @@ -692,6 +693,14 @@ static inline const char *cc_name(int cc_op) return cc_names[cc_op]; } +static inline void setcc(S390CPU *cpu, uint64_t cc) +{ + CPUS390XState *env = &cpu->env; + + env->psw.mask &= ~(3ull << 44); + env->psw.mask |= (cc & 3) << 44; +} + typedef struct LowCore { /* prefix area: defined by architecture */ @@ -1058,8 +1067,6 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen); void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, uintptr_t retaddr); -#include <sysemu/kvm.h> - #ifdef CONFIG_KVM void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, uint16_t subchannel_nr, uint32_t io_int_parm, diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c index 85fd285736..8d6363df4e 100644 --- a/target-s390x/ioinst.c +++ b/target-s390x/ioinst.c @@ -36,7 +36,7 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, return 0; } -int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -44,8 +44,8 @@ int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("xsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -66,11 +66,10 @@ int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) cc = 1; break; } - - return cc; + setcc(cpu, cc); } -int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -78,8 +77,8 @@ int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("csch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -91,10 +90,10 @@ int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) } else { cc = 0; } - return cc; + setcc(cpu, cc); } -int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -102,8 +101,8 @@ int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("hsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -124,8 +123,7 @@ int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) cc = 1; break; } - - return cc; + setcc(cpu, cc); } static int ioinst_schib_valid(SCHIB *schib) @@ -141,7 +139,7 @@ static int ioinst_schib_valid(SCHIB *schib) return 1; } -int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; @@ -150,22 +148,21 @@ int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) int ret = -ENODEV; int cc; hwaddr len = sizeof(*schib); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } schib = s390_cpu_physical_memory_map(env, addr, &len, 0); if (!schib || len != sizeof(*schib)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_schib_valid(schib)) { program_interrupt(env, PGM_OPERAND, 2); - cc = -EIO; goto out; } trace_ioinst_sch_id("msch", cssid, ssid, schid); @@ -187,9 +184,10 @@ int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) cc = 1; break; } + setcc(cpu, cc); + out: s390_cpu_physical_memory_unmap(env, schib, len, 0); - return cc; } static void copy_orb_from_guest(ORB *dest, const ORB *src) @@ -213,7 +211,7 @@ static int ioinst_orb_valid(ORB *orb) return 1; } -int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; @@ -222,23 +220,22 @@ int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) int ret = -ENODEV; int cc; hwaddr len = sizeof(*orig_orb); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0); if (!orig_orb || len != sizeof(*orig_orb)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } copy_orb_from_guest(&orb, orig_orb); if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) || !ioinst_orb_valid(&orb)) { program_interrupt(env, PGM_OPERAND, 2); - cc = -EIO; goto out; } trace_ioinst_sch_id("ssch", cssid, ssid, schid); @@ -260,38 +257,39 @@ int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) cc = 1; break; } + setcc(cpu, cc); out: s390_cpu_physical_memory_unmap(env, orig_orb, len, 0); - return cc; } -int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb) +void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb) { CRW *crw; uint64_t addr; int cc; hwaddr len = sizeof(*crw); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } crw = s390_cpu_physical_memory_map(env, addr, &len, 1); if (!crw || len != sizeof(*crw)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } cc = css_do_stcrw(crw); /* 0 - crw stored, 1 - zeroes stored */ + setcc(cpu, cc); + out: s390_cpu_physical_memory_unmap(env, crw, len, 1); - return cc; } -int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) { int cssid, ssid, schid, m; SubchDev *sch; @@ -299,22 +297,21 @@ int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) int cc; SCHIB *schib; hwaddr len = sizeof(*schib); + CPUS390XState *env = &cpu->env; addr = decode_basedisp_s(env, ipb); if (addr & 3) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } schib = s390_cpu_physical_memory_map(env, addr, &len, 1); if (!schib || len != sizeof(*schib)) { program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; goto out; } if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { program_interrupt(env, PGM_OPERAND, 2); - cc = -EIO; goto out; } trace_ioinst_sch_id("stsch", cssid, ssid, schid); @@ -336,9 +333,10 @@ int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) cc = 0; } } + setcc(cpu, cc); + out: s390_cpu_physical_memory_unmap(env, schib, len, 1); - return cc; } int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) @@ -575,7 +573,7 @@ static void ioinst_handle_chsc_unimplemented(ChscResp *res) res->param = 0; } -int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) +void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb) { ChscReq *req; ChscResp *res; @@ -584,7 +582,7 @@ int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) uint16_t len; uint16_t command; hwaddr map_size = TARGET_PAGE_SIZE; - int ret = 0; + CPUS390XState *env = &cpu->env; trace_ioinst("chsc"); reg = (ipb >> 20) & 0x00f; @@ -592,19 +590,17 @@ int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) /* Page boundary? */ if (addr & 0xfff) { program_interrupt(env, PGM_SPECIFICATION, 2); - return -EIO; + return; } req = s390_cpu_physical_memory_map(env, addr, &map_size, 1); if (!req || map_size != TARGET_PAGE_SIZE) { program_interrupt(env, PGM_ADDRESSING, 2); - ret = -EIO; goto out; } len = be16_to_cpu(req->len); /* Length field valid? */ if ((len < 16) || (len > 4088) || (len & 7)) { program_interrupt(env, PGM_OPERAND, 2); - ret = -EIO; goto out; } memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); @@ -628,7 +624,6 @@ int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) out: s390_cpu_physical_memory_unmap(env, req, map_size, 1); - return ret; } int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) @@ -666,18 +661,19 @@ out: #define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1) #define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001) -int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, - uint32_t ipb) +void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, + uint32_t ipb) { uint8_t mbk; int update; int dct; + CPUS390XState *env = &cpu->env; trace_ioinst("schm"); if (SCHM_REG1_RES(reg1)) { program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } mbk = SCHM_REG1_MBK(reg1); @@ -686,15 +682,13 @@ int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, if (update && (reg2 & 0x000000000000001f)) { program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } css_do_schm(mbk, update, dct, update ? reg2 : 0); - - return 0; } -int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1) { int cssid, ssid, schid, m; SubchDev *sch; @@ -702,8 +696,8 @@ int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) int cc; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); + return; } trace_ioinst_sch_id("rsch", cssid, ssid, schid); sch = css_find_subch(m, cssid, ssid, schid); @@ -724,24 +718,23 @@ int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) cc = 1; break; } - - return cc; - + setcc(cpu, cc); } #define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00) #define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16) #define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff) -int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1) { int cc; uint8_t cssid; uint8_t chpid; int ret; + CPUS390XState *env = &cpu->env; if (RCHP_REG1_RES(reg1)) { program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } cssid = RCHP_REG1_CSSID(reg1); @@ -764,19 +757,16 @@ int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1) default: /* Invalid channel subsystem. */ program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + return; } - - return cc; + setcc(cpu, cc); } #define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000) -int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1) +void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1) { /* We do not provide address limit checking, so let's suppress it. */ if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) { - program_interrupt(env, PGM_OPERAND, 2); - return -EIO; + program_interrupt(&cpu->env, PGM_OPERAND, 2); } - return 0; } diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h index 7bed2910dc..613da49b3b 100644 --- a/target-s390x/ioinst.h +++ b/target-s390x/ioinst.h @@ -214,20 +214,20 @@ typedef struct IOIntCode { int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, int *schid); -int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); -int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); -int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb); -int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); +void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); +void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb); +void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb); int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); -int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb); +void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb); int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb); -int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, - uint32_t ipb); -int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1); -int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1); +void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2, + uint32_t ipb); +void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1); +void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1); #endif diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 4923e0a717..a444f6999b 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -418,18 +418,6 @@ static void enter_pgmcheck(S390CPU *cpu, uint16_t code) kvm_s390_interrupt(cpu, KVM_S390_PROGRAM_INT, code); } -static inline void setcc(S390CPU *cpu, uint64_t cc) -{ - CPUS390XState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - cs->kvm_run->psw_mask &= ~(3ull << 44); - cs->kvm_run->psw_mask |= (cc & 3) << 44; - - env->psw.mask &= ~(3ul << 44); - env->psw.mask |= (cc & 3) << 44; -} - static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint16_t ipbh0) { @@ -439,6 +427,10 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, int r = 0; cpu_synchronize_state(CPU(cpu)); + if (env->psw.mask & PSW_MASK_PSTATE) { + enter_pgmcheck(cpu, PGM_PRIVILEGED); + return 0; + } sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; @@ -454,8 +446,6 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, uint8_t ipa0, uint8_t ipa1, uint8_t ipb) { - int r = 0; - int no_cc = 0; CPUS390XState *env = &cpu->env; CPUState *cs = CPU(cpu); @@ -469,69 +459,61 @@ static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, switch (ipa1) { case PRIV_XSCH: - r = ioinst_handle_xsch(env, env->regs[1]); + ioinst_handle_xsch(cpu, env->regs[1]); break; case PRIV_CSCH: - r = ioinst_handle_csch(env, env->regs[1]); + ioinst_handle_csch(cpu, env->regs[1]); break; case PRIV_HSCH: - r = ioinst_handle_hsch(env, env->regs[1]); + ioinst_handle_hsch(cpu, env->regs[1]); break; case PRIV_MSCH: - r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb); + ioinst_handle_msch(cpu, env->regs[1], run->s390_sieic.ipb); break; case PRIV_SSCH: - r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb); + ioinst_handle_ssch(cpu, env->regs[1], run->s390_sieic.ipb); break; case PRIV_STCRW: - r = ioinst_handle_stcrw(env, run->s390_sieic.ipb); + ioinst_handle_stcrw(cpu, run->s390_sieic.ipb); break; case PRIV_STSCH: - r = ioinst_handle_stsch(env, env->regs[1], run->s390_sieic.ipb); + ioinst_handle_stsch(cpu, env->regs[1], run->s390_sieic.ipb); break; case PRIV_TSCH: /* We should only get tsch via KVM_EXIT_S390_TSCH. */ fprintf(stderr, "Spurious tsch intercept\n"); break; case PRIV_CHSC: - r = ioinst_handle_chsc(env, run->s390_sieic.ipb); + ioinst_handle_chsc(cpu, run->s390_sieic.ipb); break; case PRIV_TPI: /* This should have been handled by kvm already. */ fprintf(stderr, "Spurious tpi intercept\n"); break; case PRIV_SCHM: - no_cc = 1; - r = ioinst_handle_schm(env, env->regs[1], env->regs[2], - run->s390_sieic.ipb); + ioinst_handle_schm(cpu, env->regs[1], env->regs[2], + run->s390_sieic.ipb); break; case PRIV_RSCH: - r = ioinst_handle_rsch(env, env->regs[1]); + ioinst_handle_rsch(cpu, env->regs[1]); break; case PRIV_RCHP: - r = ioinst_handle_rchp(env, env->regs[1]); + ioinst_handle_rchp(cpu, env->regs[1]); break; case PRIV_STCPS: /* We do not provide this instruction, it is suppressed. */ - no_cc = 1; - r = 0; break; case PRIV_SAL: - no_cc = 1; - r = ioinst_handle_sal(env, env->regs[1]); + ioinst_handle_sal(cpu, env->regs[1]); break; case PRIV_SIGA: /* Not provided, set CC = 3 for subchannel not operational */ - r = 3; + setcc(cpu, 3); break; default: return -1; } - if (r >= 0 && !no_cc) { - setcc(cpu, r); - } - return 0; } diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 1690907169..10d04252d5 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -33,6 +33,7 @@ #include "exec/softmmu_exec.h" #include "sysemu/cpus.h" #include "sysemu/sysemu.h" +#include "hw/s390x/ebcdic.h" #endif /* #define DEBUG_HELPER */ @@ -72,86 +73,6 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp) #ifndef CONFIG_USER_ONLY -/* EBCDIC handling */ -static const uint8_t ebcdic2ascii[] = { - 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, - 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, - 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, - 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, - 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, - 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, - 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, - 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, - 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, - 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, - 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, - 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, - 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, - 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, - 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, - 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, - 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, - 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, - 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, - 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, - 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, -}; - -static const uint8_t ascii2ebcdic[] = { - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, - 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, - 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, - 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, - 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, - 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, - 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, - 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, - 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF -}; - -static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) -{ - int i; - - for (i = 0; i < len; i++) { - p[i] = ascii2ebcdic[(uint8_t)ascii[i]]; - } -} - void program_interrupt(CPUS390XState *env, uint32_t code, int ilen) { qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", @@ -192,6 +113,29 @@ static void cpu_reset_all(void) } } +static void cpu_full_reset_all(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_reset(cpu); + } +} + +static int modified_clear_reset(S390CPU *cpu) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); + + pause_all_vcpus(); + cpu_synchronize_all_states(); + cpu_full_reset_all(); + io_subsystem_reset(); + scc->load_normal(CPU(cpu)); + cpu_synchronize_all_post_reset(); + resume_all_vcpus(); + return 0; +} + static int load_normal_reset(S390CPU *cpu) { S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); @@ -225,6 +169,9 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) } switch (subcode) { + case 0: + modified_clear_reset(s390_env_get_cpu(env)); + break; case 1: load_normal_reset(s390_env_get_cpu(env)); break; |