Merge remote-tracking branch 'agraf/s390-for-upstream' into staging
# By Cornelia Huck (13) and others # Via Alexander Graf * agraf/s390-for-upstream: s390: Drop set_bit usage in virtio_ccw. s390: css error codes. s390: Use s390_cpu_physical_memory_map for tpi. sclpconsole: Don't instantiate sclpconsole with -nodefaults s390: Add s390-ccw-virtio machine. s390-virtio: Check for NULL device in reset hypercall s390: Move hw files to hw/s390x virtio-s390: add a reset function to virtio-s390 devices s390: Make typeinfo const s390: Add new channel I/O based virtio transport. s390-virtio: Factor out some initialization code. s390: Wire up channel I/O in kvm. s390: Virtual channel subsystem support. s390: Add channel I/O instructions. s390: I/O interrupt and machine check injection. s390: Channel I/O basic definitions. s390: Add mapping helper functions. s390: Lowcore mapping helper. s390: Add default support for SCLP console
This commit is contained in:
		
						commit
						6ac5107dc7
					
				| @ -33,6 +33,7 @@ typedef struct QEMUMachine { | |||||||
|     unsigned int no_serial:1, |     unsigned int no_serial:1, | ||||||
|         no_parallel:1, |         no_parallel:1, | ||||||
|         use_virtcon:1, |         use_virtcon:1, | ||||||
|  |         use_sclp:1, | ||||||
|         no_floppy:1, |         no_floppy:1, | ||||||
|         no_cdrom:1, |         no_cdrom:1, | ||||||
|         no_sdcard:1; |         no_sdcard:1; | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| obj-y = s390-virtio-bus.o s390-virtio.o | obj-y = s390-virtio-bus.o s390-virtio.o | ||||||
| 
 |  | ||||||
| obj-y := $(addprefix ../,$(obj-y)) |  | ||||||
| obj-y += s390-virtio-hcall.o | obj-y += s390-virtio-hcall.o | ||||||
| obj-y += sclp.o | obj-y += sclp.o | ||||||
| obj-y += event-facility.o | obj-y += event-facility.o | ||||||
| obj-y += sclpquiesce.o sclpconsole.o | obj-y += sclpquiesce.o sclpconsole.o | ||||||
| obj-y += ipl.o | obj-y += ipl.o | ||||||
|  | obj-y += css.o | ||||||
|  | obj-y += s390-virtio-ccw.o | ||||||
|  | obj-y += virtio-ccw.o | ||||||
|  | |||||||
							
								
								
									
										1277
									
								
								hw/s390x/css.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1277
									
								
								hw/s390x/css.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										99
									
								
								hw/s390x/css.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								hw/s390x/css.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | |||||||
|  | /*
 | ||||||
|  |  * Channel subsystem structures and definitions. | ||||||
|  |  * | ||||||
|  |  * Copyright 2012 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  |  * directory. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef CSS_H | ||||||
|  | #define CSS_H | ||||||
|  | 
 | ||||||
|  | #include "ioinst.h" | ||||||
|  | 
 | ||||||
|  | /* Channel subsystem constants. */ | ||||||
|  | #define MAX_SCHID 65535 | ||||||
|  | #define MAX_SSID 3 | ||||||
|  | #define MAX_CSSID 254 /* 255 is reserved */ | ||||||
|  | #define MAX_CHPID 255 | ||||||
|  | 
 | ||||||
|  | #define MAX_CIWS 62 | ||||||
|  | 
 | ||||||
|  | typedef struct CIW { | ||||||
|  |     uint8_t type; | ||||||
|  |     uint8_t command; | ||||||
|  |     uint16_t count; | ||||||
|  | } QEMU_PACKED CIW; | ||||||
|  | 
 | ||||||
|  | typedef struct SenseId { | ||||||
|  |     /* common part */ | ||||||
|  |     uint8_t reserved;        /* always 0x'FF' */ | ||||||
|  |     uint16_t cu_type;        /* control unit type */ | ||||||
|  |     uint8_t cu_model;        /* control unit model */ | ||||||
|  |     uint16_t dev_type;       /* device type */ | ||||||
|  |     uint8_t dev_model;       /* device model */ | ||||||
|  |     uint8_t unused;          /* padding byte */ | ||||||
|  |     /* extended part */ | ||||||
|  |     CIW ciw[MAX_CIWS];       /* variable # of CIWs */ | ||||||
|  | } QEMU_PACKED SenseId; | ||||||
|  | 
 | ||||||
|  | /* Channel measurements, from linux/drivers/s390/cio/cmf.c. */ | ||||||
|  | typedef struct CMB { | ||||||
|  |     uint16_t ssch_rsch_count; | ||||||
|  |     uint16_t sample_count; | ||||||
|  |     uint32_t device_connect_time; | ||||||
|  |     uint32_t function_pending_time; | ||||||
|  |     uint32_t device_disconnect_time; | ||||||
|  |     uint32_t control_unit_queuing_time; | ||||||
|  |     uint32_t device_active_only_time; | ||||||
|  |     uint32_t reserved[2]; | ||||||
|  | } QEMU_PACKED CMB; | ||||||
|  | 
 | ||||||
|  | typedef struct CMBE { | ||||||
|  |     uint32_t ssch_rsch_count; | ||||||
|  |     uint32_t sample_count; | ||||||
|  |     uint32_t device_connect_time; | ||||||
|  |     uint32_t function_pending_time; | ||||||
|  |     uint32_t device_disconnect_time; | ||||||
|  |     uint32_t control_unit_queuing_time; | ||||||
|  |     uint32_t device_active_only_time; | ||||||
|  |     uint32_t device_busy_time; | ||||||
|  |     uint32_t initial_command_response_time; | ||||||
|  |     uint32_t reserved[7]; | ||||||
|  | } QEMU_PACKED CMBE; | ||||||
|  | 
 | ||||||
|  | struct SubchDev { | ||||||
|  |     /* channel-subsystem related things: */ | ||||||
|  |     uint8_t cssid; | ||||||
|  |     uint8_t ssid; | ||||||
|  |     uint16_t schid; | ||||||
|  |     uint16_t devno; | ||||||
|  |     SCHIB curr_status; | ||||||
|  |     uint8_t sense_data[32]; | ||||||
|  |     hwaddr channel_prog; | ||||||
|  |     CCW1 last_cmd; | ||||||
|  |     bool last_cmd_valid; | ||||||
|  |     ORB *orb; | ||||||
|  |     /* transport-provided data: */ | ||||||
|  |     int (*ccw_cb) (SubchDev *, CCW1); | ||||||
|  |     SenseId id; | ||||||
|  |     void *driver_data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid, | ||||||
|  |                                        uint16_t schid); | ||||||
|  | int css_create_css_image(uint8_t cssid, bool default_image); | ||||||
|  | bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno); | ||||||
|  | void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, | ||||||
|  |                       uint16_t devno, SubchDev *sch); | ||||||
|  | void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type); | ||||||
|  | void css_reset(void); | ||||||
|  | void css_reset_sch(SubchDev *sch); | ||||||
|  | void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); | ||||||
|  | void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, | ||||||
|  |                            int hotplugged, int add); | ||||||
|  | void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); | ||||||
|  | #endif | ||||||
| @ -159,7 +159,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data) | |||||||
|     dc->no_user = 1; |     dc->no_user = 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static TypeInfo s390_ipl_info = { | static const TypeInfo s390_ipl_info = { | ||||||
|     .class_init = s390_ipl_class_init, |     .class_init = s390_ipl_class_init, | ||||||
|     .parent = TYPE_SYS_BUS_DEVICE, |     .parent = TYPE_SYS_BUS_DEVICE, | ||||||
|     .name  = "s390-ipl", |     .name  = "s390-ipl", | ||||||
|  | |||||||
| @ -17,12 +17,12 @@ | |||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 |  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include "hw.h" | #include "hw/hw.h" | ||||||
| #include "block/block.h" | #include "block/block.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "boards.h" | #include "hw/boards.h" | ||||||
| #include "monitor/monitor.h" | #include "monitor/monitor.h" | ||||||
| #include "loader.h" | #include "hw/loader.h" | ||||||
| #include "elf.h" | #include "elf.h" | ||||||
| #include "hw/virtio.h" | #include "hw/virtio.h" | ||||||
| #include "hw/virtio-rng.h" | #include "hw/virtio-rng.h" | ||||||
| @ -31,7 +31,7 @@ | |||||||
| #include "hw/sysbus.h" | #include "hw/sysbus.h" | ||||||
| #include "sysemu/kvm.h" | #include "sysemu/kvm.h" | ||||||
| 
 | 
 | ||||||
| #include "hw/s390-virtio-bus.h" | #include "hw/s390x/s390-virtio-bus.h" | ||||||
| #include "hw/virtio-bus.h" | #include "hw/virtio-bus.h" | ||||||
| 
 | 
 | ||||||
| /* #define DEBUG_S390 */ | /* #define DEBUG_S390 */ | ||||||
| @ -508,6 +508,13 @@ static int s390_virtio_busdev_init(DeviceState *dev) | |||||||
|     return _info->init(_dev); |     return _info->init(_dev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void s390_virtio_busdev_reset(DeviceState *dev) | ||||||
|  | { | ||||||
|  |     VirtIOS390Device *_dev = (VirtIOS390Device *)dev; | ||||||
|  | 
 | ||||||
|  |     virtio_reset(_dev->vdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void virtio_s390_device_class_init(ObjectClass *klass, void *data) | static void virtio_s390_device_class_init(ObjectClass *klass, void *data) | ||||||
| { | { | ||||||
|     DeviceClass *dc = DEVICE_CLASS(klass); |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
| @ -515,6 +522,7 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data) | |||||||
|     dc->init = s390_virtio_busdev_init; |     dc->init = s390_virtio_busdev_init; | ||||||
|     dc->bus_type = TYPE_S390_VIRTIO_BUS; |     dc->bus_type = TYPE_S390_VIRTIO_BUS; | ||||||
|     dc->unplug = qdev_simple_unplug_cb; |     dc->unplug = qdev_simple_unplug_cb; | ||||||
|  |     dc->reset = s390_virtio_busdev_reset; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const TypeInfo virtio_s390_device_info = { | static const TypeInfo virtio_s390_device_info = { | ||||||
| @ -19,12 +19,12 @@ | |||||||
| #ifndef HW_S390_VIRTIO_BUS_H | #ifndef HW_S390_VIRTIO_BUS_H | ||||||
| #define HW_S390_VIRTIO_BUS_H 1 | #define HW_S390_VIRTIO_BUS_H 1 | ||||||
| 
 | 
 | ||||||
| #include "virtio-blk.h" | #include "hw/virtio-blk.h" | ||||||
| #include "virtio-net.h" | #include "hw/virtio-net.h" | ||||||
| #include "virtio-rng.h" | #include "hw/virtio-rng.h" | ||||||
| #include "virtio-serial.h" | #include "hw/virtio-serial.h" | ||||||
| #include "virtio-scsi.h" | #include "hw/virtio-scsi.h" | ||||||
| #include "virtio-bus.h" | #include "hw/virtio-bus.h" | ||||||
| 
 | 
 | ||||||
| #define VIRTIO_DEV_OFFS_TYPE		0	/* 8 bits */ | #define VIRTIO_DEV_OFFS_TYPE		0	/* 8 bits */ | ||||||
| #define VIRTIO_DEV_OFFS_NUM_VQ		1	/* 8 bits */ | #define VIRTIO_DEV_OFFS_NUM_VQ		1	/* 8 bits */ | ||||||
							
								
								
									
										134
									
								
								hw/s390x/s390-virtio-ccw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								hw/s390x/s390-virtio-ccw.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | /*
 | ||||||
|  |  * virtio ccw machine | ||||||
|  |  * | ||||||
|  |  * Copyright 2012 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.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/boards.h" | ||||||
|  | #include "exec/address-spaces.h" | ||||||
|  | #include "s390-virtio.h" | ||||||
|  | #include "sclp.h" | ||||||
|  | #include "ioinst.h" | ||||||
|  | #include "css.h" | ||||||
|  | #include "virtio-ccw.h" | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_hcall_notify(const uint64_t *args) | ||||||
|  | { | ||||||
|  |     uint64_t subch_id = args[0]; | ||||||
|  |     uint64_t queue = args[1]; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (!sch || !css_subch_visible(sch)) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |     virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); | ||||||
|  |     return 0; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_hcall_early_printk(const uint64_t *args) | ||||||
|  | { | ||||||
|  |     uint64_t mem = args[0]; | ||||||
|  | 
 | ||||||
|  |     if (mem < ram_size) { | ||||||
|  |         /* Early printk */ | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     return -EINVAL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_register_hcalls(void) | ||||||
|  | { | ||||||
|  |     s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, | ||||||
|  |                                    virtio_ccw_hcall_notify); | ||||||
|  |     /* Tolerate early printk. */ | ||||||
|  |     s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, | ||||||
|  |                                    virtio_ccw_hcall_early_printk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ccw_init(QEMUMachineInitArgs *args) | ||||||
|  | { | ||||||
|  |     ram_addr_t my_ram_size = args->ram_size; | ||||||
|  |     MemoryRegion *sysmem = get_system_memory(); | ||||||
|  |     MemoryRegion *ram = g_new(MemoryRegion, 1); | ||||||
|  |     int shift = 0; | ||||||
|  |     uint8_t *storage_keys; | ||||||
|  |     int ret; | ||||||
|  |     VirtualCssBus *css_bus; | ||||||
|  | 
 | ||||||
|  |     /* s390x ram size detection needs a 16bit multiplier + an increment. So
 | ||||||
|  |        guests > 64GB can be specified in 2MB steps etc. */ | ||||||
|  |     while ((my_ram_size >> (20 + shift)) > 65535) { | ||||||
|  |         shift++; | ||||||
|  |     } | ||||||
|  |     my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); | ||||||
|  | 
 | ||||||
|  |     /* lets propagate the changed ram size into the global variable. */ | ||||||
|  |     ram_size = my_ram_size; | ||||||
|  | 
 | ||||||
|  |     /* get a BUS */ | ||||||
|  |     css_bus = virtual_css_bus_init(); | ||||||
|  |     s390_sclp_init(); | ||||||
|  |     s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, | ||||||
|  |                       args->initrd_filename); | ||||||
|  | 
 | ||||||
|  |     /* register hypercalls */ | ||||||
|  |     virtio_ccw_register_hcalls(); | ||||||
|  | 
 | ||||||
|  |     /* allocate RAM */ | ||||||
|  |     memory_region_init_ram(ram, "s390.ram", my_ram_size); | ||||||
|  |     vmstate_register_ram_global(ram); | ||||||
|  |     memory_region_add_subregion(sysmem, 0, ram); | ||||||
|  | 
 | ||||||
|  |     /* allocate storage keys */ | ||||||
|  |     storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); | ||||||
|  | 
 | ||||||
|  |     /* init CPUs */ | ||||||
|  |     s390_init_cpus(args->cpu_model, storage_keys); | ||||||
|  | 
 | ||||||
|  |     if (kvm_enabled()) { | ||||||
|  |         kvm_s390_enable_css_support(s390_cpu_addr2state(0)); | ||||||
|  |     } | ||||||
|  |     /*
 | ||||||
|  |      * Create virtual css and set it as default so that non mcss-e | ||||||
|  |      * enabled guests only see virtio devices. | ||||||
|  |      */ | ||||||
|  |     ret = css_create_css_image(VIRTUAL_CSSID, true); | ||||||
|  |     assert(ret == 0); | ||||||
|  | 
 | ||||||
|  |     /* Create VirtIO network adapters */ | ||||||
|  |     s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static QEMUMachine ccw_machine = { | ||||||
|  |     .name = "s390-ccw-virtio", | ||||||
|  |     .alias = "s390-ccw", | ||||||
|  |     .desc = "VirtIO-ccw based S390 machine", | ||||||
|  |     .init = ccw_init, | ||||||
|  |     .block_default_type = IF_VIRTIO, | ||||||
|  |     .no_cdrom = 1, | ||||||
|  |     .no_floppy = 1, | ||||||
|  |     .no_serial = 1, | ||||||
|  |     .no_parallel = 1, | ||||||
|  |     .no_sdcard = 1, | ||||||
|  |     .use_sclp = 1, | ||||||
|  |     .max_cpus = 255, | ||||||
|  |     DEFAULT_MACHINE_OPTIONS, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void ccw_machine_init(void) | ||||||
|  | { | ||||||
|  |     qemu_register_machine(&ccw_machine); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | machine_init(ccw_machine_init) | ||||||
| @ -10,7 +10,7 @@ | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include "cpu.h" | #include "cpu.h" | ||||||
| #include "hw/s390-virtio.h" | #include "hw/s390x/s390-virtio.h" | ||||||
| 
 | 
 | ||||||
| #define MAX_DIAG_SUBCODES 255 | #define MAX_DIAG_SUBCODES 255 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -21,22 +21,22 @@ | |||||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 |  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include "hw.h" | #include "hw/hw.h" | ||||||
| #include "block/block.h" | #include "block/block.h" | ||||||
| #include "sysemu/blockdev.h" | #include "sysemu/blockdev.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "net/net.h" | #include "net/net.h" | ||||||
| #include "boards.h" | #include "hw/boards.h" | ||||||
| #include "monitor/monitor.h" | #include "monitor/monitor.h" | ||||||
| #include "loader.h" | #include "hw/loader.h" | ||||||
| #include "hw/virtio.h" | #include "hw/virtio.h" | ||||||
| #include "hw/sysbus.h" | #include "hw/sysbus.h" | ||||||
| #include "sysemu/kvm.h" | #include "sysemu/kvm.h" | ||||||
| #include "exec/address-spaces.h" | #include "exec/address-spaces.h" | ||||||
| 
 | 
 | ||||||
| #include "hw/s390-virtio-bus.h" | #include "hw/s390x/s390-virtio-bus.h" | ||||||
| #include "hw/s390x/sclp.h" | #include "hw/s390x/sclp.h" | ||||||
| #include "hw/s390-virtio.h" | #include "hw/s390x/s390-virtio.h" | ||||||
| 
 | 
 | ||||||
| //#define DEBUG_S390
 | //#define DEBUG_S390
 | ||||||
| 
 | 
 | ||||||
| @ -86,6 +86,9 @@ static int s390_virtio_hcall_reset(const uint64_t *args) | |||||||
|     VirtIOS390Device *dev; |     VirtIOS390Device *dev; | ||||||
| 
 | 
 | ||||||
|     dev = s390_virtio_bus_find_mem(s390_bus, mem); |     dev = s390_virtio_bus_find_mem(s390_bus, mem); | ||||||
|  |     if (dev == NULL) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|     virtio_reset(dev->vdev); |     virtio_reset(dev->vdev); | ||||||
|     stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); |     stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); | ||||||
|     s390_virtio_device_sync(dev); |     s390_virtio_device_sync(dev); | ||||||
| @ -147,13 +150,73 @@ unsigned s390_del_running_cpu(CPUS390XState *env) | |||||||
|     return s390_running_cpus; |     return s390_running_cpus; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void s390_init_ipl_dev(const char *kernel_filename, | ||||||
|  |                        const char *kernel_cmdline, | ||||||
|  |                        const char *initrd_filename) | ||||||
|  | { | ||||||
|  |     DeviceState *dev; | ||||||
|  | 
 | ||||||
|  |     dev  = qdev_create(NULL, "s390-ipl"); | ||||||
|  |     if (kernel_filename) { | ||||||
|  |         qdev_prop_set_string(dev, "kernel", kernel_filename); | ||||||
|  |     } | ||||||
|  |     if (initrd_filename) { | ||||||
|  |         qdev_prop_set_string(dev, "initrd", initrd_filename); | ||||||
|  |     } | ||||||
|  |     qdev_prop_set_string(dev, "cmdline", kernel_cmdline); | ||||||
|  |     qdev_init_nofail(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  | 
 | ||||||
|  |     if (cpu_model == NULL) { | ||||||
|  |         cpu_model = "host"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < smp_cpus; i++) { | ||||||
|  |         S390CPU *cpu; | ||||||
|  | 
 | ||||||
|  |         cpu = cpu_s390x_init(cpu_model); | ||||||
|  | 
 | ||||||
|  |         ipi_states[i] = cpu; | ||||||
|  |         cpu->env.halted = 1; | ||||||
|  |         cpu->env.exception_index = EXCP_HLT; | ||||||
|  |         cpu->env.storage_keys = storage_keys; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void s390_create_virtio_net(BusState *bus, const char *name) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < nb_nics; i++) { | ||||||
|  |         NICInfo *nd = &nd_table[i]; | ||||||
|  |         DeviceState *dev; | ||||||
|  | 
 | ||||||
|  |         if (!nd->model) { | ||||||
|  |             nd->model = g_strdup("virtio"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (strcmp(nd->model, "virtio")) { | ||||||
|  |             fprintf(stderr, "S390 only supports VirtIO nics\n"); | ||||||
|  |             exit(1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         dev = qdev_create(bus, name); | ||||||
|  |         qdev_set_nic_properties(dev, nd); | ||||||
|  |         qdev_init_nofail(dev); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* PC hardware initialisation */ | /* PC hardware initialisation */ | ||||||
| static void s390_init(QEMUMachineInitArgs *args) | static void s390_init(QEMUMachineInitArgs *args) | ||||||
| { | { | ||||||
|     ram_addr_t my_ram_size = args->ram_size; |     ram_addr_t my_ram_size = args->ram_size; | ||||||
|     const char *cpu_model = args->cpu_model; |  | ||||||
|     CPUS390XState *env = NULL; |  | ||||||
|     DeviceState *dev; |  | ||||||
|     MemoryRegion *sysmem = get_system_memory(); |     MemoryRegion *sysmem = get_system_memory(); | ||||||
|     MemoryRegion *ram = g_new(MemoryRegion, 1); |     MemoryRegion *ram = g_new(MemoryRegion, 1); | ||||||
|     int shift = 0; |     int shift = 0; | ||||||
| @ -161,7 +224,6 @@ static void s390_init(QEMUMachineInitArgs *args) | |||||||
|     void *virtio_region; |     void *virtio_region; | ||||||
|     hwaddr virtio_region_len; |     hwaddr virtio_region_len; | ||||||
|     hwaddr virtio_region_start; |     hwaddr virtio_region_start; | ||||||
|     int i; |  | ||||||
| 
 | 
 | ||||||
|     /* s390x ram size detection needs a 16bit multiplier + an increment. So
 |     /* s390x ram size detection needs a 16bit multiplier + an increment. So
 | ||||||
|        guests > 64GB can be specified in 2MB steps etc. */ |        guests > 64GB can be specified in 2MB steps etc. */ | ||||||
| @ -176,15 +238,8 @@ static void s390_init(QEMUMachineInitArgs *args) | |||||||
|     /* get a BUS */ |     /* get a BUS */ | ||||||
|     s390_bus = s390_virtio_bus_init(&my_ram_size); |     s390_bus = s390_virtio_bus_init(&my_ram_size); | ||||||
|     s390_sclp_init(); |     s390_sclp_init(); | ||||||
|     dev  = qdev_create(NULL, "s390-ipl"); |     s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, | ||||||
|     if (args->kernel_filename) { |                       args->initrd_filename); | ||||||
|         qdev_prop_set_string(dev, "kernel", args->kernel_filename); |  | ||||||
|     } |  | ||||||
|     if (args->initrd_filename) { |  | ||||||
|         qdev_prop_set_string(dev, "initrd", args->initrd_filename); |  | ||||||
|     } |  | ||||||
|     qdev_prop_set_string(dev, "cmdline", args->kernel_cmdline); |  | ||||||
|     qdev_init_nofail(dev); |  | ||||||
| 
 | 
 | ||||||
|     /* register hypercalls */ |     /* register hypercalls */ | ||||||
|     s390_virtio_register_hcalls(); |     s390_virtio_register_hcalls(); | ||||||
| @ -207,46 +262,10 @@ static void s390_init(QEMUMachineInitArgs *args) | |||||||
|     storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); |     storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); | ||||||
| 
 | 
 | ||||||
|     /* init CPUs */ |     /* init CPUs */ | ||||||
|     if (cpu_model == NULL) { |     s390_init_cpus(args->cpu_model, storage_keys); | ||||||
|         cpu_model = "host"; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); |  | ||||||
| 
 |  | ||||||
|     for (i = 0; i < smp_cpus; i++) { |  | ||||||
|         S390CPU *cpu; |  | ||||||
|         CPUS390XState *tmp_env; |  | ||||||
| 
 |  | ||||||
|         cpu = cpu_s390x_init(cpu_model); |  | ||||||
|         tmp_env = &cpu->env; |  | ||||||
|         if (!env) { |  | ||||||
|             env = tmp_env; |  | ||||||
|         } |  | ||||||
|         ipi_states[i] = cpu; |  | ||||||
|         tmp_env->halted = 1; |  | ||||||
|         tmp_env->exception_index = EXCP_HLT; |  | ||||||
|         tmp_env->storage_keys = storage_keys; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     /* Create VirtIO network adapters */ |     /* Create VirtIO network adapters */ | ||||||
|     for(i = 0; i < nb_nics; i++) { |     s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); | ||||||
|         NICInfo *nd = &nd_table[i]; |  | ||||||
|         DeviceState *dev; |  | ||||||
| 
 |  | ||||||
|         if (!nd->model) { |  | ||||||
|             nd->model = g_strdup("virtio"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (strcmp(nd->model, "virtio")) { |  | ||||||
|             fprintf(stderr, "S390 only supports VirtIO nics\n"); |  | ||||||
|             exit(1); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         dev = qdev_create((BusState *)s390_bus, "virtio-net-s390"); |  | ||||||
|         qdev_set_nic_properties(dev, nd); |  | ||||||
|         qdev_init_nofail(dev); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static QEMUMachine s390_machine = { | static QEMUMachine s390_machine = { | ||||||
| @ -15,8 +15,14 @@ | |||||||
| #define KVM_S390_VIRTIO_NOTIFY          0 | #define KVM_S390_VIRTIO_NOTIFY          0 | ||||||
| #define KVM_S390_VIRTIO_RESET           1 | #define KVM_S390_VIRTIO_RESET           1 | ||||||
| #define KVM_S390_VIRTIO_SET_STATUS      2 | #define KVM_S390_VIRTIO_SET_STATUS      2 | ||||||
|  | #define KVM_S390_VIRTIO_CCW_NOTIFY      3 | ||||||
| 
 | 
 | ||||||
| typedef int (*s390_virtio_fn)(const uint64_t *args); | typedef int (*s390_virtio_fn)(const uint64_t *args); | ||||||
| void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); | void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); | ||||||
| 
 | 
 | ||||||
|  | void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys); | ||||||
|  | void s390_init_ipl_dev(const char *kernel_filename, | ||||||
|  |                        const char *kernel_cmdline, | ||||||
|  |                        const char *initrd_filename); | ||||||
|  | void s390_create_virtio_net(BusState *bus, const char *name); | ||||||
| #endif | #endif | ||||||
							
								
								
									
										960
									
								
								hw/s390x/virtio-ccw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										960
									
								
								hw/s390x/virtio-ccw.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,960 @@ | |||||||
|  | /*
 | ||||||
|  |  * virtio ccw target implementation | ||||||
|  |  * | ||||||
|  |  * Copyright 2012 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.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/hw.h" | ||||||
|  | #include "block/block.h" | ||||||
|  | #include "sysemu/blockdev.h" | ||||||
|  | #include "sysemu/sysemu.h" | ||||||
|  | #include "net/net.h" | ||||||
|  | #include "monitor/monitor.h" | ||||||
|  | #include "hw/virtio.h" | ||||||
|  | #include "hw/virtio-serial.h" | ||||||
|  | #include "hw/virtio-net.h" | ||||||
|  | #include "hw/sysbus.h" | ||||||
|  | #include "qemu/bitops.h" | ||||||
|  | #include "hw/virtio-bus.h" | ||||||
|  | 
 | ||||||
|  | #include "ioinst.h" | ||||||
|  | #include "css.h" | ||||||
|  | #include "virtio-ccw.h" | ||||||
|  | #include "trace.h" | ||||||
|  | 
 | ||||||
|  | static int virtual_css_bus_reset(BusState *qbus) | ||||||
|  | { | ||||||
|  |     /* This should actually be modelled via the generic css */ | ||||||
|  |     css_reset(); | ||||||
|  | 
 | ||||||
|  |     /* we dont traverse ourself, return 0 */ | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void virtual_css_bus_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     BusClass *k = BUS_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->reset = virtual_css_bus_reset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtual_css_bus_info = { | ||||||
|  |     .name = TYPE_VIRTUAL_CSS_BUS, | ||||||
|  |     .parent = TYPE_BUS, | ||||||
|  |     .instance_size = sizeof(VirtualCssBus), | ||||||
|  |     .class_init = virtual_css_bus_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const VirtIOBindings virtio_ccw_bindings; | ||||||
|  | 
 | ||||||
|  | VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev = NULL; | ||||||
|  | 
 | ||||||
|  |     if (sch->driver_data) { | ||||||
|  |         vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev; | ||||||
|  |     } | ||||||
|  |     return vdev; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | VirtualCssBus *virtual_css_bus_init(void) | ||||||
|  | { | ||||||
|  |     VirtualCssBus *cbus; | ||||||
|  |     BusState *bus; | ||||||
|  |     DeviceState *dev; | ||||||
|  | 
 | ||||||
|  |     /* Create bridge device */ | ||||||
|  |     dev = qdev_create(NULL, "virtual-css-bridge"); | ||||||
|  |     qdev_init_nofail(dev); | ||||||
|  | 
 | ||||||
|  |     /* Create bus on bridge device */ | ||||||
|  |     bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); | ||||||
|  |     cbus = VIRTUAL_CSS_BUS(bus); | ||||||
|  | 
 | ||||||
|  |     /* Enable hotplugging */ | ||||||
|  |     bus->allow_hotplug = 1; | ||||||
|  | 
 | ||||||
|  |     return cbus; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Communication blocks used by several channel commands. */ | ||||||
|  | typedef struct VqInfoBlock { | ||||||
|  |     uint64_t queue; | ||||||
|  |     uint32_t align; | ||||||
|  |     uint16_t index; | ||||||
|  |     uint16_t num; | ||||||
|  | } QEMU_PACKED VqInfoBlock; | ||||||
|  | 
 | ||||||
|  | typedef struct VqConfigBlock { | ||||||
|  |     uint16_t index; | ||||||
|  |     uint16_t num_max; | ||||||
|  | } QEMU_PACKED VqConfigBlock; | ||||||
|  | 
 | ||||||
|  | typedef struct VirtioFeatDesc { | ||||||
|  |     uint32_t features; | ||||||
|  |     uint8_t index; | ||||||
|  | } QEMU_PACKED VirtioFeatDesc; | ||||||
|  | 
 | ||||||
|  | /* Specify where the virtqueues for the subchannel are in guest memory. */ | ||||||
|  | static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, | ||||||
|  |                               uint16_t index, uint16_t num) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *dev = sch->driver_data; | ||||||
|  | 
 | ||||||
|  |     if (index > VIRTIO_PCI_QUEUE_MAX) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Current code in virtio.c relies on 4K alignment. */ | ||||||
|  |     if (addr && (align != 4096)) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!dev) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtio_queue_set_addr(dev->vdev, index, addr); | ||||||
|  |     if (!addr) { | ||||||
|  |         virtio_queue_set_vector(dev->vdev, index, 0); | ||||||
|  |     } else { | ||||||
|  |         /* Fail if we don't have a big enough queue. */ | ||||||
|  |         /* TODO: Add interface to handle vring.num changing */ | ||||||
|  |         if (virtio_queue_get_num(dev->vdev, index) > num) { | ||||||
|  |             return -EINVAL; | ||||||
|  |         } | ||||||
|  |         virtio_queue_set_vector(dev->vdev, index, index); | ||||||
|  |     } | ||||||
|  |     /* tell notify handler in case of config change */ | ||||||
|  |     dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | ||||||
|  | { | ||||||
|  |     int ret; | ||||||
|  |     VqInfoBlock info; | ||||||
|  |     uint8_t status; | ||||||
|  |     VirtioFeatDesc features; | ||||||
|  |     void *config; | ||||||
|  |     hwaddr indicators; | ||||||
|  |     VqConfigBlock vq_config; | ||||||
|  |     VirtioCcwDevice *dev = sch->driver_data; | ||||||
|  |     bool check_len; | ||||||
|  |     int len; | ||||||
|  |     hwaddr hw_len; | ||||||
|  | 
 | ||||||
|  |     if (!dev) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, | ||||||
|  |                                    ccw.cmd_code); | ||||||
|  |     check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); | ||||||
|  | 
 | ||||||
|  |     /* Look at the command. */ | ||||||
|  |     switch (ccw.cmd_code) { | ||||||
|  |     case CCW_CMD_SET_VQ: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(info)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(info)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             info.queue = ldq_phys(ccw.cda); | ||||||
|  |             info.align = ldl_phys(ccw.cda + sizeof(info.queue)); | ||||||
|  |             info.index = lduw_phys(ccw.cda + sizeof(info.queue) | ||||||
|  |                                    + sizeof(info.align)); | ||||||
|  |             info.num = lduw_phys(ccw.cda + sizeof(info.queue) | ||||||
|  |                                  + sizeof(info.align) | ||||||
|  |                                  + sizeof(info.index)); | ||||||
|  |             ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, | ||||||
|  |                                      info.num); | ||||||
|  |             sch->curr_status.scsw.count = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_VDEV_RESET: | ||||||
|  |         virtio_reset(dev->vdev); | ||||||
|  |         ret = 0; | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_READ_FEAT: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(features)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(features)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             features.index = ldub_phys(ccw.cda + sizeof(features.features)); | ||||||
|  |             if (features.index < ARRAY_SIZE(dev->host_features)) { | ||||||
|  |                 features.features = dev->host_features[features.index]; | ||||||
|  |             } else { | ||||||
|  |                 /* Return zeroes if the guest supports more feature bits. */ | ||||||
|  |                 features.features = 0; | ||||||
|  |             } | ||||||
|  |             stl_le_phys(ccw.cda, features.features); | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - sizeof(features); | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_WRITE_FEAT: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(features)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(features)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             features.index = ldub_phys(ccw.cda + sizeof(features.features)); | ||||||
|  |             features.features = ldl_le_phys(ccw.cda); | ||||||
|  |             if (features.index < ARRAY_SIZE(dev->host_features)) { | ||||||
|  |                 if (dev->vdev->set_features) { | ||||||
|  |                     dev->vdev->set_features(dev->vdev, features.features); | ||||||
|  |                 } | ||||||
|  |                 dev->vdev->guest_features = features.features; | ||||||
|  |             } else { | ||||||
|  |                 /*
 | ||||||
|  |                  * If the guest supports more feature bits, assert that it | ||||||
|  |                  * passes us zeroes for those we don't support. | ||||||
|  |                  */ | ||||||
|  |                 if (features.features) { | ||||||
|  |                     fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n", | ||||||
|  |                             features.index, features.features); | ||||||
|  |                     /* XXX: do a unit check here? */ | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - sizeof(features); | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_READ_CONF: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count > dev->vdev->config_len) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         len = MIN(ccw.count, dev->vdev->config_len); | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             dev->vdev->get_config(dev->vdev, dev->vdev->config); | ||||||
|  |             /* XXX config space endianness */ | ||||||
|  |             cpu_physical_memory_write(ccw.cda, dev->vdev->config, len); | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - len; | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_WRITE_CONF: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count > dev->vdev->config_len) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         len = MIN(ccw.count, dev->vdev->config_len); | ||||||
|  |         hw_len = len; | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             config = cpu_physical_memory_map(ccw.cda, &hw_len, 0); | ||||||
|  |             if (!config) { | ||||||
|  |                 ret = -EFAULT; | ||||||
|  |             } else { | ||||||
|  |                 len = hw_len; | ||||||
|  |                 /* XXX config space endianness */ | ||||||
|  |                 memcpy(dev->vdev->config, config, len); | ||||||
|  |                 cpu_physical_memory_unmap(config, hw_len, 0, hw_len); | ||||||
|  |                 if (dev->vdev->set_config) { | ||||||
|  |                     dev->vdev->set_config(dev->vdev, dev->vdev->config); | ||||||
|  |                 } | ||||||
|  |                 sch->curr_status.scsw.count = ccw.count - len; | ||||||
|  |                 ret = 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_WRITE_STATUS: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(status)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(status)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             status = ldub_phys(ccw.cda); | ||||||
|  |             virtio_set_status(dev->vdev, status); | ||||||
|  |             if (dev->vdev->status == 0) { | ||||||
|  |                 virtio_reset(dev->vdev); | ||||||
|  |             } | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - sizeof(status); | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_SET_IND: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(indicators)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(indicators)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         indicators = ldq_phys(ccw.cda); | ||||||
|  |         if (!indicators) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             dev->indicators = indicators; | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - sizeof(indicators); | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_SET_CONF_IND: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(indicators)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(indicators)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         indicators = ldq_phys(ccw.cda); | ||||||
|  |         if (!indicators) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             dev->indicators2 = indicators; | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - sizeof(indicators); | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CCW_CMD_READ_VQ_CONF: | ||||||
|  |         if (check_len) { | ||||||
|  |             if (ccw.count != sizeof(vq_config)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if (ccw.count < sizeof(vq_config)) { | ||||||
|  |             /* Can't execute command. */ | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!ccw.cda) { | ||||||
|  |             ret = -EFAULT; | ||||||
|  |         } else { | ||||||
|  |             vq_config.index = lduw_phys(ccw.cda); | ||||||
|  |             vq_config.num_max = virtio_queue_get_num(dev->vdev, | ||||||
|  |                                                      vq_config.index); | ||||||
|  |             stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max); | ||||||
|  |             sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); | ||||||
|  |             ret = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ret = -ENOSYS; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) | ||||||
|  | { | ||||||
|  |     unsigned int cssid = 0; | ||||||
|  |     unsigned int ssid = 0; | ||||||
|  |     unsigned int schid; | ||||||
|  |     unsigned int devno; | ||||||
|  |     bool have_devno = false; | ||||||
|  |     bool found = false; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     int ret; | ||||||
|  |     int num; | ||||||
|  |     DeviceState *parent = DEVICE(dev); | ||||||
|  | 
 | ||||||
|  |     sch = g_malloc0(sizeof(SubchDev)); | ||||||
|  | 
 | ||||||
|  |     sch->driver_data = dev; | ||||||
|  |     dev->sch = sch; | ||||||
|  | 
 | ||||||
|  |     dev->vdev = vdev; | ||||||
|  |     dev->indicators = 0; | ||||||
|  | 
 | ||||||
|  |     /* Initialize subchannel structure. */ | ||||||
|  |     sch->channel_prog = 0x0; | ||||||
|  |     sch->last_cmd_valid = false; | ||||||
|  |     sch->orb = NULL; | ||||||
|  |     /*
 | ||||||
|  |      * Use a device number if provided. Otherwise, fall back to subchannel | ||||||
|  |      * number. | ||||||
|  |      */ | ||||||
|  |     if (dev->bus_id) { | ||||||
|  |         num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); | ||||||
|  |         if (num == 3) { | ||||||
|  |             if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 error_report("Invalid cssid or ssid: cssid %x, ssid %x", | ||||||
|  |                              cssid, ssid); | ||||||
|  |                 goto out_err; | ||||||
|  |             } | ||||||
|  |             /* Enforce use of virtual cssid. */ | ||||||
|  |             if (cssid != VIRTUAL_CSSID) { | ||||||
|  |                 ret = -EINVAL; | ||||||
|  |                 error_report("cssid %x not valid for virtio devices", cssid); | ||||||
|  |                 goto out_err; | ||||||
|  |             } | ||||||
|  |             if (css_devno_used(cssid, ssid, devno)) { | ||||||
|  |                 ret = -EEXIST; | ||||||
|  |                 error_report("Device %x.%x.%04x already exists", cssid, ssid, | ||||||
|  |                              devno); | ||||||
|  |                 goto out_err; | ||||||
|  |             } | ||||||
|  |             sch->cssid = cssid; | ||||||
|  |             sch->ssid = ssid; | ||||||
|  |             sch->devno = devno; | ||||||
|  |             have_devno = true; | ||||||
|  |         } else { | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             error_report("Malformed devno parameter '%s'", dev->bus_id); | ||||||
|  |             goto out_err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Find the next free id. */ | ||||||
|  |     if (have_devno) { | ||||||
|  |         for (schid = 0; schid <= MAX_SCHID; schid++) { | ||||||
|  |             if (!css_find_subch(1, cssid, ssid, schid)) { | ||||||
|  |                 sch->schid = schid; | ||||||
|  |                 css_subch_assign(cssid, ssid, schid, devno, sch); | ||||||
|  |                 found = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!found) { | ||||||
|  |             ret = -ENODEV; | ||||||
|  |             error_report("No free subchannel found for %x.%x.%04x", cssid, ssid, | ||||||
|  |                          devno); | ||||||
|  |             goto out_err; | ||||||
|  |         } | ||||||
|  |         trace_virtio_ccw_new_device(cssid, ssid, schid, devno, | ||||||
|  |                                     "user-configured"); | ||||||
|  |     } else { | ||||||
|  |         cssid = VIRTUAL_CSSID; | ||||||
|  |         for (ssid = 0; ssid <= MAX_SSID; ssid++) { | ||||||
|  |             for (schid = 0; schid <= MAX_SCHID; schid++) { | ||||||
|  |                 if (!css_find_subch(1, cssid, ssid, schid)) { | ||||||
|  |                     sch->cssid = cssid; | ||||||
|  |                     sch->ssid = ssid; | ||||||
|  |                     sch->schid = schid; | ||||||
|  |                     devno = schid; | ||||||
|  |                     /*
 | ||||||
|  |                      * If the devno is already taken, look further in this | ||||||
|  |                      * subchannel set. | ||||||
|  |                      */ | ||||||
|  |                     while (css_devno_used(cssid, ssid, devno)) { | ||||||
|  |                         if (devno == MAX_SCHID) { | ||||||
|  |                             devno = 0; | ||||||
|  |                         } else if (devno == schid - 1) { | ||||||
|  |                             ret = -ENODEV; | ||||||
|  |                             error_report("No free devno found"); | ||||||
|  |                             goto out_err; | ||||||
|  |                         } else { | ||||||
|  |                             devno++; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     sch->devno = devno; | ||||||
|  |                     css_subch_assign(cssid, ssid, schid, devno, sch); | ||||||
|  |                     found = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (found) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!found) { | ||||||
|  |             ret = -ENODEV; | ||||||
|  |             error_report("Virtual channel subsystem is full!"); | ||||||
|  |             goto out_err; | ||||||
|  |         } | ||||||
|  |         trace_virtio_ccw_new_device(cssid, ssid, schid, devno, | ||||||
|  |                                     "auto-configured"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Build initial schib. */ | ||||||
|  |     css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); | ||||||
|  | 
 | ||||||
|  |     sch->ccw_cb = virtio_ccw_cb; | ||||||
|  | 
 | ||||||
|  |     /* Build senseid data. */ | ||||||
|  |     memset(&sch->id, 0, sizeof(SenseId)); | ||||||
|  |     sch->id.reserved = 0xff; | ||||||
|  |     sch->id.cu_type = VIRTIO_CCW_CU_TYPE; | ||||||
|  |     sch->id.cu_model = dev->vdev->device_id; | ||||||
|  | 
 | ||||||
|  |     virtio_bind_device(vdev, &virtio_ccw_bindings, DEVICE(dev)); | ||||||
|  |     /* Only the first 32 feature bits are used. */ | ||||||
|  |     dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]); | ||||||
|  |     dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; | ||||||
|  |     dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; | ||||||
|  | 
 | ||||||
|  |     css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, | ||||||
|  |                           parent->hotplugged, 1); | ||||||
|  |     return 0; | ||||||
|  | 
 | ||||||
|  | out_err: | ||||||
|  |     dev->sch = NULL; | ||||||
|  |     g_free(sch); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_exit(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     SubchDev *sch = dev->sch; | ||||||
|  | 
 | ||||||
|  |     if (sch) { | ||||||
|  |         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); | ||||||
|  |         g_free(sch); | ||||||
|  |     } | ||||||
|  |     dev->indicators = 0; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_net_init(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev; | ||||||
|  | 
 | ||||||
|  |     vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net); | ||||||
|  |     if (!vdev) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return virtio_ccw_device_init(dev, vdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_net_exit(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     virtio_net_exit(dev->vdev); | ||||||
|  |     return virtio_ccw_exit(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_blk_init(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev; | ||||||
|  | 
 | ||||||
|  |     vdev = virtio_blk_init((DeviceState *)dev, &dev->blk); | ||||||
|  |     if (!vdev) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return virtio_ccw_device_init(dev, vdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_blk_exit(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     virtio_blk_exit(dev->vdev); | ||||||
|  |     blockdev_mark_auto_del(dev->blk.conf.bs); | ||||||
|  |     return virtio_ccw_exit(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_serial_init(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev; | ||||||
|  | 
 | ||||||
|  |     vdev = virtio_serial_init((DeviceState *)dev, &dev->serial); | ||||||
|  |     if (!vdev) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return virtio_ccw_device_init(dev, vdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_serial_exit(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     virtio_serial_exit(dev->vdev); | ||||||
|  |     return virtio_ccw_exit(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_balloon_init(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev; | ||||||
|  | 
 | ||||||
|  |     vdev = virtio_balloon_init((DeviceState *)dev); | ||||||
|  |     if (!vdev) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return virtio_ccw_device_init(dev, vdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_balloon_exit(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     virtio_balloon_exit(dev->vdev); | ||||||
|  |     return virtio_ccw_exit(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_scsi_init(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev; | ||||||
|  | 
 | ||||||
|  |     vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi); | ||||||
|  |     if (!vdev) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return virtio_ccw_device_init(dev, vdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_scsi_exit(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     virtio_scsi_exit(dev->vdev); | ||||||
|  |     return virtio_ccw_exit(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* DeviceState to VirtioCcwDevice. Note: used on datapath,
 | ||||||
|  |  * be careful and test performance if you change this. | ||||||
|  |  */ | ||||||
|  | static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) | ||||||
|  | { | ||||||
|  |     return container_of(d, VirtioCcwDevice, parent_obj); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_notify(DeviceState *d, uint16_t vector) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); | ||||||
|  |     SubchDev *sch = dev->sch; | ||||||
|  |     uint64_t indicators; | ||||||
|  | 
 | ||||||
|  |     if (vector >= 128) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (vector < VIRTIO_PCI_QUEUE_MAX) { | ||||||
|  |         indicators = ldq_phys(dev->indicators); | ||||||
|  |         indicators |= 1ULL << vector; | ||||||
|  |         stq_phys(dev->indicators, indicators); | ||||||
|  |     } else { | ||||||
|  |         vector = 0; | ||||||
|  |         indicators = ldq_phys(dev->indicators2); | ||||||
|  |         indicators |= 1ULL << vector; | ||||||
|  |         stq_phys(dev->indicators2, indicators); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     css_conditional_io_interrupt(sch); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static unsigned virtio_ccw_get_features(DeviceState *d) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); | ||||||
|  | 
 | ||||||
|  |     /* Only the first 32 feature bits are used. */ | ||||||
|  |     return dev->host_features[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_reset(DeviceState *d) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); | ||||||
|  | 
 | ||||||
|  |     virtio_reset(dev->vdev); | ||||||
|  |     css_reset_sch(dev->sch); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**************** Virtio-ccw Bus Device Descriptions *******************/ | ||||||
|  | 
 | ||||||
|  | static const VirtIOBindings virtio_ccw_bindings = { | ||||||
|  |     .notify = virtio_ccw_notify, | ||||||
|  |     .get_features = virtio_ccw_get_features, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Property virtio_ccw_net_properties[] = { | ||||||
|  |     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), | ||||||
|  |     DEFINE_VIRTIO_NET_FEATURES(VirtioCcwDevice, host_features[0]), | ||||||
|  |     DEFINE_NIC_PROPERTIES(VirtioCcwDevice, nic), | ||||||
|  |     DEFINE_PROP_UINT32("x-txtimer", VirtioCcwDevice, | ||||||
|  |                        net.txtimer, TX_TIMER_INTERVAL), | ||||||
|  |     DEFINE_PROP_INT32("x-txburst", VirtioCcwDevice, | ||||||
|  |                       net.txburst, TX_BURST), | ||||||
|  |     DEFINE_PROP_STRING("tx", VirtioCcwDevice, net.tx), | ||||||
|  |     DEFINE_PROP_END_OF_LIST(), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  |     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->init = virtio_ccw_net_init; | ||||||
|  |     k->exit = virtio_ccw_net_exit; | ||||||
|  |     dc->reset = virtio_ccw_reset; | ||||||
|  |     dc->props = virtio_ccw_net_properties; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_net = { | ||||||
|  |     .name          = "virtio-net-ccw", | ||||||
|  |     .parent        = TYPE_VIRTIO_CCW_DEVICE, | ||||||
|  |     .instance_size = sizeof(VirtioCcwDevice), | ||||||
|  |     .class_init    = virtio_ccw_net_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Property virtio_ccw_blk_properties[] = { | ||||||
|  |     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), | ||||||
|  |     DEFINE_BLOCK_PROPERTIES(VirtioCcwDevice, blk.conf), | ||||||
|  |     DEFINE_PROP_STRING("serial", VirtioCcwDevice, blk.serial), | ||||||
|  | #ifdef __linux__ | ||||||
|  |     DEFINE_PROP_BIT("scsi", VirtioCcwDevice, blk.scsi, 0, true), | ||||||
|  | #endif | ||||||
|  |     DEFINE_VIRTIO_BLK_FEATURES(VirtioCcwDevice, host_features[0]), | ||||||
|  |     DEFINE_PROP_END_OF_LIST(), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  |     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->init = virtio_ccw_blk_init; | ||||||
|  |     k->exit = virtio_ccw_blk_exit; | ||||||
|  |     dc->reset = virtio_ccw_reset; | ||||||
|  |     dc->props = virtio_ccw_blk_properties; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_blk = { | ||||||
|  |     .name          = "virtio-blk-ccw", | ||||||
|  |     .parent        = TYPE_VIRTIO_CCW_DEVICE, | ||||||
|  |     .instance_size = sizeof(VirtioCcwDevice), | ||||||
|  |     .class_init    = virtio_ccw_blk_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Property virtio_ccw_serial_properties[] = { | ||||||
|  |     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), | ||||||
|  |     DEFINE_PROP_UINT32("max_ports", VirtioCcwDevice, | ||||||
|  |                        serial.max_virtserial_ports, 31), | ||||||
|  |     DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), | ||||||
|  |     DEFINE_PROP_END_OF_LIST(), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  |     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->init = virtio_ccw_serial_init; | ||||||
|  |     k->exit = virtio_ccw_serial_exit; | ||||||
|  |     dc->reset = virtio_ccw_reset; | ||||||
|  |     dc->props = virtio_ccw_serial_properties; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_serial = { | ||||||
|  |     .name          = "virtio-serial-ccw", | ||||||
|  |     .parent        = TYPE_VIRTIO_CCW_DEVICE, | ||||||
|  |     .instance_size = sizeof(VirtioCcwDevice), | ||||||
|  |     .class_init    = virtio_ccw_serial_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Property virtio_ccw_balloon_properties[] = { | ||||||
|  |     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), | ||||||
|  |     DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), | ||||||
|  |     DEFINE_PROP_END_OF_LIST(), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  |     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->init = virtio_ccw_balloon_init; | ||||||
|  |     k->exit = virtio_ccw_balloon_exit; | ||||||
|  |     dc->reset = virtio_ccw_reset; | ||||||
|  |     dc->props = virtio_ccw_balloon_properties; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_balloon = { | ||||||
|  |     .name          = "virtio-balloon-ccw", | ||||||
|  |     .parent        = TYPE_VIRTIO_CCW_DEVICE, | ||||||
|  |     .instance_size = sizeof(VirtioCcwDevice), | ||||||
|  |     .class_init    = virtio_ccw_balloon_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Property virtio_ccw_scsi_properties[] = { | ||||||
|  |     DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), | ||||||
|  |     DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwDevice, host_features[0], scsi), | ||||||
|  |     DEFINE_PROP_END_OF_LIST(), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  |     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->init = virtio_ccw_scsi_init; | ||||||
|  |     k->exit = virtio_ccw_scsi_exit; | ||||||
|  |     dc->reset = virtio_ccw_reset; | ||||||
|  |     dc->props = virtio_ccw_scsi_properties; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_scsi = { | ||||||
|  |     .name          = "virtio-scsi-ccw", | ||||||
|  |     .parent        = TYPE_VIRTIO_CCW_DEVICE, | ||||||
|  |     .instance_size = sizeof(VirtioCcwDevice), | ||||||
|  |     .class_init    = virtio_ccw_scsi_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_busdev_init(DeviceState *dev) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; | ||||||
|  |     VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); | ||||||
|  | 
 | ||||||
|  |     virtio_ccw_bus_new(&_dev->bus, _dev); | ||||||
|  | 
 | ||||||
|  |     return _info->init(_dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_busdev_exit(DeviceState *dev) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; | ||||||
|  |     VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); | ||||||
|  | 
 | ||||||
|  |     return _info->exit(_dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int virtio_ccw_busdev_unplug(DeviceState *dev) | ||||||
|  | { | ||||||
|  |     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; | ||||||
|  |     SubchDev *sch = _dev->sch; | ||||||
|  | 
 | ||||||
|  |     /*
 | ||||||
|  |      * We should arrive here only for device_del, since we don't support | ||||||
|  |      * direct hot(un)plug of channels, but only through virtio. | ||||||
|  |      */ | ||||||
|  |     assert(sch != NULL); | ||||||
|  |     /* Subchannel is now disabled and no longer valid. */ | ||||||
|  |     sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | | ||||||
|  |                                      PMCW_FLAGS_MASK_DNV); | ||||||
|  | 
 | ||||||
|  |     css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); | ||||||
|  | 
 | ||||||
|  |     object_unparent(OBJECT(dev)); | ||||||
|  |     qdev_free(dev); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     dc->init = virtio_ccw_busdev_init; | ||||||
|  |     dc->exit = virtio_ccw_busdev_exit; | ||||||
|  |     dc->unplug = virtio_ccw_busdev_unplug; | ||||||
|  |     dc->bus_type = TYPE_VIRTUAL_CSS_BUS; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_device_info = { | ||||||
|  |     .name = TYPE_VIRTIO_CCW_DEVICE, | ||||||
|  |     .parent = TYPE_DEVICE, | ||||||
|  |     .instance_size = sizeof(VirtioCcwDevice), | ||||||
|  |     .class_init = virtio_ccw_device_class_init, | ||||||
|  |     .class_size = sizeof(VirtIOCCWDeviceClass), | ||||||
|  |     .abstract = true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /***************** Virtual-css Bus Bridge Device ********************/ | ||||||
|  | /* Only required to have the virtio bus as child in the system bus */ | ||||||
|  | 
 | ||||||
|  | static int virtual_css_bridge_init(SysBusDevice *dev) | ||||||
|  | { | ||||||
|  |     /* nothing */ | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
|  |     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     k->init = virtual_css_bridge_init; | ||||||
|  |     dc->no_user = 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtual_css_bridge_info = { | ||||||
|  |     .name          = "virtual-css-bridge", | ||||||
|  |     .parent        = TYPE_SYS_BUS_DEVICE, | ||||||
|  |     .instance_size = sizeof(SysBusDevice), | ||||||
|  |     .class_init    = virtual_css_bridge_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* virtio-ccw-bus */ | ||||||
|  | 
 | ||||||
|  | void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     DeviceState *qdev = DEVICE(dev); | ||||||
|  |     BusState *qbus; | ||||||
|  | 
 | ||||||
|  |     qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_CCW_BUS, qdev, NULL); | ||||||
|  |     qbus = BUS(bus); | ||||||
|  |     qbus->allow_hotplug = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) | ||||||
|  | { | ||||||
|  |     VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); | ||||||
|  |     BusClass *bus_class = BUS_CLASS(klass); | ||||||
|  | 
 | ||||||
|  |     bus_class->max_dev = 1; | ||||||
|  |     k->notify = virtio_ccw_notify; | ||||||
|  |     k->get_features = virtio_ccw_get_features; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const TypeInfo virtio_ccw_bus_info = { | ||||||
|  |     .name = TYPE_VIRTIO_CCW_BUS, | ||||||
|  |     .parent = TYPE_VIRTIO_BUS, | ||||||
|  |     .instance_size = sizeof(VirtioCcwBusState), | ||||||
|  |     .class_init = virtio_ccw_bus_class_init, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void virtio_ccw_register(void) | ||||||
|  | { | ||||||
|  |     type_register_static(&virtio_ccw_bus_info); | ||||||
|  |     type_register_static(&virtual_css_bus_info); | ||||||
|  |     type_register_static(&virtio_ccw_device_info); | ||||||
|  |     type_register_static(&virtio_ccw_serial); | ||||||
|  |     type_register_static(&virtio_ccw_blk); | ||||||
|  |     type_register_static(&virtio_ccw_net); | ||||||
|  |     type_register_static(&virtio_ccw_balloon); | ||||||
|  |     type_register_static(&virtio_ccw_scsi); | ||||||
|  |     type_register_static(&virtual_css_bridge_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type_init(virtio_ccw_register) | ||||||
							
								
								
									
										98
									
								
								hw/s390x/virtio-ccw.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								hw/s390x/virtio-ccw.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | /*
 | ||||||
|  |  * virtio ccw target definitions | ||||||
|  |  * | ||||||
|  |  * Copyright 2012 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  |  * directory. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef HW_S390X_VIRTIO_CCW_H | ||||||
|  | #define HW_S390X_VIRTIO_CCW_H | ||||||
|  | 
 | ||||||
|  | #include <hw/virtio-blk.h> | ||||||
|  | #include <hw/virtio-net.h> | ||||||
|  | #include <hw/virtio-serial.h> | ||||||
|  | #include <hw/virtio-scsi.h> | ||||||
|  | #include <hw/virtio-bus.h> | ||||||
|  | 
 | ||||||
|  | #define VIRTUAL_CSSID 0xfe | ||||||
|  | 
 | ||||||
|  | #define VIRTIO_CCW_CU_TYPE 0x3832 | ||||||
|  | #define VIRTIO_CCW_CHPID_TYPE 0x32 | ||||||
|  | 
 | ||||||
|  | #define CCW_CMD_SET_VQ       0x13 | ||||||
|  | #define CCW_CMD_VDEV_RESET   0x33 | ||||||
|  | #define CCW_CMD_READ_FEAT    0x12 | ||||||
|  | #define CCW_CMD_WRITE_FEAT   0x11 | ||||||
|  | #define CCW_CMD_READ_CONF    0x22 | ||||||
|  | #define CCW_CMD_WRITE_CONF   0x21 | ||||||
|  | #define CCW_CMD_WRITE_STATUS 0x31 | ||||||
|  | #define CCW_CMD_SET_IND      0x43 | ||||||
|  | #define CCW_CMD_SET_CONF_IND 0x53 | ||||||
|  | #define CCW_CMD_READ_VQ_CONF 0x32 | ||||||
|  | 
 | ||||||
|  | #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" | ||||||
|  | #define VIRTIO_CCW_DEVICE(obj) \ | ||||||
|  |      OBJECT_CHECK(VirtioCcwDevice, (obj), TYPE_VIRTIO_CCW_DEVICE) | ||||||
|  | #define VIRTIO_CCW_DEVICE_CLASS(klass) \ | ||||||
|  |      OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE) | ||||||
|  | #define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \ | ||||||
|  |      OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE) | ||||||
|  | 
 | ||||||
|  | typedef struct VirtioBusState VirtioCcwBusState; | ||||||
|  | typedef struct VirtioBusClass VirtioCcwBusClass; | ||||||
|  | 
 | ||||||
|  | #define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus" | ||||||
|  | #define VIRTIO_CCW_BUS(obj) \ | ||||||
|  |      OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS) | ||||||
|  | #define VIRTIO_CCW_BUS_GET_CLASS(obj) \ | ||||||
|  |     OBJECT_CHECK(VirtioCcwBusState, (obj), TYPE_VIRTIO_CCW_BUS) | ||||||
|  | #define VIRTIO_CCW_BUS_CLASS(klass) \ | ||||||
|  |     OBJECT_CLASS_CHECK(VirtioCcwBusClass, klass, TYPE_VIRTIO_CCW_BUS) | ||||||
|  | 
 | ||||||
|  | typedef struct VirtioCcwDevice VirtioCcwDevice; | ||||||
|  | 
 | ||||||
|  | void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev); | ||||||
|  | 
 | ||||||
|  | typedef struct VirtIOCCWDeviceClass { | ||||||
|  |     DeviceClass parent_class; | ||||||
|  |     int (*init)(VirtioCcwDevice *dev); | ||||||
|  |     int (*exit)(VirtioCcwDevice *dev); | ||||||
|  | } VirtIOCCWDeviceClass; | ||||||
|  | 
 | ||||||
|  | /* Change here if we want to support more feature bits. */ | ||||||
|  | #define VIRTIO_CCW_FEATURE_SIZE 1 | ||||||
|  | 
 | ||||||
|  | struct VirtioCcwDevice { | ||||||
|  |     DeviceState parent_obj; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     VirtIODevice *vdev; | ||||||
|  |     char *bus_id; | ||||||
|  |     VirtIOBlkConf blk; | ||||||
|  |     NICConf nic; | ||||||
|  |     uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; | ||||||
|  |     virtio_serial_conf serial; | ||||||
|  |     virtio_net_conf net; | ||||||
|  |     VirtIOSCSIConf scsi; | ||||||
|  |     VirtioBusState bus; | ||||||
|  |     /* Guest provided values: */ | ||||||
|  |     hwaddr indicators; | ||||||
|  |     hwaddr indicators2; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* virtual css bus type */ | ||||||
|  | typedef struct VirtualCssBus { | ||||||
|  |     BusState parent_obj; | ||||||
|  | } VirtualCssBus; | ||||||
|  | 
 | ||||||
|  | #define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" | ||||||
|  | #define VIRTUAL_CSS_BUS(obj) \ | ||||||
|  |      OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) | ||||||
|  | 
 | ||||||
|  | VirtualCssBus *virtual_css_bus_init(void); | ||||||
|  | void virtio_ccw_device_update_status(SubchDev *sch); | ||||||
|  | VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); | ||||||
|  | #endif | ||||||
| @ -1,4 +1,4 @@ | |||||||
| obj-y += translate.o helper.o cpu.o interrupt.o | obj-y += translate.o helper.o cpu.o interrupt.o | ||||||
| obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o | obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o | ||||||
| obj-$(CONFIG_SOFTMMU) += machine.o | obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o | ||||||
| obj-$(CONFIG_KVM) += kvm.o | obj-$(CONFIG_KVM) += kvm.o | ||||||
|  | |||||||
| @ -50,6 +50,11 @@ | |||||||
| #define MMU_USER_IDX 1 | #define MMU_USER_IDX 1 | ||||||
| 
 | 
 | ||||||
| #define MAX_EXT_QUEUE 16 | #define MAX_EXT_QUEUE 16 | ||||||
|  | #define MAX_IO_QUEUE 16 | ||||||
|  | #define MAX_MCHK_QUEUE 16 | ||||||
|  | 
 | ||||||
|  | #define PSW_MCHK_MASK 0x0004000000000000 | ||||||
|  | #define PSW_IO_MASK 0x0200000000000000 | ||||||
| 
 | 
 | ||||||
| typedef struct PSW { | typedef struct PSW { | ||||||
|     uint64_t mask; |     uint64_t mask; | ||||||
| @ -62,6 +67,17 @@ typedef struct ExtQueue { | |||||||
|     uint32_t param64; |     uint32_t param64; | ||||||
| } ExtQueue; | } ExtQueue; | ||||||
| 
 | 
 | ||||||
|  | typedef struct IOIntQueue { | ||||||
|  |     uint16_t id; | ||||||
|  |     uint16_t nr; | ||||||
|  |     uint32_t parm; | ||||||
|  |     uint32_t word; | ||||||
|  | } IOIntQueue; | ||||||
|  | 
 | ||||||
|  | typedef struct MchkQueue { | ||||||
|  |     uint16_t type; | ||||||
|  | } MchkQueue; | ||||||
|  | 
 | ||||||
| typedef struct CPUS390XState { | typedef struct CPUS390XState { | ||||||
|     uint64_t regs[16];     /* GP registers */ |     uint64_t regs[16];     /* GP registers */ | ||||||
|     CPU_DoubleU fregs[16]; /* FP registers */ |     CPU_DoubleU fregs[16]; /* FP registers */ | ||||||
| @ -93,9 +109,17 @@ typedef struct CPUS390XState { | |||||||
|     uint64_t cregs[16]; /* control registers */ |     uint64_t cregs[16]; /* control registers */ | ||||||
| 
 | 
 | ||||||
|     ExtQueue ext_queue[MAX_EXT_QUEUE]; |     ExtQueue ext_queue[MAX_EXT_QUEUE]; | ||||||
|     int pending_int; |     IOIntQueue io_queue[MAX_IO_QUEUE][8]; | ||||||
|  |     MchkQueue mchk_queue[MAX_MCHK_QUEUE]; | ||||||
| 
 | 
 | ||||||
|  |     int pending_int; | ||||||
|     int ext_index; |     int ext_index; | ||||||
|  |     int io_index[8]; | ||||||
|  |     int mchk_index; | ||||||
|  | 
 | ||||||
|  |     uint64_t ckc; | ||||||
|  |     uint64_t cputm; | ||||||
|  |     uint32_t todpr; | ||||||
| 
 | 
 | ||||||
|     CPU_COMMON |     CPU_COMMON | ||||||
| 
 | 
 | ||||||
| @ -123,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp) | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* distinguish between 24 bit and 31 bit addressing */ | ||||||
|  | #define HIGH_ORDER_BIT 0x80000000 | ||||||
|  | 
 | ||||||
| /* Interrupt Codes */ | /* Interrupt Codes */ | ||||||
| /* Program Interrupts */ | /* Program Interrupts */ | ||||||
| #define PGM_OPERATION                   0x0001 | #define PGM_OPERATION                   0x0001 | ||||||
| @ -300,8 +327,27 @@ int cpu_s390x_handle_mmu_fault (CPUS390XState *env, target_ulong address, int rw | |||||||
|                                 int mmu_idx); |                                 int mmu_idx); | ||||||
| #define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault | #define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault | ||||||
| 
 | 
 | ||||||
|  | #include "ioinst.h" | ||||||
| 
 | 
 | ||||||
| #ifndef CONFIG_USER_ONLY | #ifndef CONFIG_USER_ONLY | ||||||
|  | void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, | ||||||
|  |                                    int is_write); | ||||||
|  | void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, | ||||||
|  |                                     int is_write); | ||||||
|  | static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     hwaddr addr = 0; | ||||||
|  |     uint8_t reg; | ||||||
|  | 
 | ||||||
|  |     reg = ipb >> 28; | ||||||
|  |     if (reg > 0) { | ||||||
|  |         addr = env->regs[reg]; | ||||||
|  |     } | ||||||
|  |     addr += (ipb >> 16) & 0xfff; | ||||||
|  | 
 | ||||||
|  |     return addr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void s390x_tod_timer(void *opaque); | void s390x_tod_timer(void *opaque); | ||||||
| void s390x_cpu_timer(void *opaque); | void s390x_cpu_timer(void *opaque); | ||||||
| 
 | 
 | ||||||
| @ -351,6 +397,114 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env) | |||||||
| void cpu_lock(void); | void cpu_lock(void); | ||||||
| void cpu_unlock(void); | void cpu_unlock(void); | ||||||
| 
 | 
 | ||||||
|  | typedef struct SubchDev SubchDev; | ||||||
|  | 
 | ||||||
|  | #ifndef CONFIG_USER_ONLY | ||||||
|  | SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, | ||||||
|  |                          uint16_t schid); | ||||||
|  | bool css_subch_visible(SubchDev *sch); | ||||||
|  | void css_conditional_io_interrupt(SubchDev *sch); | ||||||
|  | int css_do_stsch(SubchDev *sch, SCHIB *schib); | ||||||
|  | bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid); | ||||||
|  | int css_do_msch(SubchDev *sch, SCHIB *schib); | ||||||
|  | int css_do_xsch(SubchDev *sch); | ||||||
|  | int css_do_csch(SubchDev *sch); | ||||||
|  | int css_do_hsch(SubchDev *sch); | ||||||
|  | int css_do_ssch(SubchDev *sch, ORB *orb); | ||||||
|  | int css_do_tsch(SubchDev *sch, IRB *irb); | ||||||
|  | int css_do_stcrw(CRW *crw); | ||||||
|  | int css_do_tpi(IOIntCode *int_code, int lowcore); | ||||||
|  | int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, | ||||||
|  |                          int rfmt, void *buf); | ||||||
|  | void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo); | ||||||
|  | int css_enable_mcsse(void); | ||||||
|  | int css_enable_mss(void); | ||||||
|  | int css_do_rsch(SubchDev *sch); | ||||||
|  | int css_do_rchp(uint8_t cssid, uint8_t chpid); | ||||||
|  | bool css_present(uint8_t cssid); | ||||||
|  | #else | ||||||
|  | static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, | ||||||
|  |                                        uint16_t schid) | ||||||
|  | { | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  | static inline bool css_subch_visible(SubchDev *sch) | ||||||
|  | { | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | static inline void css_conditional_io_interrupt(SubchDev *sch) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline int css_do_stsch(SubchDev *sch, SCHIB *schib) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid) | ||||||
|  | { | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | static inline int css_do_msch(SubchDev *sch, SCHIB *schib) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_xsch(SubchDev *sch) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_csch(SubchDev *sch) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_hsch(SubchDev *sch) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_ssch(SubchDev *sch, ORB *orb) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_tsch(SubchDev *sch, IRB *irb) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_stcrw(CRW *crw) | ||||||
|  | { | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | static inline int css_do_tpi(IOIntCode *int_code, int lowcore) | ||||||
|  | { | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, | ||||||
|  |                                        int rfmt, uint8_t l_chpid, void *buf) | ||||||
|  | { | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline int css_enable_mss(void) | ||||||
|  | { | ||||||
|  |     return -EINVAL; | ||||||
|  | } | ||||||
|  | static inline int css_enable_mcsse(void) | ||||||
|  | { | ||||||
|  |     return -EINVAL; | ||||||
|  | } | ||||||
|  | static inline int css_do_rsch(SubchDev *sch) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline int css_do_rchp(uint8_t cssid, uint8_t chpid) | ||||||
|  | { | ||||||
|  |     return -ENODEV; | ||||||
|  | } | ||||||
|  | static inline bool css_present(uint8_t cssid) | ||||||
|  | { | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) | static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) | ||||||
| { | { | ||||||
|     env->aregs[0] = newtls >> 32; |     env->aregs[0] = newtls >> 32; | ||||||
| @ -370,10 +524,14 @@ void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf); | |||||||
| #define EXCP_EXT 1 /* external interrupt */ | #define EXCP_EXT 1 /* external interrupt */ | ||||||
| #define EXCP_SVC 2 /* supervisor call (syscall) */ | #define EXCP_SVC 2 /* supervisor call (syscall) */ | ||||||
| #define EXCP_PGM 3 /* program interruption */ | #define EXCP_PGM 3 /* program interruption */ | ||||||
|  | #define EXCP_IO  7 /* I/O interrupt */ | ||||||
|  | #define EXCP_MCHK 8 /* machine check */ | ||||||
| 
 | 
 | ||||||
| #define INTERRUPT_EXT        (1 << 0) | #define INTERRUPT_EXT        (1 << 0) | ||||||
| #define INTERRUPT_TOD        (1 << 1) | #define INTERRUPT_TOD        (1 << 1) | ||||||
| #define INTERRUPT_CPUTIMER   (1 << 2) | #define INTERRUPT_CPUTIMER   (1 << 2) | ||||||
|  | #define INTERRUPT_IO         (1 << 3) | ||||||
|  | #define INTERRUPT_MCHK       (1 << 4) | ||||||
| 
 | 
 | ||||||
| /* Program Status Word.  */ | /* Program Status Word.  */ | ||||||
| #define S390_PSWM_REGNUM 0 | #define S390_PSWM_REGNUM 0 | ||||||
| @ -836,6 +994,45 @@ static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t pa | |||||||
|     cpu_interrupt(env, CPU_INTERRUPT_HARD); |     cpu_interrupt(env, CPU_INTERRUPT_HARD); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline void cpu_inject_io(CPUS390XState *env, uint16_t subchannel_id, | ||||||
|  |                                  uint16_t subchannel_number, | ||||||
|  |                                  uint32_t io_int_parm, uint32_t io_int_word) | ||||||
|  | { | ||||||
|  |     int isc = ffs(io_int_word << 2) - 1; | ||||||
|  | 
 | ||||||
|  |     if (env->io_index[isc] == MAX_IO_QUEUE - 1) { | ||||||
|  |         /* ugh - can't queue anymore. Let's drop. */ | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     env->io_index[isc]++; | ||||||
|  |     assert(env->io_index[isc] < MAX_IO_QUEUE); | ||||||
|  | 
 | ||||||
|  |     env->io_queue[env->io_index[isc]][isc].id = subchannel_id; | ||||||
|  |     env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; | ||||||
|  |     env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; | ||||||
|  |     env->io_queue[env->io_index[isc]][isc].word = io_int_word; | ||||||
|  | 
 | ||||||
|  |     env->pending_int |= INTERRUPT_IO; | ||||||
|  |     cpu_interrupt(env, CPU_INTERRUPT_HARD); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void cpu_inject_crw_mchk(CPUS390XState *env) | ||||||
|  | { | ||||||
|  |     if (env->mchk_index == MAX_MCHK_QUEUE - 1) { | ||||||
|  |         /* ugh - can't queue anymore. Let's drop. */ | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     env->mchk_index++; | ||||||
|  |     assert(env->mchk_index < MAX_MCHK_QUEUE); | ||||||
|  | 
 | ||||||
|  |     env->mchk_queue[env->mchk_index].type = 1; | ||||||
|  | 
 | ||||||
|  |     env->pending_int |= INTERRUPT_MCHK; | ||||||
|  |     cpu_interrupt(env, CPU_INTERRUPT_HARD); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline bool cpu_has_work(CPUState *cpu) | static inline bool cpu_has_work(CPUState *cpu) | ||||||
| { | { | ||||||
|     CPUS390XState *env = &S390_CPU(cpu)->env; |     CPUS390XState *env = &S390_CPU(cpu)->env; | ||||||
| @ -859,4 +1056,52 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen); | |||||||
| void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, | void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, | ||||||
|                                      uintptr_t retaddr); |                                      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, | ||||||
|  |                            uint32_t io_int_word); | ||||||
|  | void kvm_s390_crw_mchk(S390CPU *cpu); | ||||||
|  | void kvm_s390_enable_css_support(S390CPU *cpu); | ||||||
|  | #else | ||||||
|  | static inline void kvm_s390_io_interrupt(S390CPU *cpu, | ||||||
|  |                                         uint16_t subchannel_id, | ||||||
|  |                                         uint16_t subchannel_nr, | ||||||
|  |                                         uint32_t io_int_parm, | ||||||
|  |                                         uint32_t io_int_word) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void kvm_s390_crw_mchk(S390CPU *cpu) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | static inline void kvm_s390_enable_css_support(S390CPU *cpu) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static inline void s390_io_interrupt(S390CPU *cpu, | ||||||
|  |                                      uint16_t subchannel_id, | ||||||
|  |                                      uint16_t subchannel_nr, | ||||||
|  |                                      uint32_t io_int_parm, | ||||||
|  |                                      uint32_t io_int_word) | ||||||
|  | { | ||||||
|  |     if (kvm_enabled()) { | ||||||
|  |         kvm_s390_io_interrupt(cpu, subchannel_id, subchannel_nr, io_int_parm, | ||||||
|  |                               io_int_word); | ||||||
|  |     } else { | ||||||
|  |         cpu_inject_io(&cpu->env, subchannel_id, subchannel_nr, io_int_parm, | ||||||
|  |                       io_int_word); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void s390_crw_mchk(S390CPU *cpu) | ||||||
|  | { | ||||||
|  |     if (kvm_enabled()) { | ||||||
|  |         kvm_s390_crw_mchk(cpu); | ||||||
|  |     } else { | ||||||
|  |         cpu_inject_crw_mchk(&cpu->env); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -471,13 +471,56 @@ static uint64_t get_psw_mask(CPUS390XState *env) | |||||||
|     return r; |     return r; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static LowCore *cpu_map_lowcore(CPUS390XState *env) | ||||||
|  | { | ||||||
|  |     LowCore *lowcore; | ||||||
|  |     hwaddr len = sizeof(LowCore); | ||||||
|  | 
 | ||||||
|  |     lowcore = cpu_physical_memory_map(env->psa, &len, 1); | ||||||
|  | 
 | ||||||
|  |     if (len < sizeof(LowCore)) { | ||||||
|  |         cpu_abort(env, "Could not map lowcore\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return lowcore; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cpu_unmap_lowcore(LowCore *lowcore) | ||||||
|  | { | ||||||
|  |     cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, | ||||||
|  |                                    int is_write) | ||||||
|  | { | ||||||
|  |     hwaddr start = addr; | ||||||
|  | 
 | ||||||
|  |     /* Mind the prefix area. */ | ||||||
|  |     if (addr < 8192) { | ||||||
|  |         /* Map the lowcore. */ | ||||||
|  |         start += env->psa; | ||||||
|  |         *len = MIN(*len, 8192 - addr); | ||||||
|  |     } else if ((addr >= env->psa) && (addr < env->psa + 8192)) { | ||||||
|  |         /* Map the 0 page. */ | ||||||
|  |         start -= env->psa; | ||||||
|  |         *len = MIN(*len, 8192 - start); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return cpu_physical_memory_map(start, len, is_write); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, | ||||||
|  |                                     int is_write) | ||||||
|  | { | ||||||
|  |     cpu_physical_memory_unmap(addr, len, is_write, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void do_svc_interrupt(CPUS390XState *env) | static void do_svc_interrupt(CPUS390XState *env) | ||||||
| { | { | ||||||
|     uint64_t mask, addr; |     uint64_t mask, addr; | ||||||
|     LowCore *lowcore; |     LowCore *lowcore; | ||||||
|     hwaddr len = TARGET_PAGE_SIZE; |  | ||||||
| 
 | 
 | ||||||
|     lowcore = cpu_physical_memory_map(env->psa, &len, 1); |     lowcore = cpu_map_lowcore(env); | ||||||
| 
 | 
 | ||||||
|     lowcore->svc_code = cpu_to_be16(env->int_svc_code); |     lowcore->svc_code = cpu_to_be16(env->int_svc_code); | ||||||
|     lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen); |     lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen); | ||||||
| @ -486,7 +529,7 @@ static void do_svc_interrupt(CPUS390XState *env) | |||||||
|     mask = be64_to_cpu(lowcore->svc_new_psw.mask); |     mask = be64_to_cpu(lowcore->svc_new_psw.mask); | ||||||
|     addr = be64_to_cpu(lowcore->svc_new_psw.addr); |     addr = be64_to_cpu(lowcore->svc_new_psw.addr); | ||||||
| 
 | 
 | ||||||
|     cpu_physical_memory_unmap(lowcore, len, 1, len); |     cpu_unmap_lowcore(lowcore); | ||||||
| 
 | 
 | ||||||
|     load_psw(env, mask, addr); |     load_psw(env, mask, addr); | ||||||
| } | } | ||||||
| @ -495,7 +538,6 @@ static void do_program_interrupt(CPUS390XState *env) | |||||||
| { | { | ||||||
|     uint64_t mask, addr; |     uint64_t mask, addr; | ||||||
|     LowCore *lowcore; |     LowCore *lowcore; | ||||||
|     hwaddr len = TARGET_PAGE_SIZE; |  | ||||||
|     int ilen = env->int_pgm_ilen; |     int ilen = env->int_pgm_ilen; | ||||||
| 
 | 
 | ||||||
|     switch (ilen) { |     switch (ilen) { | ||||||
| @ -513,7 +555,7 @@ static void do_program_interrupt(CPUS390XState *env) | |||||||
|     qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", |     qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", | ||||||
|                   __func__, env->int_pgm_code, ilen); |                   __func__, env->int_pgm_code, ilen); | ||||||
| 
 | 
 | ||||||
|     lowcore = cpu_physical_memory_map(env->psa, &len, 1); |     lowcore = cpu_map_lowcore(env); | ||||||
| 
 | 
 | ||||||
|     lowcore->pgm_ilen = cpu_to_be16(ilen); |     lowcore->pgm_ilen = cpu_to_be16(ilen); | ||||||
|     lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); |     lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); | ||||||
| @ -522,7 +564,7 @@ static void do_program_interrupt(CPUS390XState *env) | |||||||
|     mask = be64_to_cpu(lowcore->program_new_psw.mask); |     mask = be64_to_cpu(lowcore->program_new_psw.mask); | ||||||
|     addr = be64_to_cpu(lowcore->program_new_psw.addr); |     addr = be64_to_cpu(lowcore->program_new_psw.addr); | ||||||
| 
 | 
 | ||||||
|     cpu_physical_memory_unmap(lowcore, len, 1, len); |     cpu_unmap_lowcore(lowcore); | ||||||
| 
 | 
 | ||||||
|     DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, |     DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, | ||||||
|             env->int_pgm_code, ilen, env->psw.mask, |             env->int_pgm_code, ilen, env->psw.mask, | ||||||
| @ -537,7 +579,6 @@ static void do_ext_interrupt(CPUS390XState *env) | |||||||
| { | { | ||||||
|     uint64_t mask, addr; |     uint64_t mask, addr; | ||||||
|     LowCore *lowcore; |     LowCore *lowcore; | ||||||
|     hwaddr len = TARGET_PAGE_SIZE; |  | ||||||
|     ExtQueue *q; |     ExtQueue *q; | ||||||
| 
 | 
 | ||||||
|     if (!(env->psw.mask & PSW_MASK_EXT)) { |     if (!(env->psw.mask & PSW_MASK_EXT)) { | ||||||
| @ -549,7 +590,7 @@ static void do_ext_interrupt(CPUS390XState *env) | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     q = &env->ext_queue[env->ext_index]; |     q = &env->ext_queue[env->ext_index]; | ||||||
|     lowcore = cpu_physical_memory_map(env->psa, &len, 1); |     lowcore = cpu_map_lowcore(env); | ||||||
| 
 | 
 | ||||||
|     lowcore->ext_int_code = cpu_to_be16(q->code); |     lowcore->ext_int_code = cpu_to_be16(q->code); | ||||||
|     lowcore->ext_params = cpu_to_be32(q->param); |     lowcore->ext_params = cpu_to_be32(q->param); | ||||||
| @ -560,7 +601,7 @@ static void do_ext_interrupt(CPUS390XState *env) | |||||||
|     mask = be64_to_cpu(lowcore->external_new_psw.mask); |     mask = be64_to_cpu(lowcore->external_new_psw.mask); | ||||||
|     addr = be64_to_cpu(lowcore->external_new_psw.addr); |     addr = be64_to_cpu(lowcore->external_new_psw.addr); | ||||||
| 
 | 
 | ||||||
|     cpu_physical_memory_unmap(lowcore, len, 1, len); |     cpu_unmap_lowcore(lowcore); | ||||||
| 
 | 
 | ||||||
|     env->ext_index--; |     env->ext_index--; | ||||||
|     if (env->ext_index == -1) { |     if (env->ext_index == -1) { | ||||||
| @ -573,12 +614,140 @@ static void do_ext_interrupt(CPUS390XState *env) | |||||||
|     load_psw(env, mask, addr); |     load_psw(env, mask, addr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void do_io_interrupt(CPUS390XState *env) | ||||||
|  | { | ||||||
|  |     uint64_t mask, addr; | ||||||
|  |     LowCore *lowcore; | ||||||
|  |     IOIntQueue *q; | ||||||
|  |     uint8_t isc; | ||||||
|  |     int disable = 1; | ||||||
|  |     int found = 0; | ||||||
|  | 
 | ||||||
|  |     if (!(env->psw.mask & PSW_MASK_IO)) { | ||||||
|  |         cpu_abort(env, "I/O int w/o I/O mask\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) { | ||||||
|  |         if (env->io_index[isc] < 0) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (env->io_index[isc] > MAX_IO_QUEUE) { | ||||||
|  |             cpu_abort(env, "I/O queue overrun for isc %d: %d\n", | ||||||
|  |                       isc, env->io_index[isc]); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         q = &env->io_queue[env->io_index[isc]][isc]; | ||||||
|  |         if (!(env->cregs[6] & q->word)) { | ||||||
|  |             disable = 0; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         found = 1; | ||||||
|  |         lowcore = cpu_map_lowcore(env); | ||||||
|  | 
 | ||||||
|  |         lowcore->subchannel_id = cpu_to_be16(q->id); | ||||||
|  |         lowcore->subchannel_nr = cpu_to_be16(q->nr); | ||||||
|  |         lowcore->io_int_parm = cpu_to_be32(q->parm); | ||||||
|  |         lowcore->io_int_word = cpu_to_be32(q->word); | ||||||
|  |         lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env)); | ||||||
|  |         lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr); | ||||||
|  |         mask = be64_to_cpu(lowcore->io_new_psw.mask); | ||||||
|  |         addr = be64_to_cpu(lowcore->io_new_psw.addr); | ||||||
|  | 
 | ||||||
|  |         cpu_unmap_lowcore(lowcore); | ||||||
|  | 
 | ||||||
|  |         env->io_index[isc]--; | ||||||
|  |         if (env->io_index >= 0) { | ||||||
|  |             disable = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (disable) { | ||||||
|  |         env->pending_int &= ~INTERRUPT_IO; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (found) { | ||||||
|  |         DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, | ||||||
|  |                 env->psw.mask, env->psw.addr); | ||||||
|  |         load_psw(env, mask, addr); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void do_mchk_interrupt(CPUS390XState *env) | ||||||
|  | { | ||||||
|  |     uint64_t mask, addr; | ||||||
|  |     LowCore *lowcore; | ||||||
|  |     MchkQueue *q; | ||||||
|  |     int i; | ||||||
|  | 
 | ||||||
|  |     if (!(env->psw.mask & PSW_MASK_MCHECK)) { | ||||||
|  |         cpu_abort(env, "Machine check w/o mchk mask\n"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) { | ||||||
|  |         cpu_abort(env, "Mchk queue overrun: %d\n", env->mchk_index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     q = &env->mchk_queue[env->mchk_index]; | ||||||
|  | 
 | ||||||
|  |     if (q->type != 1) { | ||||||
|  |         /* Don't know how to handle this... */ | ||||||
|  |         cpu_abort(env, "Unknown machine check type %d\n", q->type); | ||||||
|  |     } | ||||||
|  |     if (!(env->cregs[14] & (1 << 28))) { | ||||||
|  |         /* CRW machine checks disabled */ | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lowcore = cpu_map_lowcore(env); | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < 16; i++) { | ||||||
|  |         lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll); | ||||||
|  |         lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]); | ||||||
|  |         lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]); | ||||||
|  |         lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]); | ||||||
|  |     } | ||||||
|  |     lowcore->prefixreg_save_area = cpu_to_be32(env->psa); | ||||||
|  |     lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc); | ||||||
|  |     lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr); | ||||||
|  |     lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32); | ||||||
|  |     lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm); | ||||||
|  |     lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32); | ||||||
|  |     lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc); | ||||||
|  | 
 | ||||||
|  |     lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d); | ||||||
|  |     lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000); | ||||||
|  |     lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env)); | ||||||
|  |     lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr); | ||||||
|  |     mask = be64_to_cpu(lowcore->mcck_new_psw.mask); | ||||||
|  |     addr = be64_to_cpu(lowcore->mcck_new_psw.addr); | ||||||
|  | 
 | ||||||
|  |     cpu_unmap_lowcore(lowcore); | ||||||
|  | 
 | ||||||
|  |     env->mchk_index--; | ||||||
|  |     if (env->mchk_index == -1) { | ||||||
|  |         env->pending_int &= ~INTERRUPT_MCHK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, | ||||||
|  |             env->psw.mask, env->psw.addr); | ||||||
|  | 
 | ||||||
|  |     load_psw(env, mask, addr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void do_interrupt(CPUS390XState *env) | void do_interrupt(CPUS390XState *env) | ||||||
| { | { | ||||||
|     qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", |     qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", | ||||||
|                   __func__, env->exception_index, env->psw.addr); |                   __func__, env->exception_index, env->psw.addr); | ||||||
| 
 | 
 | ||||||
|     s390_add_running_cpu(env); |     s390_add_running_cpu(env); | ||||||
|  |     /* handle machine checks */ | ||||||
|  |     if ((env->psw.mask & PSW_MASK_MCHECK) && | ||||||
|  |         (env->exception_index == -1)) { | ||||||
|  |         if (env->pending_int & INTERRUPT_MCHK) { | ||||||
|  |             env->exception_index = EXCP_MCHK; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     /* handle external interrupts */ |     /* handle external interrupts */ | ||||||
|     if ((env->psw.mask & PSW_MASK_EXT) && |     if ((env->psw.mask & PSW_MASK_EXT) && | ||||||
|         env->exception_index == -1) { |         env->exception_index == -1) { | ||||||
| @ -597,6 +766,13 @@ void do_interrupt(CPUS390XState *env) | |||||||
|             env->pending_int &= ~INTERRUPT_TOD; |             env->pending_int &= ~INTERRUPT_TOD; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     /* handle I/O interrupts */ | ||||||
|  |     if ((env->psw.mask & PSW_MASK_IO) && | ||||||
|  |         (env->exception_index == -1)) { | ||||||
|  |         if (env->pending_int & INTERRUPT_IO) { | ||||||
|  |             env->exception_index = EXCP_IO; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     switch (env->exception_index) { |     switch (env->exception_index) { | ||||||
|     case EXCP_PGM: |     case EXCP_PGM: | ||||||
| @ -608,6 +784,12 @@ void do_interrupt(CPUS390XState *env) | |||||||
|     case EXCP_EXT: |     case EXCP_EXT: | ||||||
|         do_ext_interrupt(env); |         do_ext_interrupt(env); | ||||||
|         break; |         break; | ||||||
|  |     case EXCP_IO: | ||||||
|  |         do_io_interrupt(env); | ||||||
|  |         break; | ||||||
|  |     case EXCP_MCHK: | ||||||
|  |         do_mchk_interrupt(env); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|     env->exception_index = -1; |     env->exception_index = -1; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										761
									
								
								target-s390x/ioinst.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										761
									
								
								target-s390x/ioinst.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,761 @@ | |||||||
|  | /*
 | ||||||
|  |  * I/O instructions for S/390 | ||||||
|  |  * | ||||||
|  |  * Copyright 2012 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.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 <sys/types.h> | ||||||
|  | 
 | ||||||
|  | #include "cpu.h" | ||||||
|  | #include "ioinst.h" | ||||||
|  | #include "trace.h" | ||||||
|  | 
 | ||||||
|  | int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, | ||||||
|  |                                  int *schid) | ||||||
|  | { | ||||||
|  |     if (!IOINST_SCHID_ONE(value)) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |     if (!IOINST_SCHID_M(value)) { | ||||||
|  |         if (IOINST_SCHID_CSSID(value)) { | ||||||
|  |             return -EINVAL; | ||||||
|  |         } | ||||||
|  |         *cssid = 0; | ||||||
|  |         *m = 0; | ||||||
|  |     } else { | ||||||
|  |         *cssid = IOINST_SCHID_CSSID(value); | ||||||
|  |         *m = 1; | ||||||
|  |     } | ||||||
|  |     *ssid = IOINST_SCHID_SSID(value); | ||||||
|  |     *schid = IOINST_SCHID_NR(value); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("xsch", cssid, ssid, schid); | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_xsch(sch); | ||||||
|  |     } | ||||||
|  |     switch (ret) { | ||||||
|  |     case -ENODEV: | ||||||
|  |         cc = 3; | ||||||
|  |         break; | ||||||
|  |     case -EBUSY: | ||||||
|  |         cc = 2; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         cc = 0; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         cc = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("csch", cssid, ssid, schid); | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_csch(sch); | ||||||
|  |     } | ||||||
|  |     if (ret == -ENODEV) { | ||||||
|  |         cc = 3; | ||||||
|  |     } else { | ||||||
|  |         cc = 0; | ||||||
|  |     } | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("hsch", cssid, ssid, schid); | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_hsch(sch); | ||||||
|  |     } | ||||||
|  |     switch (ret) { | ||||||
|  |     case -ENODEV: | ||||||
|  |         cc = 3; | ||||||
|  |         break; | ||||||
|  |     case -EBUSY: | ||||||
|  |         cc = 2; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         cc = 0; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         cc = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ioinst_schib_valid(SCHIB *schib) | ||||||
|  | { | ||||||
|  |     if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) || | ||||||
|  |         (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     /* Disallow extended measurements for now. */ | ||||||
|  |     if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     SCHIB *schib; | ||||||
|  |     uint64_t addr; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  |     hwaddr len = sizeof(*schib); | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("msch", cssid, ssid, schid); | ||||||
|  |     addr = decode_basedisp_s(env, ipb); | ||||||
|  |     schib = s390_cpu_physical_memory_map(env, addr, &len, 0); | ||||||
|  |     if (!schib || len != sizeof(*schib)) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     if (!ioinst_schib_valid(schib)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_msch(sch, schib); | ||||||
|  |     } | ||||||
|  |     switch (ret) { | ||||||
|  |     case -ENODEV: | ||||||
|  |         cc = 3; | ||||||
|  |         break; | ||||||
|  |     case -EBUSY: | ||||||
|  |         cc = 2; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         cc = 0; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         cc = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, schib, len, 0); | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void copy_orb_from_guest(ORB *dest, const ORB *src) | ||||||
|  | { | ||||||
|  |     dest->intparm = be32_to_cpu(src->intparm); | ||||||
|  |     dest->ctrl0 = be16_to_cpu(src->ctrl0); | ||||||
|  |     dest->lpm = src->lpm; | ||||||
|  |     dest->ctrl1 = src->ctrl1; | ||||||
|  |     dest->cpa = be32_to_cpu(src->cpa); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ioinst_orb_valid(ORB *orb) | ||||||
|  | { | ||||||
|  |     if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) || | ||||||
|  |         (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     if ((orb->cpa & HIGH_ORDER_BIT) != 0) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     ORB *orig_orb, orb; | ||||||
|  |     uint64_t addr; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  |     hwaddr len = sizeof(*orig_orb); | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("ssch", cssid, ssid, schid); | ||||||
|  |     addr = decode_basedisp_s(env, ipb); | ||||||
|  |     orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0); | ||||||
|  |     if (!orig_orb || len != sizeof(*orig_orb)) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     copy_orb_from_guest(&orb, orig_orb); | ||||||
|  |     if (!ioinst_orb_valid(&orb)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_ssch(sch, &orb); | ||||||
|  |     } | ||||||
|  |     switch (ret) { | ||||||
|  |     case -ENODEV: | ||||||
|  |         cc = 3; | ||||||
|  |         break; | ||||||
|  |     case -EBUSY: | ||||||
|  |         cc = 2; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         cc = 0; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         cc = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, orig_orb, len, 0); | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     CRW *crw; | ||||||
|  |     uint64_t addr; | ||||||
|  |     int cc; | ||||||
|  |     hwaddr len = sizeof(*crw); | ||||||
|  | 
 | ||||||
|  |     addr = decode_basedisp_s(env, ipb); | ||||||
|  |     crw = s390_cpu_physical_memory_map(env, addr, &len, 1); | ||||||
|  |     if (!crw || len != sizeof(*crw)) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     cc = css_do_stcrw(crw); | ||||||
|  |     /* 0 - crw stored, 1 - zeroes stored */ | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, crw, len, 1); | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     uint64_t addr; | ||||||
|  |     int cc; | ||||||
|  |     SCHIB *schib; | ||||||
|  |     hwaddr len = sizeof(*schib); | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("stsch", cssid, ssid, schid); | ||||||
|  |     addr = decode_basedisp_s(env, ipb); | ||||||
|  |     schib = s390_cpu_physical_memory_map(env, addr, &len, 1); | ||||||
|  |     if (!schib || len != sizeof(*schib)) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch) { | ||||||
|  |         if (css_subch_visible(sch)) { | ||||||
|  |             css_do_stsch(sch, schib); | ||||||
|  |             cc = 0; | ||||||
|  |         } else { | ||||||
|  |             /* Indicate no more subchannels in this css/ss */ | ||||||
|  |             cc = 3; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if (css_schid_final(cssid, ssid, schid)) { | ||||||
|  |             cc = 3; /* No more subchannels in this css/ss */ | ||||||
|  |         } else { | ||||||
|  |             /* Store an empty schib. */ | ||||||
|  |             memset(schib, 0, sizeof(*schib)); | ||||||
|  |             cc = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, schib, len, 1); | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     IRB *irb; | ||||||
|  |     uint64_t addr; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  |     hwaddr len = sizeof(*irb); | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("tsch", cssid, ssid, schid); | ||||||
|  |     addr = decode_basedisp_s(env, ipb); | ||||||
|  |     irb = s390_cpu_physical_memory_map(env, addr, &len, 1); | ||||||
|  |     if (!irb || len != sizeof(*irb)) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         cc = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_tsch(sch, irb); | ||||||
|  |         /* 0 - status pending, 1 - not status pending */ | ||||||
|  |         cc = ret; | ||||||
|  |     } else { | ||||||
|  |         cc = 3; | ||||||
|  |     } | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1); | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef struct ChscReq { | ||||||
|  |     uint16_t len; | ||||||
|  |     uint16_t command; | ||||||
|  |     uint32_t param0; | ||||||
|  |     uint32_t param1; | ||||||
|  |     uint32_t param2; | ||||||
|  | } QEMU_PACKED ChscReq; | ||||||
|  | 
 | ||||||
|  | typedef struct ChscResp { | ||||||
|  |     uint16_t len; | ||||||
|  |     uint16_t code; | ||||||
|  |     uint32_t param; | ||||||
|  |     char data[0]; | ||||||
|  | } QEMU_PACKED ChscResp; | ||||||
|  | 
 | ||||||
|  | #define CHSC_MIN_RESP_LEN 0x0008 | ||||||
|  | 
 | ||||||
|  | #define CHSC_SCPD 0x0002 | ||||||
|  | #define CHSC_SCSC 0x0010 | ||||||
|  | #define CHSC_SDA  0x0031 | ||||||
|  | 
 | ||||||
|  | #define CHSC_SCPD_0_M 0x20000000 | ||||||
|  | #define CHSC_SCPD_0_C 0x10000000 | ||||||
|  | #define CHSC_SCPD_0_FMT 0x0f000000 | ||||||
|  | #define CHSC_SCPD_0_CSSID 0x00ff0000 | ||||||
|  | #define CHSC_SCPD_0_RFMT 0x00000f00 | ||||||
|  | #define CHSC_SCPD_0_RES 0xc000f000 | ||||||
|  | #define CHSC_SCPD_1_RES 0xffffff00 | ||||||
|  | #define CHSC_SCPD_01_CHPID 0x000000ff | ||||||
|  | static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res) | ||||||
|  | { | ||||||
|  |     uint16_t len = be16_to_cpu(req->len); | ||||||
|  |     uint32_t param0 = be32_to_cpu(req->param0); | ||||||
|  |     uint32_t param1 = be32_to_cpu(req->param1); | ||||||
|  |     uint16_t resp_code; | ||||||
|  |     int rfmt; | ||||||
|  |     uint16_t cssid; | ||||||
|  |     uint8_t f_chpid, l_chpid; | ||||||
|  |     int desc_size; | ||||||
|  |     int m; | ||||||
|  | 
 | ||||||
|  |     rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8; | ||||||
|  |     if ((rfmt == 0) ||  (rfmt == 1)) { | ||||||
|  |         rfmt = !!(param0 & CHSC_SCPD_0_C); | ||||||
|  |     } | ||||||
|  |     if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) || | ||||||
|  |         (param1 & CHSC_SCPD_1_RES) || req->param2) { | ||||||
|  |         resp_code = 0x0003; | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  |     if (param0 & CHSC_SCPD_0_FMT) { | ||||||
|  |         resp_code = 0x0007; | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  |     cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16; | ||||||
|  |     m = param0 & CHSC_SCPD_0_M; | ||||||
|  |     if (cssid != 0) { | ||||||
|  |         if (!m || !css_present(cssid)) { | ||||||
|  |             resp_code = 0x0008; | ||||||
|  |             goto out_err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     f_chpid = param0 & CHSC_SCPD_01_CHPID; | ||||||
|  |     l_chpid = param1 & CHSC_SCPD_01_CHPID; | ||||||
|  |     if (l_chpid < f_chpid) { | ||||||
|  |         resp_code = 0x0003; | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  |     /* css_collect_chp_desc() is endian-aware */ | ||||||
|  |     desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt, | ||||||
|  |                                      &res->data); | ||||||
|  |     res->code = cpu_to_be16(0x0001); | ||||||
|  |     res->len = cpu_to_be16(8 + desc_size); | ||||||
|  |     res->param = cpu_to_be32(rfmt); | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   out_err: | ||||||
|  |     res->code = cpu_to_be16(resp_code); | ||||||
|  |     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); | ||||||
|  |     res->param = cpu_to_be32(rfmt); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define CHSC_SCSC_0_M 0x20000000 | ||||||
|  | #define CHSC_SCSC_0_FMT 0x000f0000 | ||||||
|  | #define CHSC_SCSC_0_CSSID 0x0000ff00 | ||||||
|  | #define CHSC_SCSC_0_RES 0xdff000ff | ||||||
|  | static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res) | ||||||
|  | { | ||||||
|  |     uint16_t len = be16_to_cpu(req->len); | ||||||
|  |     uint32_t param0 = be32_to_cpu(req->param0); | ||||||
|  |     uint8_t cssid; | ||||||
|  |     uint16_t resp_code; | ||||||
|  |     uint32_t general_chars[510]; | ||||||
|  |     uint32_t chsc_chars[508]; | ||||||
|  | 
 | ||||||
|  |     if (len != 0x0010) { | ||||||
|  |         resp_code = 0x0003; | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (param0 & CHSC_SCSC_0_FMT) { | ||||||
|  |         resp_code = 0x0007; | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  |     cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8; | ||||||
|  |     if (cssid != 0) { | ||||||
|  |         if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) { | ||||||
|  |             resp_code = 0x0008; | ||||||
|  |             goto out_err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) { | ||||||
|  |         resp_code = 0x0003; | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  |     res->code = cpu_to_be16(0x0001); | ||||||
|  |     res->len = cpu_to_be16(4080); | ||||||
|  |     res->param = 0; | ||||||
|  | 
 | ||||||
|  |     memset(general_chars, 0, sizeof(general_chars)); | ||||||
|  |     memset(chsc_chars, 0, sizeof(chsc_chars)); | ||||||
|  | 
 | ||||||
|  |     general_chars[0] = cpu_to_be32(0x03000000); | ||||||
|  |     general_chars[1] = cpu_to_be32(0x00059000); | ||||||
|  | 
 | ||||||
|  |     chsc_chars[0] = cpu_to_be32(0x40000000); | ||||||
|  |     chsc_chars[3] = cpu_to_be32(0x00040000); | ||||||
|  | 
 | ||||||
|  |     memcpy(res->data, general_chars, sizeof(general_chars)); | ||||||
|  |     memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars)); | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   out_err: | ||||||
|  |     res->code = cpu_to_be16(resp_code); | ||||||
|  |     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); | ||||||
|  |     res->param = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define CHSC_SDA_0_FMT 0x0f000000 | ||||||
|  | #define CHSC_SDA_0_OC 0x0000ffff | ||||||
|  | #define CHSC_SDA_0_RES 0xf0ff0000 | ||||||
|  | #define CHSC_SDA_OC_MCSSE 0x0 | ||||||
|  | #define CHSC_SDA_OC_MSS 0x2 | ||||||
|  | static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res) | ||||||
|  | { | ||||||
|  |     uint16_t resp_code = 0x0001; | ||||||
|  |     uint16_t len = be16_to_cpu(req->len); | ||||||
|  |     uint32_t param0 = be32_to_cpu(req->param0); | ||||||
|  |     uint16_t oc; | ||||||
|  |     int ret; | ||||||
|  | 
 | ||||||
|  |     if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) { | ||||||
|  |         resp_code = 0x0003; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (param0 & CHSC_SDA_0_FMT) { | ||||||
|  |         resp_code = 0x0007; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     oc = param0 & CHSC_SDA_0_OC; | ||||||
|  |     switch (oc) { | ||||||
|  |     case CHSC_SDA_OC_MCSSE: | ||||||
|  |         ret = css_enable_mcsse(); | ||||||
|  |         if (ret == -EINVAL) { | ||||||
|  |             resp_code = 0x0101; | ||||||
|  |             goto out; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CHSC_SDA_OC_MSS: | ||||||
|  |         ret = css_enable_mss(); | ||||||
|  |         if (ret == -EINVAL) { | ||||||
|  |             resp_code = 0x0101; | ||||||
|  |             goto out; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         resp_code = 0x0003; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  |     res->code = cpu_to_be16(resp_code); | ||||||
|  |     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); | ||||||
|  |     res->param = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ioinst_handle_chsc_unimplemented(ChscResp *res) | ||||||
|  | { | ||||||
|  |     res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); | ||||||
|  |     res->code = cpu_to_be16(0x0004); | ||||||
|  |     res->param = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     ChscReq *req; | ||||||
|  |     ChscResp *res; | ||||||
|  |     uint64_t addr; | ||||||
|  |     int reg; | ||||||
|  |     uint16_t len; | ||||||
|  |     uint16_t command; | ||||||
|  |     hwaddr map_size = TARGET_PAGE_SIZE; | ||||||
|  |     int ret = 0; | ||||||
|  | 
 | ||||||
|  |     trace_ioinst("chsc"); | ||||||
|  |     reg = (ipb >> 20) & 0x00f; | ||||||
|  |     addr = env->regs[reg]; | ||||||
|  |     /* Page boundary? */ | ||||||
|  |     if (addr & 0xfff) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     req = s390_cpu_physical_memory_map(env, addr, &map_size, 1); | ||||||
|  |     if (!req || map_size != TARGET_PAGE_SIZE) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 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); | ||||||
|  |     res = (void *)((char *)req + len); | ||||||
|  |     command = be16_to_cpu(req->command); | ||||||
|  |     trace_ioinst_chsc_cmd(command, len); | ||||||
|  |     switch (command) { | ||||||
|  |     case CHSC_SCSC: | ||||||
|  |         ioinst_handle_chsc_scsc(req, res); | ||||||
|  |         break; | ||||||
|  |     case CHSC_SCPD: | ||||||
|  |         ioinst_handle_chsc_scpd(req, res); | ||||||
|  |         break; | ||||||
|  |     case CHSC_SDA: | ||||||
|  |         ioinst_handle_chsc_sda(req, res); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ioinst_handle_chsc_unimplemented(res); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, req, map_size, 1); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) | ||||||
|  | { | ||||||
|  |     uint64_t addr; | ||||||
|  |     int lowcore; | ||||||
|  |     IOIntCode *int_code; | ||||||
|  |     hwaddr len, orig_len; | ||||||
|  |     int ret; | ||||||
|  | 
 | ||||||
|  |     trace_ioinst("tpi"); | ||||||
|  |     addr = decode_basedisp_s(env, ipb); | ||||||
|  |     lowcore = addr ? 0 : 1; | ||||||
|  |     len = lowcore ? 8 /* two words */ : 12 /* three words */; | ||||||
|  |     orig_len = len; | ||||||
|  |     int_code = s390_cpu_physical_memory_map(env, addr, &len, 1); | ||||||
|  |     if (!int_code || (len != orig_len)) { | ||||||
|  |         program_interrupt(env, PGM_SPECIFICATION, 2); | ||||||
|  |         ret = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     ret = css_do_tpi(int_code, lowcore); | ||||||
|  | out: | ||||||
|  |     s390_cpu_physical_memory_unmap(env, int_code, len, 1); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc) | ||||||
|  | #define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28) | ||||||
|  | #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) | ||||||
|  | { | ||||||
|  |     uint8_t mbk; | ||||||
|  |     int update; | ||||||
|  |     int dct; | ||||||
|  | 
 | ||||||
|  |     trace_ioinst("schm"); | ||||||
|  | 
 | ||||||
|  |     if (SCHM_REG1_RES(reg1)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     mbk = SCHM_REG1_MBK(reg1); | ||||||
|  |     update = SCHM_REG1_UPD(reg1); | ||||||
|  |     dct = SCHM_REG1_DCT(reg1); | ||||||
|  | 
 | ||||||
|  |     if (update && (reg2 & 0x0000000000000fff)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     css_do_schm(mbk, update, dct, update ? reg2 : 0); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) | ||||||
|  | { | ||||||
|  |     int cssid, ssid, schid, m; | ||||||
|  |     SubchDev *sch; | ||||||
|  |     int ret = -ENODEV; | ||||||
|  |     int cc; | ||||||
|  | 
 | ||||||
|  |     if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  |     trace_ioinst_sch_id("rsch", cssid, ssid, schid); | ||||||
|  |     sch = css_find_subch(m, cssid, ssid, schid); | ||||||
|  |     if (sch && css_subch_visible(sch)) { | ||||||
|  |         ret = css_do_rsch(sch); | ||||||
|  |     } | ||||||
|  |     switch (ret) { | ||||||
|  |     case -ENODEV: | ||||||
|  |         cc = 3; | ||||||
|  |         break; | ||||||
|  |     case -EINVAL: | ||||||
|  |         cc = 2; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         cc = 0; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         cc = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 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) | ||||||
|  | { | ||||||
|  |     int cc; | ||||||
|  |     uint8_t cssid; | ||||||
|  |     uint8_t chpid; | ||||||
|  |     int ret; | ||||||
|  | 
 | ||||||
|  |     if (RCHP_REG1_RES(reg1)) { | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cssid = RCHP_REG1_CSSID(reg1); | ||||||
|  |     chpid = RCHP_REG1_CHPID(reg1); | ||||||
|  | 
 | ||||||
|  |     trace_ioinst_chp_id("rchp", cssid, chpid); | ||||||
|  | 
 | ||||||
|  |     ret = css_do_rchp(cssid, chpid); | ||||||
|  | 
 | ||||||
|  |     switch (ret) { | ||||||
|  |     case -ENODEV: | ||||||
|  |         cc = 3; | ||||||
|  |         break; | ||||||
|  |     case -EBUSY: | ||||||
|  |         cc = 2; | ||||||
|  |         break; | ||||||
|  |     case 0: | ||||||
|  |         cc = 0; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         /* Invalid channel subsystem. */ | ||||||
|  |         program_interrupt(env, PGM_OPERAND, 2); | ||||||
|  |         return -EIO; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return cc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000) | ||||||
|  | int ioinst_handle_sal(CPUS390XState *env, 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; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										230
									
								
								target-s390x/ioinst.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								target-s390x/ioinst.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,230 @@ | |||||||
|  | /*
 | ||||||
|  |  * S/390 channel I/O instructions | ||||||
|  |  * | ||||||
|  |  * Copyright 2012 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  |  * directory. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #ifndef IOINST_S390X_H | ||||||
|  | #define IOINST_S390X_H | ||||||
|  | /*
 | ||||||
|  |  * Channel I/O related definitions, as defined in the Principles | ||||||
|  |  * Of Operation (and taken from the Linux implementation). | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* subchannel status word (command mode only) */ | ||||||
|  | typedef struct SCSW { | ||||||
|  |     uint16_t flags; | ||||||
|  |     uint16_t ctrl; | ||||||
|  |     uint32_t cpa; | ||||||
|  |     uint8_t dstat; | ||||||
|  |     uint8_t cstat; | ||||||
|  |     uint16_t count; | ||||||
|  | } QEMU_PACKED SCSW; | ||||||
|  | 
 | ||||||
|  | #define SCSW_FLAGS_MASK_KEY 0xf000 | ||||||
|  | #define SCSW_FLAGS_MASK_SCTL 0x0800 | ||||||
|  | #define SCSW_FLAGS_MASK_ESWF 0x0400 | ||||||
|  | #define SCSW_FLAGS_MASK_CC 0x0300 | ||||||
|  | #define SCSW_FLAGS_MASK_FMT 0x0080 | ||||||
|  | #define SCSW_FLAGS_MASK_PFCH 0x0040 | ||||||
|  | #define SCSW_FLAGS_MASK_ISIC 0x0020 | ||||||
|  | #define SCSW_FLAGS_MASK_ALCC 0x0010 | ||||||
|  | #define SCSW_FLAGS_MASK_SSI 0x0008 | ||||||
|  | #define SCSW_FLAGS_MASK_ZCC 0x0004 | ||||||
|  | #define SCSW_FLAGS_MASK_ECTL 0x0002 | ||||||
|  | #define SCSW_FLAGS_MASK_PNO 0x0001 | ||||||
|  | 
 | ||||||
|  | #define SCSW_CTRL_MASK_FCTL 0x7000 | ||||||
|  | #define SCSW_CTRL_MASK_ACTL 0x0fe0 | ||||||
|  | #define SCSW_CTRL_MASK_STCTL 0x001f | ||||||
|  | 
 | ||||||
|  | #define SCSW_FCTL_CLEAR_FUNC 0x1000 | ||||||
|  | #define SCSW_FCTL_HALT_FUNC 0x2000 | ||||||
|  | #define SCSW_FCTL_START_FUNC 0x4000 | ||||||
|  | 
 | ||||||
|  | #define SCSW_ACTL_SUSP 0x0020 | ||||||
|  | #define SCSW_ACTL_DEVICE_ACTIVE 0x0040 | ||||||
|  | #define SCSW_ACTL_SUBCH_ACTIVE 0x0080 | ||||||
|  | #define SCSW_ACTL_CLEAR_PEND 0x0100 | ||||||
|  | #define SCSW_ACTL_HALT_PEND  0x0200 | ||||||
|  | #define SCSW_ACTL_START_PEND 0x0400 | ||||||
|  | #define SCSW_ACTL_RESUME_PEND 0x0800 | ||||||
|  | 
 | ||||||
|  | #define SCSW_STCTL_STATUS_PEND 0x0001 | ||||||
|  | #define SCSW_STCTL_SECONDARY 0x0002 | ||||||
|  | #define SCSW_STCTL_PRIMARY 0x0004 | ||||||
|  | #define SCSW_STCTL_INTERMEDIATE 0x0008 | ||||||
|  | #define SCSW_STCTL_ALERT 0x0010 | ||||||
|  | 
 | ||||||
|  | #define SCSW_DSTAT_ATTENTION     0x80 | ||||||
|  | #define SCSW_DSTAT_STAT_MOD      0x40 | ||||||
|  | #define SCSW_DSTAT_CU_END        0x20 | ||||||
|  | #define SCSW_DSTAT_BUSY          0x10 | ||||||
|  | #define SCSW_DSTAT_CHANNEL_END   0x08 | ||||||
|  | #define SCSW_DSTAT_DEVICE_END    0x04 | ||||||
|  | #define SCSW_DSTAT_UNIT_CHECK    0x02 | ||||||
|  | #define SCSW_DSTAT_UNIT_EXCEP    0x01 | ||||||
|  | 
 | ||||||
|  | #define SCSW_CSTAT_PCI           0x80 | ||||||
|  | #define SCSW_CSTAT_INCORR_LEN    0x40 | ||||||
|  | #define SCSW_CSTAT_PROG_CHECK    0x20 | ||||||
|  | #define SCSW_CSTAT_PROT_CHECK    0x10 | ||||||
|  | #define SCSW_CSTAT_DATA_CHECK    0x08 | ||||||
|  | #define SCSW_CSTAT_CHN_CTRL_CHK  0x04 | ||||||
|  | #define SCSW_CSTAT_INTF_CTRL_CHK 0x02 | ||||||
|  | #define SCSW_CSTAT_CHAIN_CHECK   0x01 | ||||||
|  | 
 | ||||||
|  | /* path management control word */ | ||||||
|  | typedef struct PMCW { | ||||||
|  |     uint32_t intparm; | ||||||
|  |     uint16_t flags; | ||||||
|  |     uint16_t devno; | ||||||
|  |     uint8_t  lpm; | ||||||
|  |     uint8_t  pnom; | ||||||
|  |     uint8_t  lpum; | ||||||
|  |     uint8_t  pim; | ||||||
|  |     uint16_t mbi; | ||||||
|  |     uint8_t  pom; | ||||||
|  |     uint8_t  pam; | ||||||
|  |     uint8_t  chpid[8]; | ||||||
|  |     uint32_t chars; | ||||||
|  | } QEMU_PACKED PMCW; | ||||||
|  | 
 | ||||||
|  | #define PMCW_FLAGS_MASK_QF 0x8000 | ||||||
|  | #define PMCW_FLAGS_MASK_W 0x4000 | ||||||
|  | #define PMCW_FLAGS_MASK_ISC 0x3800 | ||||||
|  | #define PMCW_FLAGS_MASK_ENA 0x0080 | ||||||
|  | #define PMCW_FLAGS_MASK_LM 0x0060 | ||||||
|  | #define PMCW_FLAGS_MASK_MME 0x0018 | ||||||
|  | #define PMCW_FLAGS_MASK_MP 0x0004 | ||||||
|  | #define PMCW_FLAGS_MASK_TF 0x0002 | ||||||
|  | #define PMCW_FLAGS_MASK_DNV 0x0001 | ||||||
|  | #define PMCW_FLAGS_MASK_INVALID 0x0700 | ||||||
|  | 
 | ||||||
|  | #define PMCW_CHARS_MASK_ST 0x00e00000 | ||||||
|  | #define PMCW_CHARS_MASK_MBFC 0x00000004 | ||||||
|  | #define PMCW_CHARS_MASK_XMWME 0x00000002 | ||||||
|  | #define PMCW_CHARS_MASK_CSENSE 0x00000001 | ||||||
|  | #define PMCW_CHARS_MASK_INVALID 0xff1ffff8 | ||||||
|  | 
 | ||||||
|  | /* subchannel information block */ | ||||||
|  | typedef struct SCHIB { | ||||||
|  |     PMCW pmcw; | ||||||
|  |     SCSW scsw; | ||||||
|  |     uint64_t mba; | ||||||
|  |     uint8_t mda[4]; | ||||||
|  | } QEMU_PACKED SCHIB; | ||||||
|  | 
 | ||||||
|  | /* interruption response block */ | ||||||
|  | typedef struct IRB { | ||||||
|  |     SCSW scsw; | ||||||
|  |     uint32_t esw[5]; | ||||||
|  |     uint32_t ecw[8]; | ||||||
|  |     uint32_t emw[8]; | ||||||
|  | } QEMU_PACKED IRB; | ||||||
|  | 
 | ||||||
|  | /* operation request block */ | ||||||
|  | typedef struct ORB { | ||||||
|  |     uint32_t intparm; | ||||||
|  |     uint16_t ctrl0; | ||||||
|  |     uint8_t lpm; | ||||||
|  |     uint8_t ctrl1; | ||||||
|  |     uint32_t cpa; | ||||||
|  | } QEMU_PACKED ORB; | ||||||
|  | 
 | ||||||
|  | #define ORB_CTRL0_MASK_KEY 0xf000 | ||||||
|  | #define ORB_CTRL0_MASK_SPND 0x0800 | ||||||
|  | #define ORB_CTRL0_MASK_STR 0x0400 | ||||||
|  | #define ORB_CTRL0_MASK_MOD 0x0200 | ||||||
|  | #define ORB_CTRL0_MASK_SYNC 0x0100 | ||||||
|  | #define ORB_CTRL0_MASK_FMT 0x0080 | ||||||
|  | #define ORB_CTRL0_MASK_PFCH 0x0040 | ||||||
|  | #define ORB_CTRL0_MASK_ISIC 0x0020 | ||||||
|  | #define ORB_CTRL0_MASK_ALCC 0x0010 | ||||||
|  | #define ORB_CTRL0_MASK_SSIC 0x0008 | ||||||
|  | #define ORB_CTRL0_MASK_C64 0x0002 | ||||||
|  | #define ORB_CTRL0_MASK_I2K 0x0001 | ||||||
|  | #define ORB_CTRL0_MASK_INVALID 0x0004 | ||||||
|  | 
 | ||||||
|  | #define ORB_CTRL1_MASK_ILS 0x80 | ||||||
|  | #define ORB_CTRL1_MASK_MIDAW 0x40 | ||||||
|  | #define ORB_CTRL1_MASK_ORBX 0x01 | ||||||
|  | #define ORB_CTRL1_MASK_INVALID 0x3e | ||||||
|  | 
 | ||||||
|  | /* channel command word (type 1) */ | ||||||
|  | typedef struct CCW1 { | ||||||
|  |     uint8_t cmd_code; | ||||||
|  |     uint8_t flags; | ||||||
|  |     uint16_t count; | ||||||
|  |     uint32_t cda; | ||||||
|  | } QEMU_PACKED CCW1; | ||||||
|  | 
 | ||||||
|  | #define CCW_FLAG_DC              0x80 | ||||||
|  | #define CCW_FLAG_CC              0x40 | ||||||
|  | #define CCW_FLAG_SLI             0x20 | ||||||
|  | #define CCW_FLAG_SKIP            0x10 | ||||||
|  | #define CCW_FLAG_PCI             0x08 | ||||||
|  | #define CCW_FLAG_IDA             0x04 | ||||||
|  | #define CCW_FLAG_SUSPEND         0x02 | ||||||
|  | 
 | ||||||
|  | #define CCW_CMD_NOOP             0x03 | ||||||
|  | #define CCW_CMD_BASIC_SENSE      0x04 | ||||||
|  | #define CCW_CMD_TIC              0x08 | ||||||
|  | #define CCW_CMD_SENSE_ID         0xe4 | ||||||
|  | 
 | ||||||
|  | typedef struct CRW { | ||||||
|  |     uint16_t flags; | ||||||
|  |     uint16_t rsid; | ||||||
|  | } QEMU_PACKED CRW; | ||||||
|  | 
 | ||||||
|  | #define CRW_FLAGS_MASK_S 0x4000 | ||||||
|  | #define CRW_FLAGS_MASK_R 0x2000 | ||||||
|  | #define CRW_FLAGS_MASK_C 0x1000 | ||||||
|  | #define CRW_FLAGS_MASK_RSC 0x0f00 | ||||||
|  | #define CRW_FLAGS_MASK_A 0x0080 | ||||||
|  | #define CRW_FLAGS_MASK_ERC 0x003f | ||||||
|  | 
 | ||||||
|  | #define CRW_ERC_INIT 0x02 | ||||||
|  | #define CRW_ERC_IPI  0x04 | ||||||
|  | 
 | ||||||
|  | #define CRW_RSC_SUBCH 0x3 | ||||||
|  | #define CRW_RSC_CHP   0x4 | ||||||
|  | 
 | ||||||
|  | /* I/O interruption code */ | ||||||
|  | typedef struct IOIntCode { | ||||||
|  |     uint32_t subsys_id; | ||||||
|  |     uint32_t intparm; | ||||||
|  |     uint32_t interrupt_id; | ||||||
|  | } QEMU_PACKED IOIntCode; | ||||||
|  | 
 | ||||||
|  | /* schid disintegration */ | ||||||
|  | #define IOINST_SCHID_ONE(_schid)   ((_schid & 0x00010000) >> 16) | ||||||
|  | #define IOINST_SCHID_M(_schid)     ((_schid & 0x00080000) >> 19) | ||||||
|  | #define IOINST_SCHID_CSSID(_schid) ((_schid & 0xff000000) >> 24) | ||||||
|  | #define IOINST_SCHID_SSID(_schid)  ((_schid & 0x00060000) >> 17) | ||||||
|  | #define IOINST_SCHID_NR(_schid)    (_schid & 0x0000ffff) | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); | ||||||
|  | int ioinst_handle_chsc(CPUS390XState *env, 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); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -47,9 +47,29 @@ | |||||||
| 
 | 
 | ||||||
| #define IPA0_DIAG                       0x8300 | #define IPA0_DIAG                       0x8300 | ||||||
| #define IPA0_SIGP                       0xae00 | #define IPA0_SIGP                       0xae00 | ||||||
| #define IPA0_PRIV                       0xb200 | #define IPA0_B2                         0xb200 | ||||||
|  | #define IPA0_B9                         0xb900 | ||||||
|  | #define IPA0_EB                         0xeb00 | ||||||
| 
 | 
 | ||||||
| #define PRIV_SCLP_CALL                  0x20 | #define PRIV_SCLP_CALL                  0x20 | ||||||
|  | #define PRIV_CSCH                       0x30 | ||||||
|  | #define PRIV_HSCH                       0x31 | ||||||
|  | #define PRIV_MSCH                       0x32 | ||||||
|  | #define PRIV_SSCH                       0x33 | ||||||
|  | #define PRIV_STSCH                      0x34 | ||||||
|  | #define PRIV_TSCH                       0x35 | ||||||
|  | #define PRIV_TPI                        0x36 | ||||||
|  | #define PRIV_SAL                        0x37 | ||||||
|  | #define PRIV_RSCH                       0x38 | ||||||
|  | #define PRIV_STCRW                      0x39 | ||||||
|  | #define PRIV_STCPS                      0x3a | ||||||
|  | #define PRIV_RCHP                       0x3b | ||||||
|  | #define PRIV_SCHM                       0x3c | ||||||
|  | #define PRIV_CHSC                       0x5f | ||||||
|  | #define PRIV_SIGA                       0x74 | ||||||
|  | #define PRIV_XSCH                       0x76 | ||||||
|  | #define PRIV_SQBS                       0x8a | ||||||
|  | #define PRIV_EQBS                       0x9c | ||||||
| #define DIAG_KVM_HYPERCALL              0x500 | #define DIAG_KVM_HYPERCALL              0x500 | ||||||
| #define DIAG_KVM_BREAKPOINT             0x501 | #define DIAG_KVM_BREAKPOINT             0x501 | ||||||
| 
 | 
 | ||||||
| @ -380,10 +400,123 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int handle_priv(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) | 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; | ||||||
|  | 
 | ||||||
|  |     if (ipa0 != 0xb2) { | ||||||
|  |         /* Not handled for now. */ | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     cpu_synchronize_state(env); | ||||||
|  |     switch (ipa1) { | ||||||
|  |     case PRIV_XSCH: | ||||||
|  |         r = ioinst_handle_xsch(env, env->regs[1]); | ||||||
|  |         break; | ||||||
|  |     case PRIV_CSCH: | ||||||
|  |         r = ioinst_handle_csch(env, env->regs[1]); | ||||||
|  |         break; | ||||||
|  |     case PRIV_HSCH: | ||||||
|  |         r = ioinst_handle_hsch(env, env->regs[1]); | ||||||
|  |         break; | ||||||
|  |     case PRIV_MSCH: | ||||||
|  |         r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb); | ||||||
|  |         break; | ||||||
|  |     case PRIV_SSCH: | ||||||
|  |         r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb); | ||||||
|  |         break; | ||||||
|  |     case PRIV_STCRW: | ||||||
|  |         r = ioinst_handle_stcrw(env, run->s390_sieic.ipb); | ||||||
|  |         break; | ||||||
|  |     case PRIV_STSCH: | ||||||
|  |         r = ioinst_handle_stsch(env, 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); | ||||||
|  |         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); | ||||||
|  |         break; | ||||||
|  |     case PRIV_RSCH: | ||||||
|  |         r = ioinst_handle_rsch(env, env->regs[1]); | ||||||
|  |         break; | ||||||
|  |     case PRIV_RCHP: | ||||||
|  |         r = ioinst_handle_rchp(env, 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]); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         r = -1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (r >= 0) { | ||||||
|  |         if (!no_cc) { | ||||||
|  |             setcc(cpu, r); | ||||||
|  |         } | ||||||
|  |         r = 0; | ||||||
|  |     } else if (r < -1) { | ||||||
|  |         r = 0; | ||||||
|  |     } | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int is_ioinst(uint8_t ipa0, uint8_t ipa1, uint8_t ipb) | ||||||
|  | { | ||||||
|  |     int ret = 0; | ||||||
|  |     uint16_t ipa = (ipa0 << 8) | ipa1; | ||||||
|  | 
 | ||||||
|  |     switch (ipa) { | ||||||
|  |     case IPA0_B2 | PRIV_CSCH: | ||||||
|  |     case IPA0_B2 | PRIV_HSCH: | ||||||
|  |     case IPA0_B2 | PRIV_MSCH: | ||||||
|  |     case IPA0_B2 | PRIV_SSCH: | ||||||
|  |     case IPA0_B2 | PRIV_STSCH: | ||||||
|  |     case IPA0_B2 | PRIV_TPI: | ||||||
|  |     case IPA0_B2 | PRIV_SAL: | ||||||
|  |     case IPA0_B2 | PRIV_RSCH: | ||||||
|  |     case IPA0_B2 | PRIV_STCRW: | ||||||
|  |     case IPA0_B2 | PRIV_STCPS: | ||||||
|  |     case IPA0_B2 | PRIV_RCHP: | ||||||
|  |     case IPA0_B2 | PRIV_SCHM: | ||||||
|  |     case IPA0_B2 | PRIV_CHSC: | ||||||
|  |     case IPA0_B2 | PRIV_SIGA: | ||||||
|  |     case IPA0_B2 | PRIV_XSCH: | ||||||
|  |     case IPA0_B9 | PRIV_EQBS: | ||||||
|  |     case IPA0_EB | PRIV_SQBS: | ||||||
|  |         ret = 1; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int handle_priv(S390CPU *cpu, struct kvm_run *run, | ||||||
|  |                        uint8_t ipa0, uint8_t ipa1) | ||||||
| { | { | ||||||
|     int r = 0; |     int r = 0; | ||||||
|     uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; |     uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; | ||||||
|  |     uint8_t ipb = run->s390_sieic.ipb & 0xff; | ||||||
| 
 | 
 | ||||||
|     dprintf("KVM: PRIV: %d\n", ipa1); |     dprintf("KVM: PRIV: %d\n", ipa1); | ||||||
|     switch (ipa1) { |     switch (ipa1) { | ||||||
| @ -391,8 +524,16 @@ static int handle_priv(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) | |||||||
|             r = kvm_sclp_service_call(cpu, run, ipbh0); |             r = kvm_sclp_service_call(cpu, run, ipbh0); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|  |             if (is_ioinst(ipa0, ipa1, ipb)) { | ||||||
|  |                 r = kvm_handle_css_inst(cpu, run, ipa0, ipa1, ipb); | ||||||
|  |                 if (r == -1) { | ||||||
|  |                     setcc(cpu, 3); | ||||||
|  |                     r = 0; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|                 dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); |                 dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); | ||||||
|                 r = -1; |                 r = -1; | ||||||
|  |             } | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -533,8 +674,10 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) | |||||||
| 
 | 
 | ||||||
|     dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb); |     dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb); | ||||||
|     switch (ipa0) { |     switch (ipa0) { | ||||||
|         case IPA0_PRIV: |     case IPA0_B2: | ||||||
|             r = handle_priv(cpu, run, ipa1); |     case IPA0_B9: | ||||||
|  |     case IPA0_EB: | ||||||
|  |         r = handle_priv(cpu, run, ipa0 >> 8, ipa1); | ||||||
|         break; |         break; | ||||||
|     case IPA0_DIAG: |     case IPA0_DIAG: | ||||||
|         r = handle_diag(env, run, ipb_code); |         r = handle_diag(env, run, ipb_code); | ||||||
| @ -600,6 +743,43 @@ static int handle_intercept(S390CPU *cpu) | |||||||
|     return r; |     return r; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int handle_tsch(S390CPU *cpu) | ||||||
|  | { | ||||||
|  |     CPUS390XState *env = &cpu->env; | ||||||
|  |     CPUState *cs = CPU(cpu); | ||||||
|  |     struct kvm_run *run = cs->kvm_run; | ||||||
|  |     int ret; | ||||||
|  | 
 | ||||||
|  |     cpu_synchronize_state(env); | ||||||
|  |     ret = ioinst_handle_tsch(env, env->regs[1], run->s390_tsch.ipb); | ||||||
|  |     if (ret >= 0) { | ||||||
|  |         /* Success; set condition code. */ | ||||||
|  |         setcc(cpu, ret); | ||||||
|  |         ret = 0; | ||||||
|  |     } else if (ret < -1) { | ||||||
|  |         /*
 | ||||||
|  |          * Failure. | ||||||
|  |          * If an I/O interrupt had been dequeued, we have to reinject it. | ||||||
|  |          */ | ||||||
|  |         if (run->s390_tsch.dequeued) { | ||||||
|  |             uint16_t subchannel_id = run->s390_tsch.subchannel_id; | ||||||
|  |             uint16_t subchannel_nr = run->s390_tsch.subchannel_nr; | ||||||
|  |             uint32_t io_int_parm = run->s390_tsch.io_int_parm; | ||||||
|  |             uint32_t io_int_word = run->s390_tsch.io_int_word; | ||||||
|  |             uint32_t type = ((subchannel_id & 0xff00) << 24) | | ||||||
|  |                 ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); | ||||||
|  | 
 | ||||||
|  |             kvm_s390_interrupt_internal(cpu, type, | ||||||
|  |                                         ((uint32_t)subchannel_id << 16) | ||||||
|  |                                         | subchannel_nr, | ||||||
|  |                                         ((uint64_t)io_int_parm << 32) | ||||||
|  |                                         | io_int_word, 1); | ||||||
|  |         } | ||||||
|  |         ret = 0; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | ||||||
| { | { | ||||||
|     S390CPU *cpu = S390_CPU(cs); |     S390CPU *cpu = S390_CPU(cs); | ||||||
| @ -612,6 +792,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | |||||||
|         case KVM_EXIT_S390_RESET: |         case KVM_EXIT_S390_RESET: | ||||||
|             qemu_system_reset_request(); |             qemu_system_reset_request(); | ||||||
|             break; |             break; | ||||||
|  |         case KVM_EXIT_S390_TSCH: | ||||||
|  |             ret = handle_tsch(cpu); | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|             fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason); |             fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason); | ||||||
|             break; |             break; | ||||||
| @ -637,3 +820,33 @@ int kvm_arch_on_sigbus(int code, void *addr) | |||||||
| { | { | ||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, | ||||||
|  |                            uint16_t subchannel_nr, uint32_t io_int_parm, | ||||||
|  |                            uint32_t io_int_word) | ||||||
|  | { | ||||||
|  |     uint32_t type; | ||||||
|  | 
 | ||||||
|  |     type = ((subchannel_id & 0xff00) << 24) | | ||||||
|  |         ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); | ||||||
|  |     kvm_s390_interrupt_internal(cpu, type, | ||||||
|  |                                 ((uint32_t)subchannel_id << 16) | subchannel_nr, | ||||||
|  |                                 ((uint64_t)io_int_parm << 32) | io_int_word, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void kvm_s390_crw_mchk(S390CPU *cpu) | ||||||
|  | { | ||||||
|  |     kvm_s390_interrupt_internal(cpu, KVM_S390_MCHK, 1 << 28, | ||||||
|  |                                 0x00400f1d40330000, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void kvm_s390_enable_css_support(S390CPU *cpu) | ||||||
|  | { | ||||||
|  |     struct kvm_enable_cap cap = {}; | ||||||
|  |     int r; | ||||||
|  | 
 | ||||||
|  |     /* Activate host kernel channel subsystem support. */ | ||||||
|  |     cap.cap = KVM_CAP_S390_CSS_SUPPORT; | ||||||
|  |     r = kvm_vcpu_ioctl(CPU(cpu), KVM_ENABLE_CAP, &cap); | ||||||
|  |     assert(r == 0); | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								trace-events
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								trace-events
									
									
									
									
									
								
							| @ -1072,3 +1072,21 @@ xics_ics_eoi(int nr) "ics_eoi: irq %#x" | |||||||
| hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" | hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" | ||||||
| hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 | hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 | ||||||
| hbitmap_set(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 | hbitmap_set(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 | ||||||
|  | 
 | ||||||
|  | # target-s390x/ioinst.c | ||||||
|  | ioinst(const char *insn) "IOINST: %s" | ||||||
|  | ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" | ||||||
|  | ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" | ||||||
|  | ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command %04x, len %04x" | ||||||
|  | 
 | ||||||
|  | # hw/s390x/css.c | ||||||
|  | css_enable_facility(const char *facility) "CSS: enable %s" | ||||||
|  | css_crw(uint8_t rsc, uint8_t erc, uint16_t rsid, const char *chained) "CSS: queueing crw: rsc=%x, erc=%x, rsid=%x %s" | ||||||
|  | css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02x (type %02x)" | ||||||
|  | css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s" | ||||||
|  | css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" | ||||||
|  | css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" | ||||||
|  | 
 | ||||||
|  | # hw/s390x/virtio-ccw.c | ||||||
|  | virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" | ||||||
|  | virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)" | ||||||
|  | |||||||
							
								
								
									
										52
									
								
								vl.c
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								vl.c
									
									
									
									
									
								
							| @ -176,6 +176,7 @@ int main(int argc, char **argv) | |||||||
| #define DEFAULT_RAM_SIZE 128 | #define DEFAULT_RAM_SIZE 128 | ||||||
| 
 | 
 | ||||||
| #define MAX_VIRTIO_CONSOLES 1 | #define MAX_VIRTIO_CONSOLES 1 | ||||||
|  | #define MAX_SCLP_CONSOLES 1 | ||||||
| 
 | 
 | ||||||
| static const char *data_dir; | static const char *data_dir; | ||||||
| const char *bios_name = NULL; | const char *bios_name = NULL; | ||||||
| @ -203,6 +204,7 @@ int no_quit = 0; | |||||||
| CharDriverState *serial_hds[MAX_SERIAL_PORTS]; | CharDriverState *serial_hds[MAX_SERIAL_PORTS]; | ||||||
| CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; | CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; | ||||||
| CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; | CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; | ||||||
|  | CharDriverState *sclp_hds[MAX_SCLP_CONSOLES]; | ||||||
| int win2k_install_hack = 0; | int win2k_install_hack = 0; | ||||||
| int singlestep = 0; | int singlestep = 0; | ||||||
| int smp_cpus = 1; | int smp_cpus = 1; | ||||||
| @ -271,6 +273,7 @@ static int tcg_tb_size; | |||||||
| static int default_serial = 1; | static int default_serial = 1; | ||||||
| static int default_parallel = 1; | static int default_parallel = 1; | ||||||
| static int default_virtcon = 1; | static int default_virtcon = 1; | ||||||
|  | static int default_sclp = 1; | ||||||
| static int default_monitor = 1; | static int default_monitor = 1; | ||||||
| static int default_floppy = 1; | static int default_floppy = 1; | ||||||
| static int default_cdrom = 1; | static int default_cdrom = 1; | ||||||
| @ -2340,6 +2343,7 @@ struct device_config { | |||||||
|         DEV_VIRTCON,   /* -virtioconsole */ |         DEV_VIRTCON,   /* -virtioconsole */ | ||||||
|         DEV_DEBUGCON,  /* -debugcon */ |         DEV_DEBUGCON,  /* -debugcon */ | ||||||
|         DEV_GDB,       /* -gdb, -s */ |         DEV_GDB,       /* -gdb, -s */ | ||||||
|  |         DEV_SCLP,      /* s390 sclp */ | ||||||
|     } type; |     } type; | ||||||
|     const char *cmdline; |     const char *cmdline; | ||||||
|     Location loc; |     Location loc; | ||||||
| @ -2458,6 +2462,39 @@ static int virtcon_parse(const char *devname) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int sclp_parse(const char *devname) | ||||||
|  | { | ||||||
|  |     QemuOptsList *device = qemu_find_opts("device"); | ||||||
|  |     static int index = 0; | ||||||
|  |     char label[32]; | ||||||
|  |     QemuOpts *dev_opts; | ||||||
|  | 
 | ||||||
|  |     if (strcmp(devname, "none") == 0) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     if (index == MAX_SCLP_CONSOLES) { | ||||||
|  |         fprintf(stderr, "qemu: too many sclp consoles\n"); | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     assert(arch_type == QEMU_ARCH_S390X); | ||||||
|  | 
 | ||||||
|  |     dev_opts = qemu_opts_create(device, NULL, 0, NULL); | ||||||
|  |     qemu_opt_set(dev_opts, "driver", "sclpconsole"); | ||||||
|  | 
 | ||||||
|  |     snprintf(label, sizeof(label), "sclpcon%d", index); | ||||||
|  |     sclp_hds[index] = qemu_chr_new(label, devname, NULL); | ||||||
|  |     if (!sclp_hds[index]) { | ||||||
|  |         fprintf(stderr, "qemu: could not connect sclp console" | ||||||
|  |                 " to character backend '%s'\n", devname); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     qemu_opt_set(dev_opts, "chardev", label); | ||||||
|  | 
 | ||||||
|  |     index++; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int debugcon_parse(const char *devname) | static int debugcon_parse(const char *devname) | ||||||
| {    | {    | ||||||
|     QemuOpts *opts; |     QemuOpts *opts; | ||||||
| @ -3615,6 +3652,7 @@ int main(int argc, char **argv, char **envp) | |||||||
|                 default_serial = 0; |                 default_serial = 0; | ||||||
|                 default_parallel = 0; |                 default_parallel = 0; | ||||||
|                 default_virtcon = 0; |                 default_virtcon = 0; | ||||||
|  |                 default_sclp = 0; | ||||||
|                 default_monitor = 0; |                 default_monitor = 0; | ||||||
|                 default_net = 0; |                 default_net = 0; | ||||||
|                 default_floppy = 0; |                 default_floppy = 0; | ||||||
| @ -3832,6 +3870,9 @@ int main(int argc, char **argv, char **envp) | |||||||
|     if (!machine->use_virtcon) { |     if (!machine->use_virtcon) { | ||||||
|         default_virtcon = 0; |         default_virtcon = 0; | ||||||
|     } |     } | ||||||
|  |     if (!machine->use_sclp) { | ||||||
|  |         default_sclp = 0; | ||||||
|  |     } | ||||||
|     if (machine->no_floppy) { |     if (machine->no_floppy) { | ||||||
|         default_floppy = 0; |         default_floppy = 0; | ||||||
|     } |     } | ||||||
| @ -3873,11 +3914,16 @@ int main(int argc, char **argv, char **envp) | |||||||
|             add_device_config(DEV_SERIAL, "mon:stdio"); |             add_device_config(DEV_SERIAL, "mon:stdio"); | ||||||
|         } else if (default_virtcon && default_monitor) { |         } else if (default_virtcon && default_monitor) { | ||||||
|             add_device_config(DEV_VIRTCON, "mon:stdio"); |             add_device_config(DEV_VIRTCON, "mon:stdio"); | ||||||
|  |         } else if (default_sclp && default_monitor) { | ||||||
|  |             add_device_config(DEV_SCLP, "mon:stdio"); | ||||||
|         } else { |         } else { | ||||||
|             if (default_serial) |             if (default_serial) | ||||||
|                 add_device_config(DEV_SERIAL, "stdio"); |                 add_device_config(DEV_SERIAL, "stdio"); | ||||||
|             if (default_virtcon) |             if (default_virtcon) | ||||||
|                 add_device_config(DEV_VIRTCON, "stdio"); |                 add_device_config(DEV_VIRTCON, "stdio"); | ||||||
|  |             if (default_sclp) { | ||||||
|  |                 add_device_config(DEV_SCLP, "stdio"); | ||||||
|  |             } | ||||||
|             if (default_monitor) |             if (default_monitor) | ||||||
|                 monitor_parse("stdio", "readline"); |                 monitor_parse("stdio", "readline"); | ||||||
|         } |         } | ||||||
| @ -3890,6 +3936,9 @@ int main(int argc, char **argv, char **envp) | |||||||
|             monitor_parse("vc:80Cx24C", "readline"); |             monitor_parse("vc:80Cx24C", "readline"); | ||||||
|         if (default_virtcon) |         if (default_virtcon) | ||||||
|             add_device_config(DEV_VIRTCON, "vc:80Cx24C"); |             add_device_config(DEV_VIRTCON, "vc:80Cx24C"); | ||||||
|  |         if (default_sclp) { | ||||||
|  |             add_device_config(DEV_SCLP, "vc:80Cx24C"); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     socket_init(); |     socket_init(); | ||||||
| @ -4060,6 +4109,9 @@ int main(int argc, char **argv, char **envp) | |||||||
|         exit(1); |         exit(1); | ||||||
|     if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) |     if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) | ||||||
|         exit(1); |         exit(1); | ||||||
|  |     if (foreach_device_config(DEV_SCLP, sclp_parse) < 0) { | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|     if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) |     if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) | ||||||
|         exit(1); |         exit(1); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Anthony Liguori
						Anthony Liguori