changeset 529:3879c28965b4

cp: implement the generic part of SSCH The handler takes care of everything but stopping just short of executing the CCW program. That part is virtual device type dependent (dedicated devices need to do CCW translation, minidisks perform a similar translation, spool devices emulate the channel & device behavior, and console devices tie into the console code). Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Mon, 25 Apr 2011 22:45:28 -0400
parents 737dce57f60d
children 94dc1afb9214
files cp/include/vdevice.h cp/shell/instruction_priv.c
diffstat 2 files changed, 111 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/cp/include/vdevice.h	Mon Apr 25 22:20:53 2011 -0400
+++ b/cp/include/vdevice.h	Mon Apr 25 22:45:28 2011 -0400
@@ -35,8 +35,11 @@
 		} dedicate;
 	} u;
 
+	u8 sense;			/* sense info */
+
 	struct pmcw pmcw;		/* path info */
 	struct scsw scsw;		/* subchannel-status */
+	struct orb orb;
 };
 
 extern int alloc_virt_dev(struct virt_sys *sys,
--- a/cp/shell/instruction_priv.c	Mon Apr 25 22:20:53 2011 -0400
+++ b/cp/shell/instruction_priv.c	Mon Apr 25 22:45:28 2011 -0400
@@ -135,8 +135,114 @@
 
 static int handle_ssch(struct virt_sys *sys)
 {
-	con_printf(sys->con, "SSCH handler\n");
-	return -ENOMEM;
+	struct virt_cpu *cpu = sys->task->cpu;
+	u64 len;
+
+	u64 r1   = __guest_gpr(cpu, 1);
+	u64 addr = RAW_S_1(cpu);
+
+	struct orb gorb;
+	struct virt_device *vdev, *vdev_cur;
+	enum directory_vdevtype vtype;
+	int ret = 0;
+
+	/* sch number must be: X'0001____' */
+	if ((r1 & 0xffff0000) != 0x00010000) {
+		queue_prog_exception(sys, PROG_OPERAND, r1);
+		goto out;
+	}
+
+	/* schib must be word-aligned */
+	if (addr & 0x3) {
+		queue_prog_exception(sys, PROG_SPEC, addr);
+		goto out;
+	}
+
+	/* find the virtual device */
+	vdev = NULL;
+	list_for_each_entry(vdev_cur, &sys->virt_devs, devices) {
+		if (vdev_cur->sch == (u32) r1) {
+			vdev = vdev_cur;
+			break;
+		}
+	}
+
+	/* There's no virtual device with this sch number; CC=3 */
+	if (!vdev) {
+		cpu->sie_cb.gpsw.cc = 3;
+		goto out;
+	}
+
+	/* copy the ORB from the guest */
+	len = sizeof(struct orb);
+	ret = memcpy_from_guest(addr, &gorb, &len);
+	if (ret) {
+		if (ret == -EFAULT) {
+			ret = 0;
+			queue_prog_exception(sys, PROG_ADDR, addr);
+		}
+
+		goto out;
+	}
+
+	mutex_lock(&vdev->lock);
+
+	/*
+	 * Condition code 1 is set, and no other action is taken, when the
+	 *   subchannel is status pending. (See “Status Control (SC)” on
+	 *   page 16-16.)
+	 */
+	if (vdev->scsw.sc & SC_STATUS) {
+		mutex_unlock(&vdev->lock);
+		cpu->sie_cb.gpsw.cc = 1;
+		goto out;
+	}
+
+	/*
+	 * Condition code 2 is set, and no other action is taken, when a
+	 *   clear, halt, or start function is in progress at the
+	 *   subchannel.  (See “Function Control (FC)” on page 16-12.)
+	 */
+	if (vdev->scsw.fc & (FC_START | FC_HALT | FC_CLEAR)) {
+		mutex_unlock(&vdev->lock);
+		cpu->sie_cb.gpsw.cc = 2;
+		goto out;
+	}
+
+	/*
+	 * Let's set up the subchannel status for the I/O
+	 */
+	memset(&vdev->scsw, 0, sizeof(struct scsw));
+	vdev->scsw.fc = FC_START;
+	vdev->scsw.ac = AC_START;
+	vdev->pmcw.interrupt_param = gorb.param;
+	memcpy(&vdev->orb, &gorb, sizeof(struct orb));
+
+	vtype = vdev->vtype;
+
+	mutex_unlock(&vdev->lock);
+
+	switch (vtype) {
+		case VDEV_DED:
+			ret = -EINVAL;
+			break;
+
+		case VDEV_SPOOL:
+			ret = -EINVAL;
+			break;
+
+		default:
+			goto stop;
+	}
+
+out:
+	return ret;
+
+stop:
+	cpu->state = GUEST_STOPPED;
+	con_printf(sys->con, "SSCH handler - CPU STOPPED\n");
+
+	return 0;
 }
 
 static int handle_stsch(struct virt_sys *sys)