s390x/css: ccw translation infrastructure

Implement a basic infrastructure of handling channel I/O instruction
interception for passed through subchannels:
1. Branch the code path of instruction interception handling by
   SubChannel type.
2. For a passed-through subchannel, issue the ORB to kernel to do ccw
   translation and perform an I/O operation.
3. Assign different condition code based on the I/O result, or
   trigger a program check.

Signed-off-by: Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
Signed-off-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
Message-Id: <20170517004813.58227-12-bjsdjshi@linux.vnet.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
This commit is contained in:
Xiao Feng Ren 2017-05-17 02:48:11 +02:00 committed by Cornelia Huck
parent 8ca2b376b4
commit bab482d740
5 changed files with 109 additions and 6 deletions

View File

@ -524,7 +524,7 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr,
return ret; return ret;
} }
static void sch_handle_start_func(SubchDev *sch, ORB *orb) static void sch_handle_start_func_virtual(SubchDev *sch, ORB *orb)
{ {
PMCW *p = &sch->curr_status.pmcw; PMCW *p = &sch->curr_status.pmcw;
@ -626,13 +626,58 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
} }
static int sch_handle_start_func_passthrough(SubchDev *sch, ORB *orb)
{
PMCW *p = &sch->curr_status.pmcw;
SCSW *s = &sch->curr_status.scsw;
int ret;
if (!(s->ctrl & SCSW_ACTL_SUSP)) {
assert(orb != NULL);
p->intparm = orb->intparm;
}
/*
* Only support prefetch enable mode.
* Only support 64bit addressing idal.
*/
if (!(orb->ctrl0 & ORB_CTRL0_MASK_PFCH) ||
!(orb->ctrl0 & ORB_CTRL0_MASK_C64)) {
return -EINVAL;
}
ret = s390_ccw_cmd_request(orb, s, sch->driver_data);
switch (ret) {
/* Currently we don't update control block and just return the cc code. */
case 0:
break;
case -EBUSY:
break;
case -ENODEV:
break;
case -EACCES:
/* Let's reflect an inaccessible host device by cc 3. */
ret = -ENODEV;
break;
default:
/*
* All other return codes will trigger a program check,
* or set cc to 1.
*/
break;
};
return ret;
}
/* /*
* On real machines, this would run asynchronously to the main vcpus. * On real machines, this would run asynchronously to the main vcpus.
* We might want to make some parts of the ssch handling (interpreting * We might want to make some parts of the ssch handling (interpreting
* read/writes) asynchronous later on if we start supporting more than * read/writes) asynchronous later on if we start supporting more than
* our current very simple devices. * our current very simple devices.
*/ */
static void do_subchannel_work(SubchDev *sch, ORB *orb) int do_subchannel_work_virtual(SubchDev *sch, ORB *orb)
{ {
SCSW *s = &sch->curr_status.scsw; SCSW *s = &sch->curr_status.scsw;
@ -643,12 +688,45 @@ static void do_subchannel_work(SubchDev *sch, ORB *orb)
sch_handle_halt_func(sch); sch_handle_halt_func(sch);
} else if (s->ctrl & SCSW_FCTL_START_FUNC) { } else if (s->ctrl & SCSW_FCTL_START_FUNC) {
/* Triggered by both ssch and rsch. */ /* Triggered by both ssch and rsch. */
sch_handle_start_func(sch, orb); sch_handle_start_func_virtual(sch, orb);
} else { } else {
/* Cannot happen. */ /* Cannot happen. */
return; return 0;
} }
css_inject_io_interrupt(sch); css_inject_io_interrupt(sch);
return 0;
}
int do_subchannel_work_passthrough(SubchDev *sch, ORB *orb)
{
int ret;
SCSW *s = &sch->curr_status.scsw;
if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) {
/* TODO: Clear handling */
sch_handle_clear_func(sch);
ret = 0;
} else if (s->ctrl & SCSW_FCTL_HALT_FUNC) {
/* TODO: Halt handling */
sch_handle_halt_func(sch);
ret = 0;
} else if (s->ctrl & SCSW_FCTL_START_FUNC) {
ret = sch_handle_start_func_passthrough(sch, orb);
} else {
/* Cannot happen. */
return -ENODEV;
}
return ret;
}
static int do_subchannel_work(SubchDev *sch, ORB *orb)
{
if (sch->do_subchannel_work) {
return sch->do_subchannel_work(sch, orb);
} else {
return -EINVAL;
}
} }
static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src)
@ -967,8 +1045,7 @@ int css_do_ssch(SubchDev *sch, ORB *orb)
s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND);
s->flags &= ~SCSW_FLAGS_MASK_PNO; s->flags &= ~SCSW_FLAGS_MASK_PNO;
do_subchannel_work(sch, orb); ret = do_subchannel_work(sch, orb);
ret = 0;
out: out:
return ret; return ret;

View File

@ -18,6 +18,17 @@
#include "hw/s390x/css-bridge.h" #include "hw/s390x/css-bridge.h"
#include "hw/s390x/s390-ccw.h" #include "hw/s390x/s390-ccw.h"
int s390_ccw_cmd_request(ORB *orb, SCSW *scsw, void *data)
{
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(data);
if (cdc->handle_request) {
return cdc->handle_request(orb, scsw, data);
} else {
return -ENOSYS;
}
}
static void s390_ccw_get_dev_info(S390CCWDevice *cdev, static void s390_ccw_get_dev_info(S390CCWDevice *cdev,
char *sysfsdev, char *sysfsdev,
Error **errp) Error **errp)
@ -72,6 +83,7 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp)
goto out_mdevid_free; goto out_mdevid_free;
} }
sch->driver_data = cdev; sch->driver_data = cdev;
sch->do_subchannel_work = do_subchannel_work_passthrough;
ccw_dev->sch = sch; ccw_dev->sch = sch;
ret = css_sch_build_schib(sch, &cdev->hostid); ret = css_sch_build_schib(sch, &cdev->hostid);

View File

@ -701,6 +701,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
sch->disable_cb = virtio_sch_disable_cb; sch->disable_cb = virtio_sch_disable_cb;
sch->id.reserved = 0xff; sch->id.reserved = 0xff;
sch->id.cu_type = VIRTIO_CCW_CU_TYPE; sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
sch->do_subchannel_work = do_subchannel_work_virtual;
ccw_dev->sch = sch; ccw_dev->sch = sch;
dev->indicators = NULL; dev->indicators = NULL;
dev->revision = -1; dev->revision = -1;

View File

@ -91,6 +91,7 @@ struct SubchDev {
/* transport-provided data: */ /* transport-provided data: */
int (*ccw_cb) (SubchDev *, CCW1); int (*ccw_cb) (SubchDev *, CCW1);
void (*disable_cb)(SubchDev *); void (*disable_cb)(SubchDev *);
int (*do_subchannel_work) (SubchDev *, ORB *);
SenseId id; SenseId id;
void *driver_data; void *driver_data;
}; };
@ -148,6 +149,9 @@ void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
void css_generate_css_crws(uint8_t cssid); void css_generate_css_crws(uint8_t cssid);
void css_clear_sei_pending(void); void css_clear_sei_pending(void);
void css_adapter_interrupt(uint8_t isc); void css_adapter_interrupt(uint8_t isc);
int s390_ccw_cmd_request(ORB *orb, SCSW *scsw, void *data);
int do_subchannel_work_virtual(SubchDev *sub, ORB *orb);
int do_subchannel_work_passthrough(SubchDev *sub, ORB *orb);
typedef enum { typedef enum {
CSS_IO_ADAPTER_VIRTIO = 0, CSS_IO_ADAPTER_VIRTIO = 0,

View File

@ -244,6 +244,15 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
case -EBUSY: case -EBUSY:
cc = 2; cc = 2;
break; break;
case -EFAULT:
/*
* TODO:
* I'm wondering whether there is something better
* to do for us here (like setting some device or
* subchannel status).
*/
program_interrupt(env, PGM_ADDRESSING, 4);
return;
case 0: case 0:
cc = 0; cc = 0;
break; break;