Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37db8985 authored by Halil Pasic's avatar Halil Pasic Committed by Heiko Carstens
Browse files

s390/cio: add basic protected virtualization support



As virtio-ccw devices are channel devices, we need to use the
dma area within the common I/O layer for any communication with
the hypervisor.

Note that we do not need to use that area for control blocks
directly referenced by instructions, e.g. the orb.

It handles neither QDIO in the common code, nor any device type specific
stuff (like channel programs constructed by the DASD driver).

An interesting side effect is that virtio structures are now going to
get allocated in 31 bit addressable storage.

Signed-off-by: default avatarHalil Pasic <pasic@linux.ibm.com>
Reviewed-by: default avatarSebastian Ott <sebott@linux.ibm.com>
Reviewed-by: default avatarCornelia Huck <cohuck@redhat.com>
Reviewed-by: default avatarMichael Mueller <mimu@linux.ibm.com>
Tested-by: default avatarMichael Mueller <mimu@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
parent bb99332a
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -226,6 +226,10 @@ extern int ccw_device_enable_console(struct ccw_device *);
extern void ccw_device_wait_idle(struct ccw_device *);
extern int ccw_device_force_console(struct ccw_device *);

extern void *ccw_device_dma_zalloc(struct ccw_device *cdev, size_t size);
extern void ccw_device_dma_free(struct ccw_device *cdev,
				void *cpu_addr, size_t size);

int ccw_device_siosl(struct ccw_device *);

extern void ccw_device_get_schid(struct ccw_device *, struct subchannel_id *);
+5 −4
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ static void ccwreq_stop(struct ccw_device *cdev, int rc)
		return;
	req->done = 1;
	ccw_device_set_timeout(cdev, 0);
	memset(&cdev->private->irb, 0, sizeof(struct irb));
	memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
	if (rc && rc != -ENODEV && req->drc)
		rc = req->drc;
	req->callback(cdev, req->data, rc);
@@ -86,7 +86,7 @@ static void ccwreq_do(struct ccw_device *cdev)
			continue;
		}
		/* Perform start function. */
		memset(&cdev->private->irb, 0, sizeof(struct irb));
		memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
		rc = cio_start(sch, cp, (u8) req->mask);
		if (rc == 0) {
			/* I/O started successfully. */
@@ -169,7 +169,7 @@ int ccw_request_cancel(struct ccw_device *cdev)
 */
static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
{
	struct irb *irb = &cdev->private->irb;
	struct irb *irb = &cdev->private->dma_area->irb;
	struct cmd_scsw *scsw = &irb->scsw.cmd;
	enum uc_todo todo;

@@ -187,7 +187,8 @@ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
		CIO_TRACE_EVENT(2, "sensedata");
		CIO_HEX_EVENT(2, &cdev->private->dev_id,
			      sizeof(struct ccw_dev_id));
		CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT);
		CIO_HEX_EVENT(2, &cdev->private->dma_area->irb.ecw,
			      SENSE_MAX_COUNT);
		/* Check for command reject. */
		if (irb->ecw[0] & SNS0_CMD_REJECT)
			return IO_REJECTED;
+57 −11
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/timer.h>
#include <linux/kernel_stat.h>
#include <linux/sched/signal.h>
#include <linux/dma-mapping.h>

#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -687,6 +688,9 @@ ccw_device_release(struct device *dev)
	struct ccw_device *cdev;

	cdev = to_ccwdev(dev);
	cio_gp_dma_free(cdev->private->dma_pool, cdev->private->dma_area,
			sizeof(*cdev->private->dma_area));
	cio_gp_dma_destroy(cdev->private->dma_pool, &cdev->dev);
	/* Release reference of parent subchannel. */
	put_device(cdev->dev.parent);
	kfree(cdev->private);
@@ -696,15 +700,33 @@ ccw_device_release(struct device *dev)
static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
{
	struct ccw_device *cdev;
	struct gen_pool *dma_pool;

	cdev  = kzalloc(sizeof(*cdev), GFP_KERNEL);
	if (cdev) {
	if (!cdev)
		goto err_cdev;
	cdev->private = kzalloc(sizeof(struct ccw_device_private),
				GFP_KERNEL | GFP_DMA);
		if (cdev->private)
	if (!cdev->private)
		goto err_priv;
	cdev->dev.coherent_dma_mask = sch->dev.coherent_dma_mask;
	cdev->dev.dma_mask = &cdev->dev.coherent_dma_mask;
	dma_pool = cio_gp_dma_create(&cdev->dev, 1);
	if (!dma_pool)
		goto err_dma_pool;
	cdev->private->dma_pool = dma_pool;
	cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev,
					sizeof(*cdev->private->dma_area));
	if (!cdev->private->dma_area)
		goto err_dma_area;
	return cdev;
	}
err_dma_area:
	cio_gp_dma_destroy(dma_pool, &cdev->dev);
err_dma_pool:
	kfree(cdev->private);
err_priv:
	kfree(cdev);
err_cdev:
	return ERR_PTR(-ENOMEM);
}

@@ -1062,6 +1084,14 @@ static int io_subchannel_probe(struct subchannel *sch)
	if (!io_priv)
		goto out_schedule;

	io_priv->dma_area = dma_alloc_coherent(&sch->dev,
				sizeof(*io_priv->dma_area),
				&io_priv->dma_area_dma, GFP_KERNEL);
	if (!io_priv->dma_area) {
		kfree(io_priv);
		goto out_schedule;
	}

	set_io_private(sch, io_priv);
	css_schedule_eval(sch->schid);
	return 0;
@@ -1088,6 +1118,8 @@ static int io_subchannel_remove(struct subchannel *sch)
	set_io_private(sch, NULL);
	spin_unlock_irq(sch->lock);
out_free:
	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
			  io_priv->dma_area, io_priv->dma_area_dma);
	kfree(io_priv);
	sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
	return 0;
@@ -1593,13 +1625,19 @@ struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv)
		return ERR_CAST(sch);

	io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA);
	if (!io_priv) {
		put_device(&sch->dev);
		return ERR_PTR(-ENOMEM);
	}
	if (!io_priv)
		goto err_priv;
	io_priv->dma_area = dma_alloc_coherent(&sch->dev,
				sizeof(*io_priv->dma_area),
				&io_priv->dma_area_dma, GFP_KERNEL);
	if (!io_priv->dma_area)
		goto err_dma_area;
	set_io_private(sch, io_priv);
	cdev = io_subchannel_create_ccwdev(sch);
	if (IS_ERR(cdev)) {
		dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
				  io_priv->dma_area, io_priv->dma_area_dma);
		set_io_private(sch, NULL);
		put_device(&sch->dev);
		kfree(io_priv);
		return cdev;
@@ -1607,6 +1645,12 @@ struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv)
	cdev->drv = drv;
	ccw_device_set_int_class(cdev);
	return cdev;

err_dma_area:
	kfree(io_priv);
err_priv:
	put_device(&sch->dev);
	return ERR_PTR(-ENOMEM);
}

void __init ccw_device_destroy_console(struct ccw_device *cdev)
@@ -1617,6 +1661,8 @@ void __init ccw_device_destroy_console(struct ccw_device *cdev)
	set_io_private(sch, NULL);
	put_device(&sch->dev);
	put_device(&cdev->dev);
	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
			  io_priv->dma_area, io_priv->dma_area_dma);
	kfree(io_priv);
}

+29 −20
Original line number Diff line number Diff line
@@ -67,8 +67,10 @@ static void ccw_timeout_log(struct ccw_device *cdev)
			       sizeof(struct tcw), 0);
	} else {
		printk(KERN_WARNING "cio: orb indicates command mode\n");
		if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
		    (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
		if ((void *)(addr_t)orb->cmd.cpa ==
		    &private->dma_area->sense_ccw ||
		    (void *)(addr_t)orb->cmd.cpa ==
		    cdev->private->dma_area->iccws)
			printk(KERN_WARNING "cio: last channel program "
			       "(intern):\n");
		else
@@ -143,18 +145,22 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
void ccw_device_update_sense_data(struct ccw_device *cdev)
{
	memset(&cdev->id, 0, sizeof(cdev->id));
	cdev->id.cu_type   = cdev->private->senseid.cu_type;
	cdev->id.cu_model  = cdev->private->senseid.cu_model;
	cdev->id.dev_type  = cdev->private->senseid.dev_type;
	cdev->id.dev_model = cdev->private->senseid.dev_model;
	cdev->id.cu_type = cdev->private->dma_area->senseid.cu_type;
	cdev->id.cu_model = cdev->private->dma_area->senseid.cu_model;
	cdev->id.dev_type = cdev->private->dma_area->senseid.dev_type;
	cdev->id.dev_model = cdev->private->dma_area->senseid.dev_model;
}

int ccw_device_test_sense_data(struct ccw_device *cdev)
{
	return cdev->id.cu_type == cdev->private->senseid.cu_type &&
		cdev->id.cu_model == cdev->private->senseid.cu_model &&
		cdev->id.dev_type == cdev->private->senseid.dev_type &&
		cdev->id.dev_model == cdev->private->senseid.dev_model;
	return cdev->id.cu_type ==
		cdev->private->dma_area->senseid.cu_type &&
		cdev->id.cu_model ==
		cdev->private->dma_area->senseid.cu_model &&
		cdev->id.dev_type ==
		cdev->private->dma_area->senseid.dev_type &&
		cdev->id.dev_model ==
		cdev->private->dma_area->senseid.dev_model;
}

/*
@@ -342,7 +348,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
		cio_disable_subchannel(sch);

	/* Reset device status. */
	memset(&cdev->private->irb, 0, sizeof(struct irb));
	memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));

	cdev->private->state = state;

@@ -509,13 +515,14 @@ void ccw_device_verify_done(struct ccw_device *cdev, int err)
		ccw_device_done(cdev, DEV_STATE_ONLINE);
		/* Deliver fake irb to device driver, if needed. */
		if (cdev->private->flags.fake_irb) {
			create_fake_irb(&cdev->private->irb,
			create_fake_irb(&cdev->private->dma_area->irb,
					cdev->private->flags.fake_irb);
			cdev->private->flags.fake_irb = 0;
			if (cdev->handler)
				cdev->handler(cdev, cdev->private->intparm,
					      &cdev->private->irb);
			memset(&cdev->private->irb, 0, sizeof(struct irb));
					      &cdev->private->dma_area->irb);
			memset(&cdev->private->dma_area->irb, 0,
			       sizeof(struct irb));
		}
		ccw_device_report_path_events(cdev);
		ccw_device_handle_broken_paths(cdev);
@@ -672,7 +679,8 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)

	if (scsw_actl(&sch->schib.scsw) != 0 ||
	    (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
	    (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
	    (scsw_stctl(&cdev->private->dma_area->irb.scsw) &
	     SCSW_STCTL_STATUS_PEND)) {
		/*
		 * No final status yet or final status not yet delivered
		 * to the device driver. Can't do path verification now,
@@ -719,7 +727,7 @@ static int ccw_device_call_handler(struct ccw_device *cdev)
	 *  - fast notification was requested (primary status)
	 *  - unsolicited interrupts
	 */
	stctl = scsw_stctl(&cdev->private->irb.scsw);
	stctl = scsw_stctl(&cdev->private->dma_area->irb.scsw);
	ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
		(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
		(stctl == SCSW_STCTL_STATUS_PEND);
@@ -735,9 +743,9 @@ static int ccw_device_call_handler(struct ccw_device *cdev)

	if (cdev->handler)
		cdev->handler(cdev, cdev->private->intparm,
			      &cdev->private->irb);
			      &cdev->private->dma_area->irb);

	memset(&cdev->private->irb, 0, sizeof(struct irb));
	memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
	return 1;
}

@@ -759,7 +767,8 @@ ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
			/* Unit check but no sense data. Need basic sense. */
			if (ccw_device_do_sense(cdev, irb) != 0)
				goto call_handler_unsol;
			memcpy(&cdev->private->irb, irb, sizeof(struct irb));
			memcpy(&cdev->private->dma_area->irb, irb,
			       sizeof(struct irb));
			cdev->private->state = DEV_STATE_W4SENSE;
			cdev->private->intparm = 0;
			return;
@@ -842,7 +851,7 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
	if (scsw_fctl(&irb->scsw) &
	    (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
		cdev->private->flags.dosense = 0;
		memset(&cdev->private->irb, 0, sizeof(struct irb));
		memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));
		ccw_device_accumulate_irb(cdev, irb);
		goto call_handler;
	}
+11 −9
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
static int diag210_get_dev_info(struct ccw_device *cdev)
{
	struct ccw_dev_id *dev_id = &cdev->private->dev_id;
	struct senseid *senseid = &cdev->private->senseid;
	struct senseid *senseid = &cdev->private->dma_area->senseid;
	struct diag210 diag_data;
	int rc;

@@ -134,8 +134,10 @@ static int diag210_get_dev_info(struct ccw_device *cdev)
static void snsid_init(struct ccw_device *cdev)
{
	cdev->private->flags.esid = 0;
	memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
	cdev->private->senseid.cu_type = 0xffff;

	memset(&cdev->private->dma_area->senseid, 0,
	       sizeof(cdev->private->dma_area->senseid));
	cdev->private->dma_area->senseid.cu_type = 0xffff;
}

/*
@@ -143,16 +145,16 @@ static void snsid_init(struct ccw_device *cdev)
 */
static int snsid_check(struct ccw_device *cdev, void *data)
{
	struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
	struct cmd_scsw *scsw = &cdev->private->dma_area->irb.scsw.cmd;
	int len = sizeof(struct senseid) - scsw->count;

	/* Check for incomplete SENSE ID data. */
	if (len < SENSE_ID_MIN_LEN)
		goto out_restart;
	if (cdev->private->senseid.cu_type == 0xffff)
	if (cdev->private->dma_area->senseid.cu_type == 0xffff)
		goto out_restart;
	/* Check for incompatible SENSE ID data. */
	if (cdev->private->senseid.reserved != 0xff)
	if (cdev->private->dma_area->senseid.reserved != 0xff)
		return -EOPNOTSUPP;
	/* Check for extended-identification information. */
	if (len > SENSE_ID_BASIC_LEN)
@@ -170,7 +172,7 @@ static int snsid_check(struct ccw_device *cdev, void *data)
static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
{
	struct ccw_dev_id *id = &cdev->private->dev_id;
	struct senseid *senseid = &cdev->private->senseid;
	struct senseid *senseid = &cdev->private->dma_area->senseid;
	int vm = 0;

	if (rc && MACHINE_IS_VM) {
@@ -200,7 +202,7 @@ void ccw_device_sense_id_start(struct ccw_device *cdev)
{
	struct subchannel *sch = to_subchannel(cdev->dev.parent);
	struct ccw_request *req = &cdev->private->req;
	struct ccw1 *cp = cdev->private->iccws;
	struct ccw1 *cp = cdev->private->dma_area->iccws;

	CIO_TRACE_EVENT(4, "snsid");
	CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
@@ -208,7 +210,7 @@ void ccw_device_sense_id_start(struct ccw_device *cdev)
	snsid_init(cdev);
	/* Channel program setup. */
	cp->cmd_code	= CCW_CMD_SENSE_ID;
	cp->cda		= (u32) (addr_t) &cdev->private->senseid;
	cp->cda		= (u32) (addr_t) &cdev->private->dma_area->senseid;
	cp->count	= sizeof(struct senseid);
	cp->flags	= CCW_FLAG_SLI;
	/* Request setup. */
Loading