Mercurial > hvf > hvf-old
view cp/drivers/spooldev.c @ 618:535aec703236
cp: define a FIXME macro that leaves a sclp message
There are far too many fixmes in the code. Sadly, the compiler simply
discards them. This usually isn't an issue until one accidentally hits a
"weird" bug which just turns out to be an unhandled (but documented) case in
another part of the code. Using a macro instead of a comment will let the
compiler string-ify the text, and then at runtime use SCLP to print it out.
This will immediatelly point at problem areas. So, keep an eye on SCLP from
now on :)
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Tue, 13 Dec 2011 22:20:50 -0500 |
parents | bc89bcb98f74 |
children |
line wrap: on
line source
/* * (C) Copyright 2007-2011 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * This file is released under the GPLv2. See the COPYING file for more * details. */ #include <dat.h> #include <channel.h> #include <vcpu.h> #include <vdevice.h> #include <spool.h> #include <buddy.h> /* * store == 0: iobuf -> storage * store == 1: storage -> iobuf */ static int __xfer_storage(struct virt_sys *sys, struct virt_device *vdev, struct spdev_state *st, struct ccw *ccw, int write) { u64 gaddr, len; int ret; BUG_ON(ccw->flags & (CCW_FLAG_IDA | CCW_FLAG_MIDA)); gaddr = ccw->addr; /* the address has already been checked */ len = ccw->count; if (!len) return 0; if (gaddr > sys->directory->storage_size) { st->sch_status = SC_PROG_CHK; return 0; } if (write) ret = memcpy_from_guest(gaddr, st->iobuf, &len); else ret = memcpy_to_guest(gaddr, st->iobuf, &len); if (ret) st->sch_status = SC_PROT_CHK; st->pos = 0; st->rem = len; return 0; } static void __get_ccw(struct virt_sys *sys, struct spdev_state *st, struct ccw *ccw) { struct ccw0 rawccw; u64 len; int ret; if ((!st->f && (st->addr & 0xff000000)) || (st->f && (st->addr & 0x80000000)) || (st->addr & 0x00000007)) { st->sch_status = SC_PROG_CHK; return; } len = sizeof(struct ccw); ret = memcpy_from_guest(st->addr, &rawccw, &len); if (ret) { con_printf(sys->con, "failed to fetch ccw %d \n", ret); st->sch_status = SC_PROT_CHK; return; } if (!st->f) ccw0_to_ccw1(ccw, &rawccw); else memcpy(ccw, &rawccw, sizeof(struct ccw)); } /* * This fuction behaves as a control unit as described in the principles of * operation. It uses a function pointer to "send the command to the * device." */ int spool_exec(struct virt_sys *sys, struct virt_device *vdev) { struct spdev_state st; struct page *pages; struct ccw ccw; int cnt = 0; pages = alloc_pages(4, ZONE_NORMAL); /* get 64k buf */ if (!pages) { con_printf(sys->con, "out of memory: could not allocate " "channel buffer (64kB)\n"); return -ENOMEM; } st.iobuf = page_to_addr(pages); st.dev_status = 0; st.sch_status = 0; st.cd = 0; st.rem = 0; st.pos = 0; st.tic2tic = 0; mutex_lock(&vdev->lock); vdev->scsw.ac &= ~AC_START; vdev->scsw.ac |= AC_SCH_ACT | AC_DEV_ACT; st.f = vdev->orb.f; st.addr = vdev->orb.addr; st.ops = vdev->u.spool.ops; mutex_unlock(&vdev->lock); con_printf(sys->con, "spool_exec for %04x (%08x), format-%d\n", vdev->pmcw.dev_num, vdev->sch, st.f); for(;;) { con_printf(sys->con, "CCW %3d @%08x, ", cnt++, st.addr); /* ORB must contain a valid address */ __get_ccw(sys, &st, &ccw); st.addr += 8; if (st.sch_status) goto out; /* in case __get_ccw failed */ con_printf(sys->con, "%02x %02x %04x %08x ", ccw.cmd, ccw.flags, ccw.count, ccw.addr); /* handle TICs */ if (ccw.cmd == CCW_CMD_TIC) { if (st.tic2tic) goto prog_check; if (st.f && (ccw.flags || ccw.count)) goto prog_check; /* Note: we'll end up checking the address next interation */ st.addr = ccw.addr; st.tic2tic = 1; continue; } /* reset the tic-to-tic flag */ st.tic2tic = 0; ////////////////////////////////////////////////////// /* handle invalid commands */ if (!st.cd && ((ccw.cmd & 0x0f) == 0x00)) goto prog_check; /* * Invalid Count, Format 0: A CCW, which is other * than a CCW specifying transfer in channel, contains * zeros in bit positions 48-63. */ if (!st.f && !ccw.count) goto prog_check; /* * Invalid Count, Format 1: A CCW that specifies * data chaining or a CCW fetched while data chaining * contains zeros in bit positions 16-31. */ if (st.f && st.cd && !ccw.count) goto prog_check; /* The Data address must be valid */ if (st.f && (ccw.addr & 0x80000000)) goto prog_check; FIXME(""); if (ccw.flags & (CCW_FLAG_SKP | CCW_FLAG_PCI | CCW_FLAG_IDA | CCW_FLAG_S | CCW_FLAG_MIDA | CCW_FLAG_CD)) { con_printf(sys->con, "unsupported ccw flag "); goto prog_check; } /* * Write operations need to get data from storage */ if (IS_CCW_WRITE(ccw.cmd)) { if (__xfer_storage(sys, vdev, &st, &ccw, 1)) break; } /* * Great! The channel decoded the CCW properly, now it's * time to tell the device to execute the operation. */ if (st.ops && st.ops->f[ccw.cmd & 0xf]) st.ops->f[ccw.cmd & 0xf](sys, vdev, &ccw, &st); else spooldev_cmdrej(sys, vdev, &st); if (st.dev_status != (SC_STATUS_CE | SC_STATUS_DE)) break; /* command failed, stop chanining */ /* * Read operations need to get data to storage */ if (IS_CCW_READ(ccw.cmd)) { if (__xfer_storage(sys, vdev, &st, &ccw, 0)) break; } con_printf(sys->con, "OK\n"); if ((ccw.flags & (CCW_FLAG_CD | CCW_FLAG_CC)) == 0) break; /* end of chain */ st.cd = (ccw.flags & CCW_FLAG_CD) != 0; st.addr = (st.addr + 8) & 0x7fffffff; } out: mutex_lock(&vdev->lock); vdev->scsw.ac &= ~(AC_SCH_ACT | AC_DEV_ACT); vdev->scsw.sc |= SC_PRIMARY | SC_SECONDARY | SC_STATUS; if (st.sch_status || (st.dev_status != (SC_STATUS_CE | SC_STATUS_DE))) { con_printf(sys->con, "FAILED\n"); vdev->scsw.sc |= SC_ALERT; } vdev->scsw.dev_status = st.dev_status; vdev->scsw.sch_status = st.sch_status; vdev->scsw.addr = st.addr; vdev->scsw.count = st.rem; vdev->scsw.f = st.f; queue_io_interrupt(sys, vdev->sch, vdev->pmcw.interrupt_param, 0, vdev->pmcw.isc); mutex_unlock(&vdev->lock); free_pages(st.iobuf, 4); return 0; prog_check: st.sch_status = SC_PROG_CHK; goto out; }