diff options
Diffstat (limited to 'drivers')
122 files changed, 9438 insertions, 7595 deletions
diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c index 0c85e9d..7080b41 100644 --- a/drivers/base/hypervisor.c +++ b/drivers/base/hypervisor.c @@ -1,8 +1,9 @@ /* * hypervisor.c - /sys/hypervisor subsystem. * - * This file is released under the GPLv2 + * Copyright (C) IBM Corp. 2006 * + * This file is released under the GPLv2 */ #include <linux/kobject.h> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index c40e487..52ea94b 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -495,6 +495,21 @@ config LEGACY_PTY_COUNT When not in use, each legacy PTY occupies 12 bytes on 32-bit architectures and 24 bytes on 64-bit architectures. +config BRIQ_PANEL + tristate 'Total Impact briQ front panel driver' + depends on PPC_CHRP + ---help--- + The briQ is a small footprint CHRP computer with a frontpanel VFD, a + tristate led and two switches. It is the size of a CDROM drive. + + If you have such one and want anything showing on the VFD then you + must answer Y here. + + To compile this driver as a module, choose M here: the + module will be called briq_panel. + + It's safe to say N here. + config PRINTER tristate "Parallel printer support" depends on PARPORT @@ -596,6 +611,13 @@ config HVC_CONSOLE console. This driver allows each pSeries partition to have a console which is accessed via the HMC. +config HVC_ISERIES + bool "iSeries Hypervisor Virtual Console support" + depends on PPC_ISERIES && !VIOCONS + select HVC_DRIVER + help + iSeries machines support a hypervisor virtual console. + config HVC_RTAS bool "IBM RTAS Console support" depends on PPC_RTAS diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 6e0f446..8c6dfc62 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o +obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o @@ -51,6 +52,7 @@ obj-$(CONFIG_VIOCONS) += viocons.o obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_SGI_MBCS) += mbcs.o +obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_TIPAR) += tipar.o diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 1de1b12..91b71e7 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -601,8 +601,8 @@ static int __devinit agp_uninorth_probe(struct pci_dev *pdev, uninorth_node = of_find_node_by_name(NULL, "u3"); } if (uninorth_node) { - int *revprop = (int *) - get_property(uninorth_node, "device-rev", NULL); + const int *revprop = get_property(uninorth_node, + "device-rev", NULL); if (revprop != NULL) uninorth_rev = *revprop & 0x3f; of_node_put(uninorth_node); diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c new file mode 100644 index 0000000..a0e5eac --- /dev/null +++ b/drivers/char/briq_panel.c @@ -0,0 +1,268 @@ +/* + * Drivers for the Total Impact PPC based computer "BRIQ" + * by Dr. Karsten Jeppesen + * + */ + +#include <linux/module.h> + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/init.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/prom.h> + +#define BRIQ_PANEL_MINOR 156 +#define BRIQ_PANEL_VFD_IOPORT 0x0390 +#define BRIQ_PANEL_LED_IOPORT 0x0398 +#define BRIQ_PANEL_VER "1.1 (04/20/2002)" +#define BRIQ_PANEL_MSG0 "Loading Linux" + +static int vfd_is_open; +static unsigned char vfd[40]; +static int vfd_cursor; +static unsigned char ledpb, led; + +static void update_vfd(void) +{ + int i; + + /* cursor home */ + outb(0x02, BRIQ_PANEL_VFD_IOPORT); + for (i=0; i<20; i++) + outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1); + + /* cursor to next line */ + outb(0xc0, BRIQ_PANEL_VFD_IOPORT); + for (i=20; i<40; i++) + outb(vfd[i], BRIQ_PANEL_VFD_IOPORT + 1); + +} + +static void set_led(char state) +{ + if (state == 'R') + led = 0x01; + else if (state == 'G') + led = 0x02; + else if (state == 'Y') + led = 0x03; + else if (state == 'X') + led = 0x00; + outb(led, BRIQ_PANEL_LED_IOPORT); +} + +static int briq_panel_open(struct inode *ino, struct file *filep) +{ + /* enforce single access */ + if (vfd_is_open) + return -EBUSY; + vfd_is_open = 1; + + return 0; +} + +static int briq_panel_release(struct inode *ino, struct file *filep) +{ + if (!vfd_is_open) + return -ENODEV; + + vfd_is_open = 0; + + return 0; +} + +static ssize_t briq_panel_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned short c; + unsigned char cp; + +#if 0 /* Can't seek (pread) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + + if (!vfd_is_open) + return -ENODEV; + + c = (inb(BRIQ_PANEL_LED_IOPORT) & 0x000c) | (ledpb & 0x0003); + set_led(' '); + /* upper button released */ + if ((!(ledpb & 0x0004)) && (c & 0x0004)) { + cp = ' '; + ledpb = c; + if (copy_to_user(buf, &cp, 1)) + return -EFAULT; + return 1; + } + /* lower button released */ + else if ((!(ledpb & 0x0008)) && (c & 0x0008)) { + cp = '\r'; + ledpb = c; + if (copy_to_user(buf, &cp, 1)) + return -EFAULT; + return 1; + } else { + ledpb = c; + return 0; + } +} + +static void scroll_vfd( void ) +{ + int i; + + for (i=0; i<20; i++) { + vfd[i] = vfd[i+20]; + vfd[i+20] = ' '; + } + vfd_cursor = 20; +} + +static ssize_t briq_panel_write(struct file *file, const char *buf, size_t len, + loff_t *ppos) +{ + size_t indx = len; + int i, esc = 0; + +#if 0 /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + + if (!vfd_is_open) + return -EBUSY; + + for (;;) { + if (!indx) + break; + if (esc) { + set_led(*buf); + esc = 0; + } else if (*buf == 27) { + esc = 1; + } else if (*buf == 12) { + /* do a form feed */ + for (i=0; i<40; i++) + vfd[i] = ' '; + vfd_cursor = 0; + } else if (*buf == 10) { + if (vfd_cursor < 20) + vfd_cursor = 20; + else if (vfd_cursor < 40) + vfd_cursor = 40; + else if (vfd_cursor < 60) + vfd_cursor = 60; + if (vfd_cursor > 59) + scroll_vfd(); + } else { + /* just a character */ + if (vfd_cursor > 39) + scroll_vfd(); + vfd[vfd_cursor++] = *buf; + } + indx--; + buf++; + } + update_vfd(); + + return len; +} + +static struct file_operations briq_panel_fops = { + .owner = THIS_MODULE, + .read = briq_panel_read, + .write = briq_panel_write, + .open = briq_panel_open, + .release = briq_panel_release, +}; + +static struct miscdevice briq_panel_miscdev = { + BRIQ_PANEL_MINOR, + "briq_panel", + &briq_panel_fops +}; + +static int __init briq_panel_init(void) +{ + struct device_node *root = find_path_device("/"); + char *machine; + int i; + + machine = get_property(root, "model", NULL); + if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) + return -ENODEV; + + printk(KERN_INFO + "briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n", + BRIQ_PANEL_VER); + + if (!request_region(BRIQ_PANEL_VFD_IOPORT, 4, "BRIQ Front Panel")) + return -EBUSY; + + if (!request_region(BRIQ_PANEL_LED_IOPORT, 2, "BRIQ Front Panel")) { + release_region(BRIQ_PANEL_VFD_IOPORT, 4); + return -EBUSY; + } + ledpb = inb(BRIQ_PANEL_LED_IOPORT) & 0x000c; + + if (misc_register(&briq_panel_miscdev) < 0) { + release_region(BRIQ_PANEL_VFD_IOPORT, 4); + release_region(BRIQ_PANEL_LED_IOPORT, 2); + return -EBUSY; + } + + outb(0x38, BRIQ_PANEL_VFD_IOPORT); /* Function set */ + outb(0x01, BRIQ_PANEL_VFD_IOPORT); /* Clear display */ + outb(0x0c, BRIQ_PANEL_VFD_IOPORT); /* Display on */ + outb(0x06, BRIQ_PANEL_VFD_IOPORT); /* Entry normal */ + for (i=0; i<40; i++) + vfd[i]=' '; +#ifndef MODULE + vfd[0] = 'L'; + vfd[1] = 'o'; + vfd[2] = 'a'; + vfd[3] = 'd'; + vfd[4] = 'i'; + vfd[5] = 'n'; + vfd[6] = 'g'; + vfd[7] = ' '; + vfd[8] = '.'; + vfd[9] = '.'; + vfd[10] = '.'; +#endif /* !MODULE */ + + update_vfd(); + + return 0; +} + +static void __exit briq_panel_exit(void) +{ + misc_deregister(&briq_panel_miscdev); + release_region(BRIQ_PANEL_VFD_IOPORT, 4); + release_region(BRIQ_PANEL_LED_IOPORT, 2); +} + +module_init(briq_panel_init); +module_exit(briq_panel_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Karsten Jeppesen <karsten@jeppesens.com>"); +MODULE_DESCRIPTION("Driver for the Total Impact briQ front panel"); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 613d67f..a76d2c4 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -80,7 +80,8 @@ struct hvc_struct { struct tty_struct *tty; unsigned int count; int do_wakeup; - char outbuf[N_OUTBUF] __ALIGNED__; + char *outbuf; + int outbuf_size; int n_outbuf; uint32_t vtermno; struct hv_ops *ops; @@ -319,10 +320,8 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) struct kobject *kobjp; /* Auto increments kobject reference if found. */ - if (!(hp = hvc_get_by_index(tty->index))) { - printk(KERN_WARNING "hvc_console: tty open failed, no vty associated with tty.\n"); + if (!(hp = hvc_get_by_index(tty->index))) return -ENODEV; - } spin_lock_irqsave(&hp->lock, flags); /* Check and then increment for fast path open. */ @@ -505,7 +504,7 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count if (hp->n_outbuf > 0) hvc_push(hp); - while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) { + while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) { if (rsize > count) rsize = count; memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); @@ -538,7 +537,7 @@ static int hvc_write_room(struct tty_struct *tty) if (!hp) return -1; - return N_OUTBUF - hp->n_outbuf; + return hp->outbuf_size - hp->n_outbuf; } static int hvc_chars_in_buffer(struct tty_struct *tty) @@ -729,12 +728,13 @@ static struct kobj_type hvc_kobj_type = { }; struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, - struct hv_ops *ops) + struct hv_ops *ops, int outbuf_size) { struct hvc_struct *hp; int i; - hp = kmalloc(sizeof(*hp), GFP_KERNEL); + hp = kmalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, + GFP_KERNEL); if (!hp) return ERR_PTR(-ENOMEM); @@ -743,6 +743,8 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, hp->vtermno = vtermno; hp->irq = irq; hp->ops = ops; + hp->outbuf_size = outbuf_size; + hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; kobject_init(&hp->kobj); hp->kobj.ktype = &hvc_kobj_type; diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 96b7401..8c59818 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -56,7 +56,7 @@ extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq, - struct hv_ops *ops); + struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */ extern int __devexit hvc_remove(struct hvc_struct *hp); diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c new file mode 100644 index 0000000..4747729 --- /dev/null +++ b/drivers/char/hvc_iseries.c @@ -0,0 +1,594 @@ +/* + * iSeries vio driver interface to hvc_console.c + * + * This code is based heavily on hvc_vio.c and viocons.c + * + * Copyright (C) 2006 Stephen Rothwell, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <stdarg.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/console.h> + +#include <asm/hvconsole.h> +#include <asm/vio.h> +#include <asm/prom.h> +#include <asm/iseries/vio.h> +#include <asm/iseries/hv_call.h> +#include <asm/iseries/hv_lp_config.h> +#include <asm/iseries/hv_lp_event.h> + +#include "hvc_console.h" + +#define VTTY_PORTS 10 + +static DEFINE_SPINLOCK(consolelock); +static DEFINE_SPINLOCK(consoleloglock); + +static const char hvc_driver_name[] = "hvc_console"; + +#define IN_BUF_SIZE 200 + +/* + * Our port information. + */ +static struct port_info { + HvLpIndex lp; + u64 seq; /* sequence number of last HV send */ + u64 ack; /* last ack from HV */ + struct hvc_struct *hp; + int in_start; + int in_end; + unsigned char in_buf[IN_BUF_SIZE]; +} port_info[VTTY_PORTS] = { + [ 0 ... VTTY_PORTS - 1 ] = { + .lp = HvLpIndexInvalid + } +}; + +#define viochar_is_console(pi) ((pi) == &port_info[0]) + +static struct vio_device_id hvc_driver_table[] __devinitdata = { + {"serial", "IBM,iSeries-vty"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, hvc_driver_table); + +static void hvlog(char *fmt, ...) +{ + int i; + unsigned long flags; + va_list args; + static char buf[256]; + + spin_lock_irqsave(&consoleloglock, flags); + va_start(args, fmt); + i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); + va_end(args); + buf[i++] = '\r'; + HvCall_writeLogBuffer(buf, i); + spin_unlock_irqrestore(&consoleloglock, flags); +} + +/* + * Initialize the common fields in a charLpEvent + */ +static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp) +{ + struct HvLpEvent *hev = &viochar->event; + + memset(viochar, 0, sizeof(struct viocharlpevent)); + + hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK | + HV_LP_EVENT_INT; + hev->xType = HvLpEvent_Type_VirtualIo; + hev->xSubtype = viomajorsubtype_chario | viochardata; + hev->xSourceLp = HvLpConfig_getLpIndex(); + hev->xTargetLp = lp; + hev->xSizeMinus1 = sizeof(struct viocharlpevent); + hev->xSourceInstanceId = viopath_sourceinst(lp); + hev->xTargetInstanceId = viopath_targetinst(lp); +} + +static int get_chars(uint32_t vtermno, char *buf, int count) +{ + struct port_info *pi; + int n = 0; + unsigned long flags; + + if (vtermno >= VTTY_PORTS) + return -EINVAL; + if (count == 0) + return 0; + + pi = &port_info[vtermno]; + spin_lock_irqsave(&consolelock, flags); + + if (pi->in_end == 0) + goto done; + + n = pi->in_end - pi->in_start; + if (n > count) + n = count; + memcpy(buf, &pi->in_buf[pi->in_start], n); + pi->in_start += n; + if (pi->in_start == pi->in_end) { + pi->in_start = 0; + pi->in_end = 0; + } +done: + spin_unlock_irqrestore(&consolelock, flags); + return n; +} + +static int put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct viocharlpevent *viochar; + struct port_info *pi; + HvLpEvent_Rc hvrc; + unsigned long flags; + int sent = 0; + + if (vtermno >= VTTY_PORTS) + return -EINVAL; + + pi = &port_info[vtermno]; + + spin_lock_irqsave(&consolelock, flags); + + if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) { + spin_lock_irqsave(&consoleloglock, flags); + HvCall_writeLogBuffer(buf, count); + spin_unlock_irqrestore(&consoleloglock, flags); + sent = count; + goto done; + } + + viochar = vio_get_event_buffer(viomajorsubtype_chario); + if (viochar == NULL) { + hvlog("\n\rviocons: Can't get viochar buffer."); + goto done; + } + + while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { + int len; + + len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count; + + if (viochar_is_console(pi)) { + spin_lock_irqsave(&consoleloglock, flags); + HvCall_writeLogBuffer(buf, len); + spin_unlock_irqrestore(&consoleloglock, flags); + } + + init_data_event(viochar, pi->lp); + + viochar->len = len; + viochar->event.xCorrelationToken = pi->seq++; + viochar->event.xSizeMinus1 = + offsetof(struct viocharlpevent, data) + len; + + memcpy(viochar->data, buf, len); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + hvlog("\n\rerror sending event! return code %d\n\r", + (int)hvrc); + sent += len; + count -= len; + buf += len; + } + + vio_free_event_buffer(viomajorsubtype_chario, viochar); +done: + spin_unlock_irqrestore(&consolelock, flags); + return sent; +} + +static struct hv_ops hvc_get_put_ops = { + .get_chars = get_chars, + .put_chars = put_chars, +}; + +static int __devinit hvc_vio_probe(struct vio_dev *vdev, + const struct vio_device_id *id) +{ + struct hvc_struct *hp; + struct port_info *pi; + + /* probed with invalid parameters. */ + if (!vdev || !id) + return -EPERM; + + if (vdev->unit_address >= VTTY_PORTS) + return -ENODEV; + + pi = &port_info[vdev->unit_address]; + + hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, + VIOCHAR_MAX_DATA); + if (IS_ERR(hp)) + return PTR_ERR(hp); + pi->hp = hp; + dev_set_drvdata(&vdev->dev, pi); + + return 0; +} + +static int __devexit hvc_vio_remove(struct vio_dev *vdev) +{ + struct port_info *pi = dev_get_drvdata(&vdev->dev); + struct hvc_struct *hp = pi->hp; + + return hvc_remove(hp); +} + +static struct vio_driver hvc_vio_driver = { + .id_table = hvc_driver_table, + .probe = hvc_vio_probe, + .remove = hvc_vio_remove, + .driver = { + .name = hvc_driver_name, + .owner = THIS_MODULE, + } +}; + +static void hvc_open_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtual_device; + struct port_info *pi; + int reject = 0; + + if (hvlpevent_is_ack(event)) { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, flags); + + pi = &port_info[port]; + if (event->xRc == HvLpEvent_Rc_Good) { + pi->seq = pi->ack = 0; + /* + * This line allows connections from the primary + * partition but once one is connected from the + * primary partition nothing short of a reboot + * of linux will allow access from the hosting + * partition again without a required iSeries fix. + */ + pi->lp = event->xTargetLp; + } + + spin_unlock_irqrestore(&consolelock, flags); + if (event->xRc != HvLpEvent_Rc_Good) + printk(KERN_WARNING + "hvc: handle_open_event: event->xRc == (%d).\n", + event->xRc); + + if (event->xCorrelationToken != 0) { + atomic_t *aptr= (atomic_t *)event->xCorrelationToken; + atomic_set(aptr, 1); + } else + printk(KERN_WARNING + "hvc: weird...got open ack without atomic\n"); + return; + } + + /* This had better require an ack, otherwise complain */ + if (!hvlpevent_need_ack(event)) { + printk(KERN_WARNING "hvc: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) { + event->xRc = HvLpEvent_Rc_SubtypeError; + cevent->subtype_result_code = viorc_openRejected; + /* + * Flag state here since we can't printk while holding + * the consolelock spinlock. + */ + reject = 1; + } else { + pi = &port_info[port]; + if ((pi->lp != HvLpIndexInvalid) && + (pi->lp != event->xSourceLp)) { + /* + * If this is tty is already connected to a different + * partition, fail. + */ + event->xRc = HvLpEvent_Rc_SubtypeError; + cevent->subtype_result_code = viorc_openRejected; + reject = 2; + } else { + pi->lp = event->xSourceLp; + event->xRc = HvLpEvent_Rc_Good; + cevent->subtype_result_code = viorc_good; + pi->seq = pi->ack = 0; + } + } + + spin_unlock_irqrestore(&consolelock, flags); + + if (reject == 1) + printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n"); + else if (reject == 2) + printk(KERN_WARNING "hvc: open rejected: console in exclusive " + "use by another partition.\n"); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); +} + +/* + * Handle a close charLpEvent. This should ONLY be an Interrupt because the + * virtual console should never actually issue a close event to the hypervisor + * because the virtual console never goes away. A close event coming from the + * hypervisor simply means that there are no client consoles connected to the + * virtual console. + */ +static void hvc_close_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtual_device; + + if (!hvlpevent_is_int(event)) { + printk(KERN_WARNING + "hvc: got unexpected close acknowlegement\n"); + return; + } + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING + "hvc: close message from invalid virtual device.\n"); + return; + } + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, flags); + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, flags); +} + +static void hvc_data_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + struct port_info *pi; + int n; + u8 port = cevent->virtual_device; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING "hvc: data on invalid virtual device %d\n", + port); + return; + } + if (cevent->len == 0) + return; + + /* + * Change 05/01/2003 - Ryan Arnold: If a partition other than + * the current exclusive partition tries to send us data + * events then just drop them on the floor because we don't + * want his stinking data. He isn't authorized to receive + * data because he wasn't the first one to get the console, + * therefore he shouldn't be allowed to send data either. + * This will work without an iSeries fix. + */ + pi = &port_info[port]; + if (pi->lp != event->xSourceLp) + return; + + spin_lock_irqsave(&consolelock, flags); + + n = IN_BUF_SIZE - pi->in_end; + if (n > cevent->len) + n = cevent->len; + if (n > 0) { + memcpy(&pi->in_buf[pi->in_end], cevent->data, n); + pi->in_end += n; + } + spin_unlock_irqrestore(&consolelock, flags); + if (n == 0) + printk(KERN_WARNING "hvc: input buffer overflow\n"); +} + +static void hvc_ack_event(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + unsigned long flags; + u8 port = cevent->virtual_device; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING "hvc: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + port_info[port].ack = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, flags); +} + +static void hvc_config_event(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + + if (cevent->data[0] == 0x01) + printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n", + cevent->data[1], cevent->data[2], + cevent->data[3], cevent->data[4]); + else + printk(KERN_WARNING "hvc: unknown config event\n"); +} + +static void hvc_handle_event(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) + return; + + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) { + case viocharopen: + hvc_open_event(event); + break; + case viocharclose: + hvc_close_event(event); + break; + case viochardata: + hvc_data_event(event); + break; + case viocharack: + hvc_ack_event(event); + break; + case viocharconfig: + hvc_config_event(event); + break; + default: + if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +static int send_open(HvLpIndex remoteLp, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario | viocharopen, + HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(remoteLp), + viopath_targetinst(remoteLp), + (u64)(unsigned long)sem, VIOVERSION << 16, + 0, 0, 0, 0); +} + +static int hvc_vio_init(void) +{ + atomic_t wait_flag; + int rc; + + /* +2 for fudge */ + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), + viomajorsubtype_chario, VIOCHAR_WINDOW + 2); + if (rc) + printk(KERN_WARNING "hvc: error opening to primary %d\n", rc); + + if (viopath_hostLp == HvLpIndexInvalid) + vio_set_hostlp(); + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { + printk(KERN_INFO "hvc: open path to hosting (%d)\n", + viopath_hostLp); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, + VIOCHAR_WINDOW + 2); /* +2 for fudge */ + if (rc) + printk(KERN_WARNING + "error opening to partition %d: %d\n", + viopath_hostLp, rc); + } + + if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0) + printk(KERN_WARNING + "hvc: error seting handler for console events!\n"); + + /* + * First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + atomic_set(&wait_flag, 0); + if ((viopath_isactive(viopath_hostLp)) && + (send_open(viopath_hostLp, &wait_flag) == 0)) { + printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp); + while (atomic_read(&wait_flag) == 0) + mb(); + atomic_set(&wait_flag, 0); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) { + printk(KERN_INFO "hvc: opening console to primary partition\n"); + while (atomic_read(&wait_flag) == 0) + mb(); + } + + /* Register as a vio device to receive callbacks */ + rc = vio_register_driver(&hvc_vio_driver); + + return rc; +} +module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ + +static void hvc_vio_exit(void) +{ + vio_unregister_driver(&hvc_vio_driver); +} +module_exit(hvc_vio_exit); + +/* the device tree order defines our numbering */ +static int hvc_find_vtys(void) +{ + struct device_node *vty; + int num_found = 0; + + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + uint32_t *vtermno; + + /* We have statically defined space for only a certain number + * of console adapters. + */ + if ((num_found >= MAX_NR_HVC_CONSOLES) || + (num_found >= VTTY_PORTS)) + break; + + vtermno = (uint32_t *)get_property(vty, "reg", NULL); + if (!vtermno) + continue; + + if (!device_is_compatible(vty, "IBM,iSeries-vty")) + continue; + + if (num_found == 0) + add_preferred_console("hvc", 0, NULL); + hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); + ++num_found; + } + + return num_found; +} +console_initcall(hvc_find_vtys); diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 57106e02..4b97eaf 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -94,7 +94,7 @@ static int hvc_rtas_init(void) /* Allocate an hvc_struct for the console device we instantiated * earlier. Save off hp so that we can return it on exit */ - hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops); + hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16); if (IS_ERR(hp)) return PTR_ERR(hp); diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 9add81ce..cc95941 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -90,7 +90,8 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, if (!vdev || !id) return -EPERM; - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops); + hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, + MAX_VIO_PUT_CHARS); if (IS_ERR(hp)) return PTR_ERR(hp); dev_set_drvdata(&vdev->dev, hp); @@ -140,7 +141,7 @@ static int hvc_find_vtys(void) for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; vty = of_find_node_by_name(vty, "vty")) { - uint32_t *vtermno; + const uint32_t *vtermno; /* We have statically defined space for only a certain number * of console adapters. @@ -148,7 +149,7 @@ static int hvc_find_vtys(void) if (num_found >= MAX_NR_HVC_CONSOLES) break; - vtermno = (uint32_t *)get_property(vty, "reg", NULL); + vtermno = get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 017f755..a89a95f 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1274,11 +1274,10 @@ static int __init hvsi_console_init(void) vty != NULL; vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { struct hvsi_struct *hp; - uint32_t *vtermno; - uint32_t *irq; + const uint32_t *vtermno, *irq; - vtermno = (uint32_t *)get_property(vty, "reg", NULL); - irq = (uint32_t *)get_property(vty, "interrupts", NULL); + vtermno = get_property(vty, "reg", NULL); + irq = get_property(vty, "interrupts", NULL); if (!vtermno || !irq) continue; diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index 2e68eeb..aefd683 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -37,7 +37,7 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) { struct device_node *dn; unsigned long address, size; - unsigned int *reg; + const unsigned int *reg; int reglen; int naddrc; int nsizec; @@ -52,7 +52,7 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) return NULL; } - reg = (unsigned int *) get_property(dn, "reg", ®len); + reg = get_property(dn, "reg", ®len); naddrc = prom_n_addr_cells(dn); nsizec = prom_n_size_cells(dn); diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c index 766f786..f3efeaf 100644 --- a/drivers/char/viocons.c +++ b/drivers/char/viocons.c @@ -43,7 +43,6 @@ #include <linux/sysrq.h> #include <asm/iseries/vio.h> - #include <asm/iseries/hv_lp_event.h> #include <asm/iseries/hv_call_event.h> #include <asm/iseries/hv_lp_config.h> @@ -67,35 +66,6 @@ static int vio_sysrq_pressed; extern int sysrq_enabled; #endif -/* - * The structure of the events that flow between us and OS/400. You can't - * mess with this unless the OS/400 side changes too - */ -struct viocharlpevent { - struct HvLpEvent event; - u32 reserved; - u16 version; - u16 subtype_result_code; - u8 virtual_device; - u8 len; - u8 data[VIOCHAR_MAX_DATA]; -}; - -#define VIOCHAR_WINDOW 10 -#define VIOCHAR_HIGHWATERMARK 3 - -enum viocharsubtype { - viocharopen = 0x0001, - viocharclose = 0x0002, - viochardata = 0x0003, - viocharack = 0x0004, - viocharconfig = 0x0005 -}; - -enum viochar_rc { - viochar_rc_ebusy = 1 -}; - #define VIOCHAR_NUM_BUF 16 /* @@ -1183,6 +1153,7 @@ static int __init viocons_init(void) port_info[i].magic = VIOTTY_MAGIC; } HvCall_setLogBufferFormatAndCodepage(HvCall_LogBuffer_ASCII, 437); + add_preferred_console("viocons", 0, NULL); register_console(&viocons_early); return 0; } diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index b72b204..73c78bf 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -940,7 +940,6 @@ static void vioHandleTapeEvent(struct HvLpEvent *event) static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) { - char tapename[32]; int i = vdev->unit_address; int j; @@ -956,10 +955,9 @@ static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) "iseries!vt%d", i); class_device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), NULL, "iseries!nvt%d", i); - sprintf(tapename, "iseries/vt%d", i); - printk(VIOTAPE_KERN_INFO "tape %s is iSeries " + printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries " "resource %10.10s type %4.4s, model %3.3s\n", - tapename, viotape_unitinfo[i].rsrcname, + i, viotape_unitinfo[i].rsrcname, viotape_unitinfo[i].type, viotape_unitinfo[i].model); return 0; } diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 53bb435..d658d91 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -207,7 +207,8 @@ static int i2c_powermac_probe(struct device *dev) struct pmac_i2c_bus *bus = dev->platform_data; struct device_node *parent = NULL; struct i2c_adapter *adapter; - char name[32], *basename; + char name[32]; + const char *basename; int rc; if (bus == NULL) diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ebf961f..996c694 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1154,7 +1154,7 @@ static int pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) { struct device_node *np = pmif->node; - int *bidp; + const int *bidp; pmif->cable_80 = 0; pmif->broken_dma = pmif->broken_dma_warn = 0; @@ -1176,14 +1176,14 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) pmif->broken_dma = 1; } - bidp = (int *)get_property(np, "AAPL,bus-id", NULL); + bidp = get_property(np, "AAPL,bus-id", NULL); pmif->aapl_bus_id = bidp ? *bidp : 0; /* Get cable type from device-tree */ if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6 || pmif->kind == controller_k2_ata6 || pmif->kind == controller_sh_ata6) { - char* cable = get_property(np, "cable-type", NULL); + const char* cable = get_property(np, "cable-type", NULL); if (cable && !strncmp(cable, "80-", 3)) pmif->cable_80 = 1; } @@ -1326,7 +1326,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) if (macio_irq_count(mdev) == 0) { printk(KERN_WARNING "ide%d: no intrs for device %s, using 13\n", i, mdev->ofdev.node->full_name); - irq = 13; + irq = irq_create_mapping(NULL, 13); } else irq = macio_irq(mdev, 0); diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index 82657bc..d562160 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -139,7 +139,9 @@ static int macio_uevent(struct device *dev, char **envp, int num_envp, { struct macio_dev * macio_dev; struct of_device * of; - char *scratch, *compat, *compat2; + char *scratch; + const char *compat, *compat2; + int i = 0; int length, cplen, cplen2, seen = 0; @@ -173,7 +175,7 @@ static int macio_uevent(struct device *dev, char **envp, int num_envp, * it's not really legal to split it out with commas. We split it * up using a number of environment variables instead. */ - compat = (char *) get_property(of->node, "compatible", &cplen); + compat = get_property(of->node, "compatible", &cplen); compat2 = compat; cplen2= cplen; while (compat && cplen > 0) { @@ -454,7 +456,7 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct resource *parent_res) { struct macio_dev *dev; - u32 *reg; + const u32 *reg; if (np == NULL) return NULL; @@ -489,7 +491,7 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, #endif MAX_NODE_NAME_SIZE, np->name); } else { - reg = (u32 *)get_property(np, "reg", NULL); + reg = get_property(np, "reg", NULL); sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c index cae24a1..8566bdf 100644 --- a/drivers/macintosh/macio_sysfs.c +++ b/drivers/macintosh/macio_sysfs.c @@ -16,12 +16,12 @@ static ssize_t compatible_show (struct device *dev, struct device_attribute *attr, char *buf) { struct of_device *of; - char *compat; + const char *compat; int cplen; int length = 0; of = &to_macio_device (dev)->ofdev; - compat = (char *) get_property(of->node, "compatible", &cplen); + compat = get_property(of->node, "compatible", &cplen); if (!compat) { *buf = '\0'; return 0; @@ -42,12 +42,12 @@ static ssize_t modalias_show (struct device *dev, struct device_attribute *attr, char *buf) { struct of_device *of; - char *compat; + const char *compat; int cplen; int length; of = &to_macio_device (dev)->ofdev; - compat = (char *) get_property (of->node, "compatible", &cplen); + compat = get_property(of->node, "compatible", &cplen); if (!compat) compat = "", cplen = 1; length = sprintf (buf, "of:N%sT%s", of->node->name, of->node->type); buf += length; diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 00ef468..090e40f 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -454,7 +454,7 @@ EXPORT_SYMBOL(smu_present); int __init smu_init (void) { struct device_node *np; - u32 *data; + const u32 *data; np = of_find_node_by_type(NULL, "smu"); if (np == NULL) @@ -490,7 +490,7 @@ int __init smu_init (void) printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); goto fail; } - data = (u32 *)get_property(smu->db_node, "reg", NULL); + data = get_property(smu->db_node, "reg", NULL); if (data == NULL) { of_node_put(smu->db_node); smu->db_node = NULL; @@ -511,7 +511,7 @@ int __init smu_init (void) smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt"); if (smu->msg_node == NULL) break; - data = (u32 *)get_property(smu->msg_node, "reg", NULL); + data = get_property(smu->msg_node, "reg", NULL); if (data == NULL) { of_node_put(smu->msg_node); smu->msg_node = NULL; @@ -982,11 +982,11 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id) /* Note: Only allowed to return error code in pointers (using ERR_PTR) * when interruptible is 1 */ -struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, - int interruptible) +const struct smu_sdbp_header *__smu_get_sdb_partition(int id, + unsigned int *size, int interruptible) { char pname[32]; - struct smu_sdbp_header *part; + const struct smu_sdbp_header *part; if (!smu) return NULL; @@ -1003,8 +1003,7 @@ struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, } else mutex_lock(&smu_part_access); - part = (struct smu_sdbp_header *)get_property(smu->of_node, - pname, size); + part = get_property(smu->of_node, pname, size); if (part == NULL) { DPRINTK("trying to extract from SMU ...\n"); part = smu_create_sdb_partition(id); @@ -1015,7 +1014,7 @@ struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, return part; } -struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) +const struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) { return __smu_get_sdb_partition(id, size, 0); } @@ -1094,7 +1093,7 @@ static ssize_t smu_write(struct file *file, const char __user *buf, pp->mode = smu_file_events; return 0; } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { - struct smu_sdbp_header *part; + const struct smu_sdbp_header *part; part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); if (part == NULL) return -EINVAL; diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index 7f86478..a0f30d0 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -47,7 +47,7 @@ static u8 FAN_SPD_SET[2] = {0x30, 0x31}; static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ -static char *sensor_location[3] = {NULL, NULL, NULL}; +static const char *sensor_location[3] = {NULL, NULL, NULL}; static int limit_adjust = 0; static int fan_speed = -1; @@ -553,7 +553,7 @@ static int __init thermostat_init(void) { struct device_node* np; - u32 *prop; + const u32 *prop; int i = 0, offset = 0; np = of_find_node_by_name(NULL, "fan"); @@ -566,13 +566,13 @@ thermostat_init(void) else return -ENODEV; - prop = (u32 *)get_property(np, "hwsensor-params-version", NULL); + prop = get_property(np, "hwsensor-params-version", NULL); printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, (*prop == 1)?"":"un"); if (*prop != 1) return -ENODEV; - prop = (u32 *)get_property(np, "reg", NULL); + prop = get_property(np, "reg", NULL); if (!prop) return -ENODEV; diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 20bf672..d00c0c3 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -660,7 +660,7 @@ static int read_eeprom(int cpu, struct mpu_data *out) { struct device_node *np; char nodename[64]; - u8 *data; + const u8 *data; int len; /* prom.c routine for finding a node by path is a bit brain dead @@ -673,7 +673,7 @@ static int read_eeprom(int cpu, struct mpu_data *out) printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n"); return -ENODEV; } - data = (u8 *)get_property(np, "cpuid", &len); + data = get_property(np, "cpuid", &len); if (data == NULL) { printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n"); of_node_put(np); @@ -1336,7 +1336,7 @@ static int init_backside_state(struct backside_pid_state *state) */ u3 = of_find_node_by_path("/u3@0,f8000000"); if (u3 != NULL) { - u32 *vers = (u32 *)get_property(u3, "device-rev", NULL); + const u32 *vers = get_property(u3, "device-rev", NULL); if (vers) if (((*vers) & 0x3f) < 0x34) u3h = 0; @@ -2111,8 +2111,8 @@ static void fcu_lookup_fans(struct device_node *fcu_node) while ((np = of_get_next_child(fcu_node, np)) != NULL) { int type = -1; - char *loc; - u32 *reg; + const char *loc; + const u32 *reg; DBG(" control: %s, type: %s\n", np->name, np->type); @@ -2128,8 +2128,8 @@ static void fcu_lookup_fans(struct device_node *fcu_node) continue; /* Lookup for a matching location */ - loc = (char *)get_property(np, "location", NULL); - reg = (u32 *)get_property(np, "reg", NULL); + loc = get_property(np, "location", NULL); + reg = get_property(np, "reg", NULL); if (loc == NULL || reg == NULL) continue; DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index c7d1c29..738faab 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -484,14 +484,14 @@ struct apple_thermal_info { static int __init g4fan_init( void ) { - struct apple_thermal_info *info; + const struct apple_thermal_info *info; struct device_node *np; init_MUTEX( &x.lock ); if( !(np=of_find_node_by_name(NULL, "power-mgt")) ) return -ENODEV; - info = (struct apple_thermal_info*)get_property(np, "thermal-info", NULL); + info = get_property(np, "thermal-info", NULL); of_node_put(np); if( !info || !machine_is_compatible("PowerMac3,6") ) diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 69d5452..7512d1c 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -123,7 +123,7 @@ int __init find_via_cuda(void) { struct adb_request req; phys_addr_t taddr; - u32 *reg; + const u32 *reg; int err; if (vias != 0) @@ -132,7 +132,7 @@ int __init find_via_cuda(void) if (vias == 0) return 0; - reg = (u32 *)get_property(vias, "reg", NULL); + reg = get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-cuda: No \"reg\" property !\n"); goto fail; diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index 5189d54..179af10 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -120,7 +120,7 @@ static int __init via_pmu_led_init(void) dt = of_find_node_by_path("/"); if (dt == NULL) return -ENODEV; - model = (const char *)get_property(dt, "model", NULL); + model = get_property(dt, "model", NULL); if (model == NULL) return -ENODEV; if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 14610a6..dda0398 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -280,7 +280,7 @@ static char *pbook_type[] = { int __init find_via_pmu(void) { u64 taddr; - u32 *reg; + const u32 *reg; if (via != 0) return 1; @@ -288,7 +288,7 @@ int __init find_via_pmu(void) if (vias == NULL) return 0; - reg = (u32 *)get_property(vias, "reg", NULL); + reg = get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); goto fail; @@ -330,7 +330,7 @@ int __init find_via_pmu(void) gpiop = of_find_node_by_name(NULL, "gpio"); if (gpiop) { - reg = (u32 *)get_property(gpiop, "reg", NULL); + reg = get_property(gpiop, "reg", NULL); if (reg) gaddr = of_translate_address(gpiop, reg); if (gaddr != OF_BAD_ADDR) @@ -479,9 +479,9 @@ static int __init via_pmu_dev_init(void) pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } else { struct device_node* prim = find_devices("power-mgt"); - u32 *prim_info = NULL; + const u32 *prim_info = NULL; if (prim) - prim_info = (u32 *)get_property(prim, "prim-info", NULL); + prim_info = get_property(prim, "prim-info", NULL); if (prim_info) { /* Other stuffs here yet unknown */ pmu_battery_count = (prim_info[6] >> 16) & 0xff; diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index f1df6ef..2ff546e 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c @@ -396,7 +396,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) static void wf_smu_create_cpu_fans(void) { struct wf_cpu_pid_param pid_param; - struct smu_sdbp_header *hdr; + const struct smu_sdbp_header *hdr; struct smu_sdbp_cpupiddata *piddata; struct smu_sdbp_fvt *fvt; s32 tmax, tdelta, maxpow, powadj; @@ -702,7 +702,7 @@ static struct notifier_block wf_smu_events = { static int wf_init_pm(void) { - struct smu_sdbp_header *hdr; + const struct smu_sdbp_header *hdr; hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL); if (hdr != 0) { diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index 0d6372e..59e9ffe 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c @@ -144,7 +144,7 @@ static struct wf_smu_slots_fans_state *wf_smu_slots_fans; static void wf_smu_create_cpu_fans(void) { struct wf_cpu_pid_param pid_param; - struct smu_sdbp_header *hdr; + const struct smu_sdbp_header *hdr; struct smu_sdbp_cpupiddata *piddata; struct smu_sdbp_fvt *fvt; s32 tmax, tdelta, maxpow, powadj; diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index a9e88ed..bff1f37 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c @@ -159,14 +159,15 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, int pwm_fan) { struct smu_fan_control *fct; - s32 *v; u32 *reg; - char *l; + const s32 *v; + const u32 *reg; + const char *l; fct = kmalloc(sizeof(struct smu_fan_control), GFP_KERNEL); if (fct == NULL) return NULL; fct->ctrl.ops = &smu_fan_ops; - l = (char *)get_property(node, "location", NULL); + l = get_property(node, "location", NULL); if (l == NULL) goto fail; @@ -223,17 +224,17 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, goto fail; /* Get min & max values*/ - v = (s32 *)get_property(node, "min-value", NULL); + v = get_property(node, "min-value", NULL); if (v == NULL) goto fail; fct->min = *v; - v = (s32 *)get_property(node, "max-value", NULL); + v = get_property(node, "max-value", NULL); if (v == NULL) goto fail; fct->max = *v; /* Get "reg" value */ - reg = (u32 *)get_property(node, "reg", NULL); + reg = get_property(node, "reg", NULL); if (reg == NULL) goto fail; fct->reg = *reg; diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index e295a07..aceb61d 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -233,15 +233,15 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) { struct wf_sat *sat; struct wf_sat_sensor *sens; - u32 *reg; - char *loc, *type; + const u32 *reg; + const char *loc, *type; u8 addr, chip, core; struct device_node *child; int shift, cpu, index; char *name; int vsens[2], isens[2]; - reg = (u32 *) get_property(dev, "reg", NULL); + reg = get_property(dev, "reg", NULL); if (reg == NULL) return; addr = *reg; @@ -268,7 +268,7 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) isens[0] = isens[1] = -1; child = NULL; while ((child = of_get_next_child(dev, child)) != NULL) { - reg = (u32 *) get_property(child, "reg", NULL); + reg = get_property(child, "reg", NULL); type = get_property(child, "device_type", NULL); loc = get_property(child, "location", NULL); if (reg == NULL || loc == NULL) diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c index bed25dc..defe992 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c @@ -198,14 +198,14 @@ static struct wf_sensor_ops smu_slotspow_ops = { static struct smu_ad_sensor *smu_ads_create(struct device_node *node) { struct smu_ad_sensor *ads; - char *c, *l; - u32 *v; + const char *c, *l; + const u32 *v; ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL); if (ads == NULL) return NULL; - c = (char *)get_property(node, "device_type", NULL); - l = (char *)get_property(node, "location", NULL); + c = get_property(node, "device_type", NULL); + l = get_property(node, "location", NULL); if (c == NULL || l == NULL) goto fail; @@ -255,7 +255,7 @@ static struct smu_ad_sensor *smu_ads_create(struct device_node *node) } else goto fail; - v = (u32 *)get_property(node, "reg", NULL); + v = get_property(node, "reg", NULL); if (v == NULL) goto fail; ads->reg = *v; @@ -382,7 +382,7 @@ smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps) static void smu_fetch_param_partitions(void) { - struct smu_sdbp_header *hdr; + const struct smu_sdbp_header *hdr; /* Get CPU voltage/current/power calibration data */ hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL); diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1344ad7..717e904 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -263,6 +263,14 @@ config RFD_FTL http://www.gensw.com/pages/prod/bios/rfd.htm +config SSFDC + bool "NAND SSFDC (SmartMedia) read only translation layer" + depends on MTD + default n + help + This enables read only access to SmartMedia formatted NAND + flash. You can mount it with FAT file system. + source "drivers/mtd/chips/Kconfig" source "drivers/mtd/maps/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index fc93744..1e36b9a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o mtd_blkdevs.o +obj-$(CONFIG_SSFDC) += ssfdc.o mtd_blkdevs.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index a482e89..702ae4c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -212,6 +212,7 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) { mtd->lock = cfi_atmel_lock; mtd->unlock = cfi_atmel_unlock; + mtd->flags |= MTD_STUPID_LOCK; } static struct cfi_fixup cfi_fixup_table[] = { diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 2c01497..354e165 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -4,82 +4,82 @@ * PMC551 PCI Mezzanine Ram Device * * Author: - * Mark Ferrell <mferrell@mvista.com> - * Copyright 1999,2000 Nortel Networks + * Mark Ferrell <mferrell@mvista.com> + * Copyright 1999,2000 Nortel Networks * * License: - * As part of this driver was derived from the slram.c driver it - * falls under the same license, which is GNU General Public - * License v2 + * As part of this driver was derived from the slram.c driver it + * falls under the same license, which is GNU General Public + * License v2 * * Description: - * This driver is intended to support the PMC551 PCI Ram device - * from Ramix Inc. The PMC551 is a PMC Mezzanine module for - * cPCI embedded systems. The device contains a single SROM - * that initially programs the V370PDC chipset onboard the - * device, and various banks of DRAM/SDRAM onboard. This driver - * implements this PCI Ram device as an MTD (Memory Technology - * Device) so that it can be used to hold a file system, or for - * added swap space in embedded systems. Since the memory on - * this board isn't as fast as main memory we do not try to hook - * it into main memory as that would simply reduce performance - * on the system. Using it as a block device allows us to use - * it as high speed swap or for a high speed disk device of some - * sort. Which becomes very useful on diskless systems in the - * embedded market I might add. + * This driver is intended to support the PMC551 PCI Ram device + * from Ramix Inc. The PMC551 is a PMC Mezzanine module for + * cPCI embedded systems. The device contains a single SROM + * that initially programs the V370PDC chipset onboard the + * device, and various banks of DRAM/SDRAM onboard. This driver + * implements this PCI Ram device as an MTD (Memory Technology + * Device) so that it can be used to hold a file system, or for + * added swap space in embedded systems. Since the memory on + * this board isn't as fast as main memory we do not try to hook + * it into main memory as that would simply reduce performance + * on the system. Using it as a block device allows us to use + * it as high speed swap or for a high speed disk device of some + * sort. Which becomes very useful on diskless systems in the + * embedded market I might add. * * Notes: - * Due to what I assume is more buggy SROM, the 64M PMC551 I - * have available claims that all 4 of it's DRAM banks have 64M - * of ram configured (making a grand total of 256M onboard). - * This is slightly annoying since the BAR0 size reflects the - * aperture size, not the dram size, and the V370PDC supplies no - * other method for memory size discovery. This problem is - * mostly only relevant when compiled as a module, as the - * unloading of the module with an aperture size smaller then - * the ram will cause the driver to detect the onboard memory - * size to be equal to the aperture size when the module is - * reloaded. Soooo, to help, the module supports an msize - * option to allow the specification of the onboard memory, and - * an asize option, to allow the specification of the aperture - * size. The aperture must be equal to or less then the memory - * size, the driver will correct this if you screw it up. This - * problem is not relevant for compiled in drivers as compiled - * in drivers only init once. + * Due to what I assume is more buggy SROM, the 64M PMC551 I + * have available claims that all 4 of it's DRAM banks have 64M + * of ram configured (making a grand total of 256M onboard). + * This is slightly annoying since the BAR0 size reflects the + * aperture size, not the dram size, and the V370PDC supplies no + * other method for memory size discovery. This problem is + * mostly only relevant when compiled as a module, as the + * unloading of the module with an aperture size smaller then + * the ram will cause the driver to detect the onboard memory + * size to be equal to the aperture size when the module is + * reloaded. Soooo, to help, the module supports an msize + * option to allow the specification of the onboard memory, and + * an asize option, to allow the specification of the aperture + * size. The aperture must be equal to or less then the memory + * size, the driver will correct this if you screw it up. This + * problem is not relevant for compiled in drivers as compiled + * in drivers only init once. * * Credits: - * Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the - * initial example code of how to initialize this device and for - * help with questions I had concerning operation of the device. + * Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the + * initial example code of how to initialize this device and for + * help with questions I had concerning operation of the device. * - * Most of the MTD code for this driver was originally written - * for the slram.o module in the MTD drivers package which - * allows the mapping of system memory into an MTD device. - * Since the PMC551 memory module is accessed in the same - * fashion as system memory, the slram.c code became a very nice - * fit to the needs of this driver. All we added was PCI - * detection/initialization to the driver and automatically figure - * out the size via the PCI detection.o, later changes by Corey - * Minyard set up the card to utilize a 1M sliding apature. + * Most of the MTD code for this driver was originally written + * for the slram.o module in the MTD drivers package which + * allows the mapping of system memory into an MTD device. + * Since the PMC551 memory module is accessed in the same + * fashion as system memory, the slram.c code became a very nice + * fit to the needs of this driver. All we added was PCI + * detection/initialization to the driver and automatically figure + * out the size via the PCI detection.o, later changes by Corey + * Minyard set up the card to utilize a 1M sliding apature. * - * Corey Minyard <minyard@nortelnetworks.com> - * * Modified driver to utilize a sliding aperture instead of - * mapping all memory into kernel space which turned out to - * be very wasteful. - * * Located a bug in the SROM's initialization sequence that - * made the memory unusable, added a fix to code to touch up - * the DRAM some. + * Corey Minyard <minyard@nortelnetworks.com> + * * Modified driver to utilize a sliding aperture instead of + * mapping all memory into kernel space which turned out to + * be very wasteful. + * * Located a bug in the SROM's initialization sequence that + * made the memory unusable, added a fix to code to touch up + * the DRAM some. * * Bugs/FIXME's: - * * MUST fix the init function to not spin on a register - * waiting for it to set .. this does not safely handle busted - * devices that never reset the register correctly which will - * cause the system to hang w/ a reboot being the only chance at - * recover. [sort of fixed, could be better] - * * Add I2C handling of the SROM so we can read the SROM's information - * about the aperture size. This should always accurately reflect the - * onboard memory size. - * * Comb the init routine. It's still a bit cludgy on a few things. + * * MUST fix the init function to not spin on a register + * waiting for it to set .. this does not safely handle busted + * devices that never reset the register correctly which will + * cause the system to hang w/ a reboot being the only chance at + * recover. [sort of fixed, could be better] + * * Add I2C handling of the SROM so we can read the SROM's information + * about the aperture size. This should always accurately reflect the + * onboard memory size. + * * Comb the init routine. It's still a bit cludgy on a few things. */ #include <linux/kernel.h> @@ -105,74 +105,77 @@ static struct mtd_info *pmc551list; -static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) +static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct mypriv *priv = mtd->priv; - u32 soff_hi, soff_lo; /* start address offset hi/lo */ - u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ - unsigned long end; + struct mypriv *priv = mtd->priv; + u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ + unsigned long end; u_char *ptr; size_t retlen; #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_erase(pos:%ld, len:%ld)\n", (long)instr->addr, (long)instr->len); + printk(KERN_DEBUG "pmc551_erase(pos:%ld, len:%ld)\n", (long)instr->addr, + (long)instr->len); #endif - end = instr->addr + instr->len - 1; + end = instr->addr + instr->len - 1; - /* Is it past the end? */ - if ( end > mtd->size ) { + /* Is it past the end? */ + if (end > mtd->size) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n", (long)end, (long)mtd->size); + printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n", + (long)end, (long)mtd->size); #endif - return -EINVAL; - } - - eoff_hi = end & ~(priv->asize - 1); - soff_hi = instr->addr & ~(priv->asize - 1); - eoff_lo = end & (priv->asize - 1); - soff_lo = instr->addr & (priv->asize - 1); - - pmc551_point (mtd, instr->addr, instr->len, &retlen, &ptr); - - if ( soff_hi == eoff_hi || mtd->size == priv->asize) { - /* The whole thing fits within one access, so just one shot - will do it. */ - memset(ptr, 0xff, instr->len); - } else { - /* We have to do multiple writes to get all the data - written. */ - while (soff_hi != eoff_hi) { + return -EINVAL; + } + + eoff_hi = end & ~(priv->asize - 1); + soff_hi = instr->addr & ~(priv->asize - 1); + eoff_lo = end & (priv->asize - 1); + soff_lo = instr->addr & (priv->asize - 1); + + pmc551_point(mtd, instr->addr, instr->len, &retlen, &ptr); + + if (soff_hi == eoff_hi || mtd->size == priv->asize) { + /* The whole thing fits within one access, so just one shot + will do it. */ + memset(ptr, 0xff, instr->len); + } else { + /* We have to do multiple writes to get all the data + written. */ + while (soff_hi != eoff_hi) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk( KERN_DEBUG "pmc551_erase() soff_hi: %ld, eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); + printk(KERN_DEBUG "pmc551_erase() soff_hi: %ld, " + "eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); #endif - memset(ptr, 0xff, priv->asize); - if (soff_hi + priv->asize >= mtd->size) { - goto out; - } - soff_hi += priv->asize; - pmc551_point (mtd,(priv->base_map0|soff_hi), - priv->asize, &retlen, &ptr); - } - memset (ptr, 0xff, eoff_lo); - } - -out: + memset(ptr, 0xff, priv->asize); + if (soff_hi + priv->asize >= mtd->size) { + goto out; + } + soff_hi += priv->asize; + pmc551_point(mtd, (priv->base_map0 | soff_hi), + priv->asize, &retlen, &ptr); + } + memset(ptr, 0xff, eoff_lo); + } + + out: instr->state = MTD_ERASE_DONE; #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_erase() done\n"); #endif - mtd_erase_callback(instr); - return 0; + mtd_erase_callback(instr); + return 0; } - -static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) +static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char ** mtdbuf) { - struct mypriv *priv = mtd->priv; - u32 soff_hi; - u32 soff_lo; + struct mypriv *priv = mtd->priv; + u32 soff_hi; + u32 soff_lo; #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len); @@ -180,18 +183,19 @@ static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t * if (from + len > mtd->size) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n", (long)from+len, (long)mtd->size); + printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n", + (long)from + len, (long)mtd->size); #endif return -EINVAL; } - soff_hi = from & ~(priv->asize - 1); - soff_lo = from & (priv->asize - 1); + soff_hi = from & ~(priv->asize - 1); + soff_lo = from & (priv->asize - 1); /* Cheap hack optimization */ - if( priv->curr_map0 != from ) { - pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, - (priv->base_map0 | soff_hi) ); + if (priv->curr_map0 != from) { + pci_write_config_dword(priv->dev, PMC551_PCI_MEM_MAP0, + (priv->base_map0 | soff_hi)); priv->curr_map0 = soff_hi; } @@ -200,137 +204,144 @@ static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t * return 0; } - -static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void pmc551_unpoint(struct mtd_info *mtd, u_char * addr, loff_t from, + size_t len) { #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_unpoint()\n"); #endif } - -static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) { - struct mypriv *priv = mtd->priv; - u32 soff_hi, soff_lo; /* start address offset hi/lo */ - u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ - unsigned long end; + struct mypriv *priv = mtd->priv; + u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ + unsigned long end; u_char *ptr; - u_char *copyto = buf; + u_char *copyto = buf; #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_read(pos:%ld, len:%ld) asize: %ld\n", (long)from, (long)len, (long)priv->asize); + printk(KERN_DEBUG "pmc551_read(pos:%ld, len:%ld) asize: %ld\n", + (long)from, (long)len, (long)priv->asize); #endif - end = from + len - 1; + end = from + len - 1; - /* Is it past the end? */ - if (end > mtd->size) { + /* Is it past the end? */ + if (end > mtd->size) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n", (long) end, (long)mtd->size); + printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n", + (long)end, (long)mtd->size); #endif - return -EINVAL; - } - - soff_hi = from & ~(priv->asize - 1); - eoff_hi = end & ~(priv->asize - 1); - soff_lo = from & (priv->asize - 1); - eoff_lo = end & (priv->asize - 1); - - pmc551_point (mtd, from, len, retlen, &ptr); - - if (soff_hi == eoff_hi) { - /* The whole thing fits within one access, so just one shot - will do it. */ - memcpy(copyto, ptr, len); - copyto += len; - } else { - /* We have to do multiple writes to get all the data - written. */ - while (soff_hi != eoff_hi) { + return -EINVAL; + } + + soff_hi = from & ~(priv->asize - 1); + eoff_hi = end & ~(priv->asize - 1); + soff_lo = from & (priv->asize - 1); + eoff_lo = end & (priv->asize - 1); + + pmc551_point(mtd, from, len, retlen, &ptr); + + if (soff_hi == eoff_hi) { + /* The whole thing fits within one access, so just one shot + will do it. */ + memcpy(copyto, ptr, len); + copyto += len; + } else { + /* We have to do multiple writes to get all the data + written. */ + while (soff_hi != eoff_hi) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk( KERN_DEBUG "pmc551_read() soff_hi: %ld, eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); + printk(KERN_DEBUG "pmc551_read() soff_hi: %ld, " + "eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); #endif - memcpy(copyto, ptr, priv->asize); - copyto += priv->asize; - if (soff_hi + priv->asize >= mtd->size) { - goto out; - } - soff_hi += priv->asize; - pmc551_point (mtd, soff_hi, priv->asize, retlen, &ptr); - } - memcpy(copyto, ptr, eoff_lo); - copyto += eoff_lo; - } - -out: + memcpy(copyto, ptr, priv->asize); + copyto += priv->asize; + if (soff_hi + priv->asize >= mtd->size) { + goto out; + } + soff_hi += priv->asize; + pmc551_point(mtd, soff_hi, priv->asize, retlen, &ptr); + } + memcpy(copyto, ptr, eoff_lo); + copyto += eoff_lo; + } + + out: #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_read() done\n"); #endif - *retlen = copyto - buf; - return 0; + *retlen = copyto - buf; + return 0; } -static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) { - struct mypriv *priv = mtd->priv; - u32 soff_hi, soff_lo; /* start address offset hi/lo */ - u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ - unsigned long end; + struct mypriv *priv = mtd->priv; + u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ + unsigned long end; u_char *ptr; - const u_char *copyfrom = buf; - + const u_char *copyfrom = buf; #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_write(pos:%ld, len:%ld) asize:%ld\n", (long)to, (long)len, (long)priv->asize); + printk(KERN_DEBUG "pmc551_write(pos:%ld, len:%ld) asize:%ld\n", + (long)to, (long)len, (long)priv->asize); #endif - end = to + len - 1; - /* Is it past the end? or did the u32 wrap? */ - if (end > mtd->size ) { + end = to + len - 1; + /* Is it past the end? or did the u32 wrap? */ + if (end > mtd->size) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, size: %ld, to: %ld)\n", (long) end, (long)mtd->size, (long)to); + printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, " + "size: %ld, to: %ld)\n", (long)end, (long)mtd->size, + (long)to); #endif - return -EINVAL; - } - - soff_hi = to & ~(priv->asize - 1); - eoff_hi = end & ~(priv->asize - 1); - soff_lo = to & (priv->asize - 1); - eoff_lo = end & (priv->asize - 1); - - pmc551_point (mtd, to, len, retlen, &ptr); - - if (soff_hi == eoff_hi) { - /* The whole thing fits within one access, so just one shot - will do it. */ - memcpy(ptr, copyfrom, len); - copyfrom += len; - } else { - /* We have to do multiple writes to get all the data - written. */ - while (soff_hi != eoff_hi) { + return -EINVAL; + } + + soff_hi = to & ~(priv->asize - 1); + eoff_hi = end & ~(priv->asize - 1); + soff_lo = to & (priv->asize - 1); + eoff_lo = end & (priv->asize - 1); + + pmc551_point(mtd, to, len, retlen, &ptr); + + if (soff_hi == eoff_hi) { + /* The whole thing fits within one access, so just one shot + will do it. */ + memcpy(ptr, copyfrom, len); + copyfrom += len; + } else { + /* We have to do multiple writes to get all the data + written. */ + while (soff_hi != eoff_hi) { #ifdef CONFIG_MTD_PMC551_DEBUG - printk( KERN_DEBUG "pmc551_write() soff_hi: %ld, eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); + printk(KERN_DEBUG "pmc551_write() soff_hi: %ld, " + "eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); #endif - memcpy(ptr, copyfrom, priv->asize); - copyfrom += priv->asize; - if (soff_hi >= mtd->size) { - goto out; - } - soff_hi += priv->asize; - pmc551_point (mtd, soff_hi, priv->asize, retlen, &ptr); - } - memcpy(ptr, copyfrom, eoff_lo); - copyfrom += eoff_lo; - } - -out: + memcpy(ptr, copyfrom, priv->asize); + copyfrom += priv->asize; + if (soff_hi >= mtd->size) { + goto out; + } + soff_hi += priv->asize; + pmc551_point(mtd, soff_hi, priv->asize, retlen, &ptr); + } + memcpy(ptr, copyfrom, eoff_lo); + copyfrom += eoff_lo; + } + + out: #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_write() done\n"); #endif - *retlen = copyfrom - buf; - return 0; + *retlen = copyfrom - buf; + return 0; } /* @@ -345,58 +356,58 @@ out: * mechanism * returns the size of the memory region found. */ -static u32 fixup_pmc551 (struct pci_dev *dev) +static u32 fixup_pmc551(struct pci_dev *dev) { #ifdef CONFIG_MTD_PMC551_BUGFIX - u32 dram_data; + u32 dram_data; #endif - u32 size, dcmd, cfg, dtmp; - u16 cmd, tmp, i; + u32 size, dcmd, cfg, dtmp; + u16 cmd, tmp, i; u8 bcmd, counter; - /* Sanity Check */ - if(!dev) { - return -ENODEV; - } + /* Sanity Check */ + if (!dev) { + return -ENODEV; + } /* * Attempt to reset the card * FIXME: Stop Spinning registers */ - counter=0; + counter = 0; /* unlock registers */ - pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5 ); + pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5); /* read in old data */ - pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd ); + pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd); /* bang the reset line up and down for a few */ - for(i=0;i<10;i++) { - counter=0; + for (i = 0; i < 10; i++) { + counter = 0; bcmd &= ~0x80; - while(counter++ < 100) { + while (counter++ < 100) { pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd); } - counter=0; + counter = 0; bcmd |= 0x80; - while(counter++ < 100) { + while (counter++ < 100) { pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd); } } - bcmd |= (0x40|0x20); + bcmd |= (0x40 | 0x20); pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd); - /* + /* * Take care and turn off the memory on the device while we * tweak the configurations */ - pci_read_config_word(dev, PCI_COMMAND, &cmd); - tmp = cmd & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY); - pci_write_config_word(dev, PCI_COMMAND, tmp); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + tmp = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, tmp); /* * Disable existing aperture before probing memory size */ pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd); - dtmp=(dcmd|PMC551_PCI_MEM_MAP_ENABLE|PMC551_PCI_MEM_MAP_REG_EN); + dtmp = (dcmd | PMC551_PCI_MEM_MAP_ENABLE | PMC551_PCI_MEM_MAP_REG_EN); pci_write_config_dword(dev, PMC551_PCI_MEM_MAP0, dtmp); /* * Grab old BAR0 config so that we can figure out memory size @@ -407,220 +418,230 @@ static u32 fixup_pmc551 (struct pci_dev *dev) * then write all 1's to the memory space, read back the result into * "size", and then write back all the old config. */ - pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &cfg ); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &cfg); #ifndef CONFIG_MTD_PMC551_BUGFIX - pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, ~0 ); - pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &size ); - size = (size&PCI_BASE_ADDRESS_MEM_MASK); - size &= ~(size-1); - pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg ); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, ~0); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &size); + size = (size & PCI_BASE_ADDRESS_MEM_MASK); + size &= ~(size - 1); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, cfg); #else - /* - * Get the size of the memory by reading all the DRAM size values - * and adding them up. - * - * KLUDGE ALERT: the boards we are using have invalid column and - * row mux values. We fix them here, but this will break other - * memory configurations. - */ - pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data); - size = PMC551_DRAM_BLK_GET_SIZE(dram_data); - dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); - dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); - pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data); - - pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data); - size += PMC551_DRAM_BLK_GET_SIZE(dram_data); - dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); - dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); - pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data); - - pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data); - size += PMC551_DRAM_BLK_GET_SIZE(dram_data); - dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); - dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); - pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data); - - pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data); - size += PMC551_DRAM_BLK_GET_SIZE(dram_data); - dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); - dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); - pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data); - - /* - * Oops .. something went wrong - */ - if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) { - return -ENODEV; - } -#endif /* CONFIG_MTD_PMC551_BUGFIX */ - - if ((cfg&PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { - return -ENODEV; + /* + * Get the size of the memory by reading all the DRAM size values + * and adding them up. + * + * KLUDGE ALERT: the boards we are using have invalid column and + * row mux values. We fix them here, but this will break other + * memory configurations. + */ + pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data); + size = PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data); + + pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data); + size += PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data); + + pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data); + size += PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data); + + pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data); + size += PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data); + + /* + * Oops .. something went wrong + */ + if ((size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) { + return -ENODEV; + } +#endif /* CONFIG_MTD_PMC551_BUGFIX */ + + if ((cfg & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { + return -ENODEV; } - /* - * Precharge Dram - */ - pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 ); - pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf ); - - /* - * Wait until command has gone through - * FIXME: register spinning issue - */ - do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd ); - if(counter++ > 100)break; - } while ( (PCI_COMMAND_IO) & cmd ); - - /* + /* + * Precharge Dram + */ + pci_write_config_word(dev, PMC551_SDRAM_MA, 0x0400); + pci_write_config_word(dev, PMC551_SDRAM_CMD, 0x00bf); + + /* + * Wait until command has gone through + * FIXME: register spinning issue + */ + do { + pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd); + if (counter++ > 100) + break; + } while ((PCI_COMMAND_IO) & cmd); + + /* * Turn on auto refresh * The loop is taken directly from Ramix's example code. I assume that * this must be held high for some duration of time, but I can find no * documentation refrencing the reasons why. - */ - for ( i = 1; i<=8 ; i++) { - pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df); - - /* - * Make certain command has gone through - * FIXME: register spinning issue - */ - counter=0; - do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd); - if(counter++ > 100)break; - } while ( (PCI_COMMAND_IO) & cmd ); - } - - pci_write_config_word ( dev, PMC551_SDRAM_MA, 0x0020); - pci_write_config_word ( dev, PMC551_SDRAM_CMD, 0x0ff); - - /* - * Wait until command completes - * FIXME: register spinning issue - */ - counter=0; - do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd); - if(counter++ > 100)break; - } while ( (PCI_COMMAND_IO) & cmd ); - - pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd); - dcmd |= 0x02000000; - pci_write_config_dword ( dev, PMC551_DRAM_CFG, dcmd); - - /* - * Check to make certain fast back-to-back, if not - * then set it so - */ - pci_read_config_word( dev, PCI_STATUS, &cmd); - if((cmd&PCI_COMMAND_FAST_BACK) == 0) { - cmd |= PCI_COMMAND_FAST_BACK; - pci_write_config_word( dev, PCI_STATUS, cmd); - } - - /* - * Check to make certain the DEVSEL is set correctly, this device - * has a tendancy to assert DEVSEL and TRDY when a write is performed - * to the memory when memory is read-only - */ - if((cmd&PCI_STATUS_DEVSEL_MASK) != 0x0) { - cmd &= ~PCI_STATUS_DEVSEL_MASK; - pci_write_config_word( dev, PCI_STATUS, cmd ); - } - /* - * Set to be prefetchable and put everything back based on old cfg. + */ + for (i = 1; i <= 8; i++) { + pci_write_config_word(dev, PMC551_SDRAM_CMD, 0x0df); + + /* + * Make certain command has gone through + * FIXME: register spinning issue + */ + counter = 0; + do { + pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd); + if (counter++ > 100) + break; + } while ((PCI_COMMAND_IO) & cmd); + } + + pci_write_config_word(dev, PMC551_SDRAM_MA, 0x0020); + pci_write_config_word(dev, PMC551_SDRAM_CMD, 0x0ff); + + /* + * Wait until command completes + * FIXME: register spinning issue + */ + counter = 0; + do { + pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd); + if (counter++ > 100) + break; + } while ((PCI_COMMAND_IO) & cmd); + + pci_read_config_dword(dev, PMC551_DRAM_CFG, &dcmd); + dcmd |= 0x02000000; + pci_write_config_dword(dev, PMC551_DRAM_CFG, dcmd); + + /* + * Check to make certain fast back-to-back, if not + * then set it so + */ + pci_read_config_word(dev, PCI_STATUS, &cmd); + if ((cmd & PCI_COMMAND_FAST_BACK) == 0) { + cmd |= PCI_COMMAND_FAST_BACK; + pci_write_config_word(dev, PCI_STATUS, cmd); + } + + /* + * Check to make certain the DEVSEL is set correctly, this device + * has a tendancy to assert DEVSEL and TRDY when a write is performed + * to the memory when memory is read-only + */ + if ((cmd & PCI_STATUS_DEVSEL_MASK) != 0x0) { + cmd &= ~PCI_STATUS_DEVSEL_MASK; + pci_write_config_word(dev, PCI_STATUS, cmd); + } + /* + * Set to be prefetchable and put everything back based on old cfg. * it's possible that the reset of the V370PDC nuked the original * setup - */ + */ + /* + cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH; + pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg ); + */ + /* - cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH; - pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg ); - */ - - /* - * Turn PCI memory and I/O bus access back on - */ - pci_write_config_word( dev, PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_IO ); + * Turn PCI memory and I/O bus access back on + */ + pci_write_config_word(dev, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_IO); #ifdef CONFIG_MTD_PMC551_DEBUG - /* - * Some screen fun - */ - printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%llx\n", - (size<1024)?size:(size<1048576)?size>>10:size>>20, - (size<1024)?'B':(size<1048576)?'K':'M', - size, ((dcmd&(0x1<<3)) == 0)?"non-":"", - (unsigned long long)((dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK)); - - /* - * Check to see the state of the memory - */ - pci_read_config_dword( dev, PMC551_DRAM_BLK0, &dcmd ); - printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n" - "pmc551: DRAM_BLK0 Size: %d at %d\n" - "pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n", - (((0x1<<1)&dcmd) == 0)?"RW":"RO", - (((0x1<<0)&dcmd) == 0)?"Off":"On", - PMC551_DRAM_BLK_GET_SIZE(dcmd), - ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); - - pci_read_config_dword( dev, PMC551_DRAM_BLK1, &dcmd ); - printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n" - "pmc551: DRAM_BLK1 Size: %d at %d\n" - "pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n", - (((0x1<<1)&dcmd) == 0)?"RW":"RO", - (((0x1<<0)&dcmd) == 0)?"Off":"On", - PMC551_DRAM_BLK_GET_SIZE(dcmd), - ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); - - pci_read_config_dword( dev, PMC551_DRAM_BLK2, &dcmd ); - printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n" - "pmc551: DRAM_BLK2 Size: %d at %d\n" - "pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n", - (((0x1<<1)&dcmd) == 0)?"RW":"RO", - (((0x1<<0)&dcmd) == 0)?"Off":"On", - PMC551_DRAM_BLK_GET_SIZE(dcmd), - ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); - - pci_read_config_dword( dev, PMC551_DRAM_BLK3, &dcmd ); - printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n" - "pmc551: DRAM_BLK3 Size: %d at %d\n" - "pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n", - (((0x1<<1)&dcmd) == 0)?"RW":"RO", - (((0x1<<0)&dcmd) == 0)?"Off":"On", - PMC551_DRAM_BLK_GET_SIZE(dcmd), - ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); - - pci_read_config_word( dev, PCI_COMMAND, &cmd ); - printk( KERN_DEBUG "pmc551: Memory Access %s\n", - (((0x1<<1)&cmd) == 0)?"off":"on" ); - printk( KERN_DEBUG "pmc551: I/O Access %s\n", - (((0x1<<0)&cmd) == 0)?"off":"on" ); - - pci_read_config_word( dev, PCI_STATUS, &cmd ); - printk( KERN_DEBUG "pmc551: Devsel %s\n", - ((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast": - ((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium": - ((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" ); - - printk( KERN_DEBUG "pmc551: %sFast Back-to-Back\n", - ((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" ); - - pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd ); - printk( KERN_DEBUG "pmc551: EEPROM is under %s control\n" - "pmc551: System Control Register is %slocked to PCI access\n" - "pmc551: System Control Register is %slocked to EEPROM access\n", - (bcmd&0x1)?"software":"hardware", - (bcmd&0x20)?"":"un", (bcmd&0x40)?"":"un"); + /* + * Some screen fun + */ + printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at " + "0x%llx\n", (size < 1024) ? size : (size < 1048576) ? + size >> 10 : size >> 20, + (size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size, + ((dcmd & (0x1 << 3)) == 0) ? "non-" : "", + (unsigned long long)pci_resource_start(dev, 0)); + + /* + * Check to see the state of the memory + */ + pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dcmd); + printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n" + "pmc551: DRAM_BLK0 Size: %d at %d\n" + "pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n", + (((0x1 << 1) & dcmd) == 0) ? "RW" : "RO", + (((0x1 << 0) & dcmd) == 0) ? "Off" : "On", + PMC551_DRAM_BLK_GET_SIZE(dcmd), + ((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7), + ((dcmd >> 9) & 0xF)); + + pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dcmd); + printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n" + "pmc551: DRAM_BLK1 Size: %d at %d\n" + "pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n", + (((0x1 << 1) & dcmd) == 0) ? "RW" : "RO", + (((0x1 << 0) & dcmd) == 0) ? "Off" : "On", + PMC551_DRAM_BLK_GET_SIZE(dcmd), + ((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7), + ((dcmd >> 9) & 0xF)); + + pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dcmd); + printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n" + "pmc551: DRAM_BLK2 Size: %d at %d\n" + "pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n", + (((0x1 << 1) & dcmd) == 0) ? "RW" : "RO", + (((0x1 << 0) & dcmd) == 0) ? "Off" : "On", + PMC551_DRAM_BLK_GET_SIZE(dcmd), + ((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7), + ((dcmd >> 9) & 0xF)); + + pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dcmd); + printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n" + "pmc551: DRAM_BLK3 Size: %d at %d\n" + "pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n", + (((0x1 << 1) & dcmd) == 0) ? "RW" : "RO", + (((0x1 << 0) & dcmd) == 0) ? "Off" : "On", + PMC551_DRAM_BLK_GET_SIZE(dcmd), + ((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7), + ((dcmd >> 9) & 0xF)); + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + printk(KERN_DEBUG "pmc551: Memory Access %s\n", + (((0x1 << 1) & cmd) == 0) ? "off" : "on"); + printk(KERN_DEBUG "pmc551: I/O Access %s\n", + (((0x1 << 0) & cmd) == 0) ? "off" : "on"); + + pci_read_config_word(dev, PCI_STATUS, &cmd); + printk(KERN_DEBUG "pmc551: Devsel %s\n", + ((PCI_STATUS_DEVSEL_MASK & cmd) == 0x000) ? "Fast" : + ((PCI_STATUS_DEVSEL_MASK & cmd) == 0x200) ? "Medium" : + ((PCI_STATUS_DEVSEL_MASK & cmd) == 0x400) ? "Slow" : "Invalid"); + + printk(KERN_DEBUG "pmc551: %sFast Back-to-Back\n", + ((PCI_COMMAND_FAST_BACK & cmd) == 0) ? "Not " : ""); + + pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd); + printk(KERN_DEBUG "pmc551: EEPROM is under %s control\n" + "pmc551: System Control Register is %slocked to PCI access\n" + "pmc551: System Control Register is %slocked to EEPROM access\n", + (bcmd & 0x1) ? "software" : "hardware", + (bcmd & 0x20) ? "" : "un", (bcmd & 0x40) ? "" : "un"); #endif - return size; + return size; } /* * Kernel version specific module stuffages */ - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Ferrell <mferrell@mvista.com>"); MODULE_DESCRIPTION(PMC551_VERSION); @@ -628,11 +649,11 @@ MODULE_DESCRIPTION(PMC551_VERSION); /* * Stuff these outside the ifdef so as to not bust compiled in driver support */ -static int msize=0; +static int msize = 0; #if defined(CONFIG_MTD_PMC551_APERTURE_SIZE) -static int asize=CONFIG_MTD_PMC551_APERTURE_SIZE +static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE #else -static int asize=0; +static int asize = 0; #endif module_param(msize, int, 0); @@ -645,164 +666,174 @@ MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); */ static int __init init_pmc551(void) { - struct pci_dev *PCI_Device = NULL; - struct mypriv *priv; - int count, found=0; - struct mtd_info *mtd; - u32 length = 0; - - if(msize) { - msize = (1 << (ffs(msize) - 1))<<20; - if (msize > (1<<30)) { - printk(KERN_NOTICE "pmc551: Invalid memory size [%d]\n", msize); + struct pci_dev *PCI_Device = NULL; + struct mypriv *priv; + int count, found = 0; + struct mtd_info *mtd; + u32 length = 0; + + if (msize) { + msize = (1 << (ffs(msize) - 1)) << 20; + if (msize > (1 << 30)) { + printk(KERN_NOTICE "pmc551: Invalid memory size [%d]\n", + msize); return -EINVAL; } } - if(asize) { - asize = (1 << (ffs(asize) - 1))<<20; - if (asize > (1<<30) ) { - printk(KERN_NOTICE "pmc551: Invalid aperture size [%d]\n", asize); + if (asize) { + asize = (1 << (ffs(asize) - 1)) << 20; + if (asize > (1 << 30)) { + printk(KERN_NOTICE "pmc551: Invalid aperture size " + "[%d]\n", asize); return -EINVAL; } } - printk(KERN_INFO PMC551_VERSION); - - /* - * PCU-bus chipset probe. - */ - for( count = 0; count < MAX_MTD_DEVICES; count++ ) { - - if ((PCI_Device = pci_find_device(PCI_VENDOR_ID_V3_SEMI, - PCI_DEVICE_ID_V3_SEMI_V370PDC, - PCI_Device ) ) == NULL) { - break; - } - - printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%llx\n", - (unsigned long long)PCI_Device->resource[0].start); - - /* - * The PMC551 device acts VERY weird if you don't init it - * first. i.e. it will not correctly report devsel. If for - * some reason the sdram is in a wrote-protected state the - * device will DEVSEL when it is written to causing problems - * with the oldproc.c driver in - * some kernels (2.2.*) - */ - if((length = fixup_pmc551(PCI_Device)) <= 0) { - printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n"); - break; - } + printk(KERN_INFO PMC551_VERSION); + + /* + * PCU-bus chipset probe. + */ + for (count = 0; count < MAX_MTD_DEVICES; count++) { + + if ((PCI_Device = pci_get_device(PCI_VENDOR_ID_V3_SEMI, + PCI_DEVICE_ID_V3_SEMI_V370PDC, + PCI_Device)) == NULL) { + break; + } + + printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%llx\n", + (unsigned long long)pci_resource_start(PCI_Device, 0)); + + /* + * The PMC551 device acts VERY weird if you don't init it + * first. i.e. it will not correctly report devsel. If for + * some reason the sdram is in a wrote-protected state the + * device will DEVSEL when it is written to causing problems + * with the oldproc.c driver in + * some kernels (2.2.*) + */ + if ((length = fixup_pmc551(PCI_Device)) <= 0) { + printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n"); + break; + } /* * This is needed until the driver is capable of reading the * onboard I2C SROM to discover the "real" memory size. */ - if(msize) { + if (msize) { length = msize; - printk(KERN_NOTICE "pmc551: Using specified memory size 0x%x\n", length); + printk(KERN_NOTICE "pmc551: Using specified memory " + "size 0x%x\n", length); } else { msize = length; } - mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!mtd) { - printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n"); - break; - } - - memset(mtd, 0, sizeof(struct mtd_info)); - - priv = kmalloc (sizeof(struct mypriv), GFP_KERNEL); - if (!priv) { - printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n"); - kfree(mtd); - break; - } - memset(priv, 0, sizeof(*priv)); - mtd->priv = priv; - priv->dev = PCI_Device; - - if(asize > length) { - printk(KERN_NOTICE "pmc551: reducing aperture size to fit %dM\n",length>>20); + mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd) { + printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " + "device.\n"); + break; + } + + priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL); + if (!priv) { + printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " + "device.\n"); + kfree(mtd); + break; + } + mtd->priv = priv; + priv->dev = PCI_Device; + + if (asize > length) { + printk(KERN_NOTICE "pmc551: reducing aperture size to " + "fit %dM\n", length >> 20); priv->asize = asize = length; } else if (asize == 0 || asize == length) { - printk(KERN_NOTICE "pmc551: Using existing aperture size %dM\n", length>>20); + printk(KERN_NOTICE "pmc551: Using existing aperture " + "size %dM\n", length >> 20); priv->asize = asize = length; } else { - printk(KERN_NOTICE "pmc551: Using specified aperture size %dM\n", asize>>20); + printk(KERN_NOTICE "pmc551: Using specified aperture " + "size %dM\n", asize >> 20); priv->asize = asize; } - priv->start = ioremap(((PCI_Device->resource[0].start) - & PCI_BASE_ADDRESS_MEM_MASK), - priv->asize); + priv->start = pci_iomap(PCI_Device, 0, priv->asize); if (!priv->start) { printk(KERN_NOTICE "pmc551: Unable to map IO space\n"); - kfree(mtd->priv); - kfree(mtd); + kfree(mtd->priv); + kfree(mtd); break; } - #ifdef CONFIG_MTD_PMC551_DEBUG - printk( KERN_DEBUG "pmc551: setting aperture to %d\n", - ffs(priv->asize>>20)-1); + printk(KERN_DEBUG "pmc551: setting aperture to %d\n", + ffs(priv->asize >> 20) - 1); #endif - priv->base_map0 = ( PMC551_PCI_MEM_MAP_REG_EN - | PMC551_PCI_MEM_MAP_ENABLE - | (ffs(priv->asize>>20)-1)<<4 ); - priv->curr_map0 = priv->base_map0; - pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, - priv->curr_map0 ); + priv->base_map0 = (PMC551_PCI_MEM_MAP_REG_EN + | PMC551_PCI_MEM_MAP_ENABLE + | (ffs(priv->asize >> 20) - 1) << 4); + priv->curr_map0 = priv->base_map0; + pci_write_config_dword(priv->dev, PMC551_PCI_MEM_MAP0, + priv->curr_map0); #ifdef CONFIG_MTD_PMC551_DEBUG - printk( KERN_DEBUG "pmc551: aperture set to %d\n", - (priv->base_map0 & 0xF0)>>4 ); + printk(KERN_DEBUG "pmc551: aperture set to %d\n", + (priv->base_map0 & 0xF0) >> 4); #endif - mtd->size = msize; - mtd->flags = MTD_CAP_RAM; - mtd->erase = pmc551_erase; - mtd->read = pmc551_read; - mtd->write = pmc551_write; - mtd->point = pmc551_point; - mtd->unpoint = pmc551_unpoint; - mtd->type = MTD_RAM; - mtd->name = "PMC551 RAM board"; - mtd->erasesize = 0x10000; - mtd->writesize = 1; - mtd->owner = THIS_MODULE; - - if (add_mtd_device(mtd)) { - printk(KERN_NOTICE "pmc551: Failed to register new device\n"); - iounmap(priv->start); - kfree(mtd->priv); - kfree(mtd); - break; - } - printk(KERN_NOTICE "Registered pmc551 memory device.\n"); - printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", - priv->asize>>20, - priv->start, - priv->start + priv->asize); - printk(KERN_NOTICE "Total memory is %d%c\n", - (length<1024)?length: - (length<1048576)?length>>10:length>>20, - (length<1024)?'B':(length<1048576)?'K':'M'); + mtd->size = msize; + mtd->flags = MTD_CAP_RAM; + mtd->erase = pmc551_erase; + mtd->read = pmc551_read; + mtd->write = pmc551_write; + mtd->point = pmc551_point; + mtd->unpoint = pmc551_unpoint; + mtd->type = MTD_RAM; + mtd->name = "PMC551 RAM board"; + mtd->erasesize = 0x10000; + mtd->writesize = 1; + mtd->owner = THIS_MODULE; + + if (add_mtd_device(mtd)) { + printk(KERN_NOTICE "pmc551: Failed to register new " + "device\n"); + pci_iounmap(PCI_Device, priv->start); + kfree(mtd->priv); + kfree(mtd); + break; + } + + /* Keep a reference as the add_mtd_device worked */ + pci_dev_get(PCI_Device); + + printk(KERN_NOTICE "Registered pmc551 memory device.\n"); + printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", + priv->asize >> 20, + priv->start, priv->start + priv->asize); + printk(KERN_NOTICE "Total memory is %d%c\n", + (length < 1024) ? length : + (length < 1048576) ? length >> 10 : length >> 20, + (length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M'); priv->nextpmc551 = pmc551list; pmc551list = mtd; found++; - } + } + + /* Exited early, reference left over */ + if (PCI_Device) + pci_dev_put(PCI_Device); - if( !pmc551list ) { - printk(KERN_NOTICE "pmc551: not detected\n"); - return -ENODEV; - } else { + if (!pmc551list) { + printk(KERN_NOTICE "pmc551: not detected\n"); + return -ENODEV; + } else { printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found); - return 0; + return 0; } } @@ -811,23 +842,24 @@ static int __init init_pmc551(void) */ static void __exit cleanup_pmc551(void) { - int found=0; - struct mtd_info *mtd; + int found = 0; + struct mtd_info *mtd; struct mypriv *priv; - while((mtd=pmc551list)) { + while ((mtd = pmc551list)) { priv = mtd->priv; pmc551list = priv->nextpmc551; - if(priv->start) { - printk (KERN_DEBUG "pmc551: unmapping %dM starting at 0x%p\n", - priv->asize>>20, priv->start); - iounmap (priv->start); + if (priv->start) { + printk(KERN_DEBUG "pmc551: unmapping %dM starting at " + "0x%p\n", priv->asize >> 20, priv->start); + pci_iounmap(priv->dev, priv->start); } + pci_dev_put(priv->dev); - kfree (mtd->priv); - del_mtd_device (mtd); - kfree (mtd); + kfree(mtd->priv); + del_mtd_device(mtd); + kfree(mtd); found++; } diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 64d1b6a..24747bd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -447,14 +447,6 @@ config MTD_DC21285 21285 bridge used with Intel's StrongARM processors. More info at <http://www.intel.com/design/bridge/docs/21285_documentation.htm>. -config MTD_IQ80310 - tristate "CFI Flash device mapped on the XScale IQ80310 board" - depends on MTD_CFI && ARCH_IQ80310 - help - This enables access routines for the flash chips on the Intel XScale - IQ80310 evaluation board. If you have one of these boards and would - like to use the flash chips on it, say 'Y'. - config MTD_IXP4XX tristate "CFI Flash device mapped on Intel IXP4xx based systems" depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP4XX diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index ab71f17..191c192 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o -obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 447955b..797caff 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -57,6 +57,7 @@ static void amd76xrom_cleanup(struct amd76xrom_window *window) /* Disable writes through the rom window */ pci_read_config_byte(window->pdev, 0x40, &byte); pci_write_config_byte(window->pdev, 0x40, byte & ~1); + pci_dev_put(window->pdev); } /* Free all of the mtd devices */ @@ -91,7 +92,7 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, struct amd76xrom_map_info *map = NULL; unsigned long map_top; - /* Remember the pci dev I find the window in */ + /* Remember the pci dev I find the window in - already have a ref */ window->pdev = pdev; /* Assume the rom window is properly setup, and find it's size */ @@ -302,7 +303,7 @@ static int __init init_amd76xrom(void) struct pci_device_id *id; pdev = NULL; for(id = amd76xrom_pci_tbl; id->vendor; id++) { - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) { break; } diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c index d95ae58..642d96b 100644 --- a/drivers/mtd/maps/arctic-mtd.c +++ b/drivers/mtd/maps/arctic-mtd.c @@ -96,6 +96,8 @@ static struct mtd_partition arctic_partitions[PARTITIONS] = { static int __init init_arctic_mtd(void) { + int err = 0; + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); arctic_mtd_map.virt = ioremap(PADDR, SIZE); @@ -109,12 +111,20 @@ init_arctic_mtd(void) printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); - if (!arctic_mtd) + if (!arctic_mtd) { + iounmap((void *) arctic_mtd_map.virt); return -ENXIO; + } arctic_mtd->owner = THIS_MODULE; - return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); + err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); + if (err) { + printk("%s: add_mtd_partitions failed\n", NAME); + iounmap((void *) arctic_mtd_map.virt); + } + + return err; } static void __exit diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c index 5df7361..a64b1a5 100644 --- a/drivers/mtd/maps/beech-mtd.c +++ b/drivers/mtd/maps/beech-mtd.c @@ -72,6 +72,8 @@ static struct mtd_partition beech_partitions[2] = { static int __init init_beech_mtd(void) { + int err = 0; + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); beech_mtd_map.virt = ioremap(PADDR, SIZE); @@ -86,12 +88,20 @@ init_beech_mtd(void) printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); - if (!beech_mtd) + if (!beech_mtd) { + iounmap((void *) beech_mtd_map.virt); return -ENXIO; + } beech_mtd->owner = THIS_MODULE; - return add_mtd_partitions(beech_mtd, beech_partitions, 2); + err = add_mtd_partitions(beech_mtd, beech_partitions, 2); + if (err) { + printk("%s: add_mtd_partitions failed\n", NAME); + iounmap((void *) beech_mtd_map.virt); + } + + return err; } static void __exit diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index aa56def..d6bef10 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -171,7 +171,14 @@ int __init init_cstm_mips_ixx(void) cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr; cstm_mips_ixx_map[i].virt = ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); if (!cstm_mips_ixx_map[i].virt) { + int j = 0; printk(KERN_WARNING "Failed to ioremap\n"); + for (j = 0; j < i; j++) { + if (cstm_mips_ixx_map[j].virt) { + iounmap((void *)cstm_mips_ixx_map[j].virt); + cstm_mips_ixx_map[j].virt = 0; + } + } return -EIO; } cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name; @@ -204,8 +211,15 @@ int __init init_cstm_mips_ixx(void) cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd; add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions); } - else - return -ENXIO; + else { + for (i = 0; i < PHYSMAP_NUMBER; i++) { + if (cstm_mips_ixx_map[i].virt) { + iounmap((void *)cstm_mips_ixx_map[i].virt); + cstm_mips_ixx_map[i].virt = 0; + } + } + return -ENXIO; + } } return 0; } diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c index 641e1dd..1488bb9 100644 --- a/drivers/mtd/maps/ebony.c +++ b/drivers/mtd/maps/ebony.c @@ -108,6 +108,7 @@ int __init init_ebony(void) ARRAY_SIZE(ebony_small_partitions)); } else { printk("map probe failed for flash\n"); + iounmap(ebony_small_map.virt); return -ENXIO; } @@ -117,6 +118,7 @@ int __init init_ebony(void) if (!ebony_large_map.virt) { printk("Failed to ioremap flash\n"); + iounmap(ebony_small_map.virt); return -EIO; } @@ -129,6 +131,8 @@ int __init init_ebony(void) ARRAY_SIZE(ebony_large_partitions)); } else { printk("map probe failed for flash\n"); + iounmap(ebony_small_map.virt); + iounmap(ebony_large_map.virt); return -ENXIO; } diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index c6bf4e1..7c50c27 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -218,8 +218,11 @@ int __init init_fortunet(void) map_regions[ix].map_info.size); if(!map_regions[ix].map_info.virt) { + int j = 0; printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", map_regions[ix].map_info.name); + for (j = 0 ; j < ix; j++) + iounmap(map_regions[j].map_info.virt); return -ENXIO; } simple_map_init(&map_regions[ix].map_info); diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index db4b570..2bb3e636 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -61,6 +61,7 @@ static void ichxrom_cleanup(struct ichxrom_window *window) /* Disable writes through the rom window */ pci_read_config_word(window->pdev, BIOS_CNTL, &word); pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1); + pci_dev_put(window->pdev); /* Free all of the mtd devices */ list_for_each_entry_safe(map, scratch, &window->maps, list) { @@ -355,7 +356,7 @@ static int __init init_ichxrom(void) pdev = NULL; for (id = ichxrom_pci_tbl; id->vendor; id++) { - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) { break; } diff --git a/drivers/mtd/maps/iq80310.c b/drivers/mtd/maps/iq80310.c deleted file mode 100644 index 62d9e87..0000000 --- a/drivers/mtd/maps/iq80310.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * $Id: iq80310.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $ - * - * Mapping for the Intel XScale IQ80310 evaluation board - * - * Author: Nicolas Pitre - * Copyright: (C) 2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <asm/io.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - - -#define WINDOW_ADDR 0 -#define WINDOW_SIZE 8*1024*1024 -#define BUSWIDTH 1 - -static struct mtd_info *mymtd; - -static struct map_info iq80310_map = { - .name = "IQ80310 flash", - .size = WINDOW_SIZE, - .bankwidth = BUSWIDTH, - .phys = WINDOW_ADDR -}; - -static struct mtd_partition iq80310_partitions[4] = { - { - .name = "Firmware", - .size = 0x00080000, - .offset = 0, - .mask_flags = MTD_WRITEABLE /* force read-only */ - },{ - .name = "Kernel", - .size = 0x000a0000, - .offset = 0x00080000, - },{ - .name = "Filesystem", - .size = 0x00600000, - .offset = 0x00120000 - },{ - .name = "RedBoot", - .size = 0x000e0000, - .offset = 0x00720000, - .mask_flags = MTD_WRITEABLE - } -}; - -static struct mtd_info *mymtd; -static struct mtd_partition *parsed_parts; -static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static int __init init_iq80310(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - int parsed_nr_parts = 0; - int ret; - - iq80310_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!iq80310_map.virt) { - printk("Failed to ioremap\n"); - return -EIO; - } - simple_map_init(&iq80310_map); - - mymtd = do_map_probe("cfi_probe", &iq80310_map); - if (!mymtd) { - iounmap((void *)iq80310_map.virt); - return -ENXIO; - } - mymtd->owner = THIS_MODULE; - - ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); - - if (ret > 0) - parsed_nr_parts = ret; - - if (parsed_nr_parts > 0) { - parts = parsed_parts; - nb_parts = parsed_nr_parts; - } else { - parts = iq80310_partitions; - nb_parts = ARRAY_SIZE(iq80310_partitions); - } - add_mtd_partitions(mymtd, parts, nb_parts); - return 0; -} - -static void __exit cleanup_iq80310(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - kfree(parsed_parts); - } - if (iq80310_map.virt) - iounmap((void *)iq80310_map.virt); -} - -module_init(init_iq80310); -module_exit(cleanup_iq80310); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); -MODULE_DESCRIPTION("MTD map driver for Intel XScale IQ80310 evaluation board"); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 986c586..7a828e3 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -253,7 +253,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) /* Use the fast version */ info->map.write = ixp4xx_write16, - err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, dev->resource->start); if (err > 0) { err = add_mtd_partitions(info->mtd, info->partitions, err); if(err) diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 6b784ef..67620ad 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -61,14 +61,17 @@ static int __init init_l440gx(void) struct resource *pm_iobase; __u16 word; - dev = pci_find_device(PCI_VENDOR_ID_INTEL, + dev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, NULL); - pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + pm_dev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + pci_dev_put(dev); + if (!dev || !pm_dev) { printk(KERN_NOTICE "L440GX flash mapping: failed to find PIIX4 ISA bridge, cannot continue\n"); + pci_dev_put(pm_dev); return -ENODEV; } @@ -76,6 +79,7 @@ static int __init init_l440gx(void) if (!l440gx_map.virt) { printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); + pci_dev_put(pm_dev); return -ENOMEM; } simple_map_init(&l440gx_map); @@ -99,8 +103,12 @@ static int __init init_l440gx(void) pm_iobase->start += iobase & ~1; pm_iobase->end += iobase & ~1; + pci_dev_put(pm_dev); + /* Allocate the resource region */ if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { + pci_dev_put(dev); + pci_dev_put(pm_dev); printk(KERN_WARNING "Could not allocate pm iobase resource\n"); iounmap(l440gx_map.virt); return -ENXIO; diff --git a/drivers/mtd/maps/lasat.c b/drivers/mtd/maps/lasat.c index 1c13d2d..e343763 100644 --- a/drivers/mtd/maps/lasat.c +++ b/drivers/mtd/maps/lasat.c @@ -79,6 +79,7 @@ static int __init init_lasat(void) return 0; } + iounmap(lasat_map.virt); return -ENXIO; } @@ -89,6 +90,7 @@ static void __exit cleanup_lasat(void) map_destroy(lasat_mtd); } if (lasat_map.virt) { + iounmap(lasat_map.virt); lasat_map.virt = 0; } } diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 0994b5b..198e840 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -277,6 +277,7 @@ int __init nettel_init(void) nettel_amd_map.virt = ioremap_nocache(amdaddr, maxsize); if (!nettel_amd_map.virt) { printk("SNAPGEAR: failed to ioremap() BOOTCS\n"); + iounmap(nettel_mmcrp); return(-EIO); } simple_map_init(&nettel_amd_map); @@ -337,7 +338,8 @@ int __init nettel_init(void) nettel_amd_map.virt = NULL; #else /* Only AMD flash supported */ - return(-ENXIO); + rc = -ENXIO; + goto out_unmap2; #endif } @@ -361,14 +363,15 @@ int __init nettel_init(void) nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); - return(-EIO); + rc = -EIO; + goto out_unmap2; } simple_map_init(&nettel_intel_map); intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); if (!intel_mtd) { - iounmap(nettel_intel_map.virt); - return(-ENXIO); + rc = -ENXIO; + goto out_unmap1; } /* Set PAR to the detected size */ @@ -394,13 +397,14 @@ int __init nettel_init(void) nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); - return(-EIO); + rc = -EIO; + goto out_unmap2; } intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); if (! intel_mtd) { - iounmap((void *) nettel_intel_map.virt); - return(-ENXIO); + rc = -ENXIO; + goto out_unmap1; } intel1size = intel_mtd->size - intel0size; @@ -456,6 +460,18 @@ int __init nettel_init(void) #endif return(rc); + +#ifdef CONFIG_MTD_CFI_INTELEXT +out_unmap1: + iounmap((void *) nettel_intel_map.virt); +#endif + +out_unmap2: + iounmap(nettel_mmcrp); + iounmap(nettel_amd_map.virt); + + return(rc); + } /****************************************************************************/ @@ -469,6 +485,10 @@ void __exit nettel_cleanup(void) del_mtd_partitions(amd_mtd); map_destroy(amd_mtd); } + if (nettel_mmcrp) { + iounmap(nettel_mmcrp); + nettel_mmcrp = NULL; + } if (nettel_amd_map.virt) { iounmap(nettel_amd_map.virt); nettel_amd_map.virt = NULL; diff --git a/drivers/mtd/maps/ocotea.c b/drivers/mtd/maps/ocotea.c index 2f07602..5522eac 100644 --- a/drivers/mtd/maps/ocotea.c +++ b/drivers/mtd/maps/ocotea.c @@ -97,6 +97,7 @@ int __init init_ocotea(void) ARRAY_SIZE(ocotea_small_partitions)); } else { printk("map probe failed for flash\n"); + iounmap(ocotea_small_map.virt); return -ENXIO; } @@ -106,6 +107,7 @@ int __init init_ocotea(void) if (!ocotea_large_map.virt) { printk("Failed to ioremap flash\n"); + iounmap(ocotea_small_map.virt); return -EIO; } @@ -118,6 +120,8 @@ int __init init_ocotea(void) ARRAY_SIZE(ocotea_large_partitions)); } else { printk("map probe failed for flash\n"); + iounmap(ocotea_small_map.virt); + iounmap(ocotea_large_map.virt); return -ENXIO; } diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index c861134..995347b 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -602,6 +602,10 @@ static int pcmciamtd_config(struct pcmcia_device *link) ret = pcmcia_request_configuration(link, &link->conf); if(ret != CS_SUCCESS) { cs_error(link, RequestConfiguration, ret); + if (dev->win_base) { + iounmap(dev->win_base); + dev->win_base = NULL; + } return -ENODEV; } diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 7799a25..bc7cc71 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -158,9 +158,42 @@ err_out: return err; } +#ifdef CONFIG_PM +static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state) +{ + struct physmap_flash_info *info = platform_get_drvdata(dev); + int ret = 0; + + if (info) + ret = info->mtd->suspend(info->mtd); + + return ret; +} + +static int physmap_flash_resume(struct platform_device *dev) +{ + struct physmap_flash_info *info = platform_get_drvdata(dev); + if (info) + info->mtd->resume(info->mtd); + return 0; +} + +static void physmap_flash_shutdown(struct platform_device *dev) +{ + struct physmap_flash_info *info = platform_get_drvdata(dev); + if (info && info->mtd->suspend(info->mtd) == 0) + info->mtd->resume(info->mtd); +} +#endif + static struct platform_driver physmap_flash_driver = { .probe = physmap_flash_probe, .remove = physmap_flash_remove, +#ifdef CONFIG_PM + .suspend = physmap_flash_suspend, + .resume = physmap_flash_resume, + .shutdown = physmap_flash_shutdown, +#endif .driver = { .name = "physmap-flash", }, diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index ec8fdae..2257d2b 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -126,6 +126,8 @@ static struct mtd_info *redwood_mtd; int __init init_redwood_flash(void) { + int err = 0; + printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); @@ -141,11 +143,18 @@ int __init init_redwood_flash(void) if (redwood_mtd) { redwood_mtd->owner = THIS_MODULE; - return add_mtd_partitions(redwood_mtd, + err = add_mtd_partitions(redwood_mtd, redwood_flash_partitions, NUM_REDWOOD_FLASH_PARTITIONS); + if (err) { + printk("init_redwood_flash: add_mtd_partitions failed\n"); + iounmap(redwood_flash_map.virt); + } + return err; + } + iounmap(redwood_flash_map.virt); return -ENXIO; } diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index 7d0fcf8..b8c1331 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c @@ -156,7 +156,7 @@ int __init init_sbc8240_mtd (void) }; int devicesfound = 0; - int i; + int i,j; for (i = 0; i < NUM_FLASH_BANKS; i++) { printk (KERN_NOTICE MSG_PREFIX @@ -166,6 +166,10 @@ int __init init_sbc8240_mtd (void) (unsigned long) ioremap (pt[i].addr, pt[i].size); if (!sbc8240_map[i].map_priv_1) { printk (MSG_PREFIX "failed to ioremap\n"); + for (j = 0; j < i; j++) { + iounmap((void *) sbc8240_map[j].map_priv_1); + sbc8240_map[j].map_priv_1 = 0; + } return -EIO; } simple_map_init(&sbc8240_mtd[i]); @@ -175,6 +179,11 @@ int __init init_sbc8240_mtd (void) if (sbc8240_mtd[i]) { sbc8240_mtd[i]->module = THIS_MODULE; devicesfound++; + } else { + if (sbc8240_map[i].map_priv_1) { + iounmap((void *) sbc8240_map[i].map_priv_1); + sbc8240_map[i].map_priv_1 = 0; + } } } diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 7391fd5..5e2bce2 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -87,19 +87,23 @@ static int __init init_scx200_docflash(void) printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n"); - if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, + if ((bridge = pci_get_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE, NULL)) == NULL) return -ENODEV; /* check that we have found the configuration block */ - if (!scx200_cb_present()) + if (!scx200_cb_present()) { + pci_dev_put(bridge); return -ENODEV; + } if (probe) { /* Try to use the present flash mapping if any */ pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base); pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl); + pci_dev_put(bridge); + pmr = inl(scx200_cb_base + SCx200_PMR); if (base == 0 @@ -127,6 +131,7 @@ static int __init init_scx200_docflash(void) return -ENOMEM; } } else { + pci_dev_put(bridge); for (u = size; u > 1; u >>= 1) ; if (u != 1) { diff --git a/drivers/mtd/maps/walnut.c b/drivers/mtd/maps/walnut.c index ec80eec..ca93212 100644 --- a/drivers/mtd/maps/walnut.c +++ b/drivers/mtd/maps/walnut.c @@ -68,6 +68,7 @@ int __init init_walnut(void) if (WALNUT_FLASH_ONBD_N(fpga_brds1)) { printk("The on-board flash is disabled (U79 sw 5)!"); + iounmap(fpga_status_adr); return -EIO; } if (WALNUT_FLASH_SRAM_SEL(fpga_brds1)) @@ -81,6 +82,7 @@ int __init init_walnut(void) if (!walnut_map.virt) { printk("Failed to ioremap flash.\n"); + iounmap(fpga_status_adr); return -EIO; } @@ -93,9 +95,11 @@ int __init init_walnut(void) ARRAY_SIZE(walnut_partitions)); } else { printk("map probe failed for flash\n"); + iounmap(fpga_status_adr); return -ENXIO; } + iounmap(fpga_status_adr); return 0; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 168d3ba..c4d26de 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -57,6 +57,16 @@ int add_mtd_device(struct mtd_info *mtd) mtd->index = i; mtd->usecount = 0; + /* Some chips always power up locked. Unlock them now */ + if ((mtd->flags & MTD_WRITEABLE) + && (mtd->flags & MTD_STUPID_LOCK) && mtd->unlock) { + if (mtd->unlock(mtd, 0, mtd->size)) + printk(KERN_WARNING + "%s: unlock failed, " + "writes may not work\n", + mtd->name); + } + DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); /* No need to get a refcount on the module containing the notifier, since we hold the mtd_table_mutex */ diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 3122833..09e421a 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -21,18 +21,7 @@ #include <linux/version.h> #include <asm/io.h> -/* fixme: this is ugly */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) #include <asm/mach-au1x00/au1xxx.h> -#else -#include <asm/au1000.h> -#ifdef CONFIG_MIPS_PB1550 -#include <asm/pb1550.h> -#endif -#ifdef CONFIG_MIPS_DB1550 -#include <asm/db1x00.h> -#endif -#endif /* * MTD structure for NAND controller diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index 516c0e5..12017f3 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -198,6 +198,9 @@ static void __exit ep7312_cleanup(void) /* Release resources, unregister device */ nand_release(ap7312_mtd); + /* Release io resource */ + iounmap((void *)this->IO_ADDR_R); + /* Free the MTD device structure */ kfree(ep7312_mtd); } diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index e5bd88f..039c759 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -168,7 +168,7 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; - chip->ecclayout = mtd->pl_chip->ecclayout; + chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout; mtd->mtd.priv = chip; mtd->mtd.owner = THIS_MODULE; } diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 22fa65c..eb7d4d4 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -276,6 +276,7 @@ static int __init ppchameleonevb_init(void) /* Scan to find existence of the device (it could not be mounted) */ if (nand_scan(ppchameleon_mtd, 1)) { iounmap((void *)ppchameleon_fio_base); + ppchameleon_fio_base = NULL; kfree(ppchameleon_mtd); goto nand_evb_init; } @@ -314,6 +315,8 @@ static int __init ppchameleonevb_init(void) ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!ppchameleonevb_mtd) { printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n"); + if (ppchameleon_fio_base) + iounmap(ppchameleon_fio_base); return -ENOMEM; } @@ -322,6 +325,8 @@ static int __init ppchameleonevb_init(void) if (!ppchameleonevb_fio_base) { printk("ioremap PPChameleonEVB NAND flash failed\n"); kfree(ppchameleonevb_mtd); + if (ppchameleon_fio_base) + iounmap(ppchameleon_fio_base); return -EIO; } @@ -378,6 +383,8 @@ static int __init ppchameleonevb_init(void) if (nand_scan(ppchameleonevb_mtd, 1)) { iounmap((void *)ppchameleonevb_fio_base); kfree(ppchameleonevb_mtd); + if (ppchameleon_fio_base) + iounmap(ppchameleon_fio_base); return -ENXIO; } #ifdef CONFIG_MTD_PARTITIONS diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c new file mode 100644 index 0000000..ddbf015 --- /dev/null +++ b/drivers/mtd/ssfdc.c @@ -0,0 +1,468 @@ +/* + * Linux driver for SSFDC Flash Translation Layer (Read only) + * (c) 2005 Eptar srl + * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com> + * + * Based on NTFL and MTDBLOCK_RO drivers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/hdreg.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/blktrans.h> + +struct ssfdcr_record { + struct mtd_blktrans_dev mbd; + int usecount; + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + int cis_block; /* block n. containing CIS/IDI */ + int erase_size; /* phys_block_size */ + unsigned short *logic_block_map; /* all zones (max 8192 phys blocks on + the 128MB) */ + int map_len; /* n. phys_blocks on the card */ +}; + +#define SSFDCR_MAJOR 257 +#define SSFDCR_PARTN_BITS 3 + +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 +#define OOB_SIZE 16 + +#define MAX_LOGIC_BLK_PER_ZONE 1000 +#define MAX_PHYS_BLK_PER_ZONE 1024 + +#define KB(x) ( (x) * 1024L ) +#define MB(x) ( KB(x) * 1024L ) + +/** CHS Table + 1MB 2MB 4MB 8MB 16MB 32MB 64MB 128MB +NCylinder 125 125 250 250 500 500 500 500 +NHead 4 4 4 4 4 8 8 16 +NSector 4 8 8 16 16 16 32 32 +SumSector 2,000 4,000 8,000 16,000 32,000 64,000 128,000 256,000 +SectorSize 512 512 512 512 512 512 512 512 +**/ + +typedef struct { + unsigned long size; + unsigned short cyl; + unsigned char head; + unsigned char sec; +} chs_entry_t; + +/* Must be ordered by size */ +static const chs_entry_t chs_table[] = { + { MB( 1), 125, 4, 4 }, + { MB( 2), 125, 4, 8 }, + { MB( 4), 250, 4, 8 }, + { MB( 8), 250, 4, 16 }, + { MB( 16), 500, 4, 16 }, + { MB( 32), 500, 8, 16 }, + { MB( 64), 500, 8, 32 }, + { MB(128), 500, 16, 32 }, + { 0 }, +}; + +static int get_chs(unsigned long size, unsigned short *cyl, unsigned char *head, + unsigned char *sec) +{ + int k; + int found = 0; + + k = 0; + while (chs_table[k].size > 0 && size > chs_table[k].size) + k++; + + if (chs_table[k].size > 0) { + if (cyl) + *cyl = chs_table[k].cyl; + if (head) + *head = chs_table[k].head; + if (sec) + *sec = chs_table[k].sec; + found = 1; + } + + return found; +} + +/* These bytes are the signature for the CIS/IDI sector */ +static const uint8_t cis_numbers[] = { + 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 +}; + +/* Read and check for a valid CIS sector */ +static int get_valid_cis_sector(struct mtd_info *mtd) +{ + int ret, k, cis_sector; + size_t retlen; + loff_t offset; + uint8_t sect_buf[SECTOR_SIZE]; + + /* + * Look for CIS/IDI sector on the first GOOD block (give up after 4 bad + * blocks). If the first good block doesn't contain CIS number the flash + * is not SSFDC formatted + */ + cis_sector = -1; + for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize) { + if (!mtd->block_isbad(mtd, offset)) { + ret = mtd->read(mtd, offset, SECTOR_SIZE, &retlen, + sect_buf); + + /* CIS pattern match on the sector buffer */ + if ( ret < 0 || retlen != SECTOR_SIZE ) { + printk(KERN_WARNING + "SSFDC_RO:can't read CIS/IDI sector\n"); + } else if ( !memcmp(sect_buf, cis_numbers, + sizeof(cis_numbers)) ) { + /* Found */ + cis_sector = (int)(offset >> SECTOR_SHIFT); + } else { + DEBUG(MTD_DEBUG_LEVEL1, + "SSFDC_RO: CIS/IDI sector not found" + " on %s (mtd%d)\n", mtd->name, + mtd->index); + } + break; + } + } + + return cis_sector; +} + +/* Read physical sector (wrapper to MTD_READ) */ +static int read_physical_sector(struct mtd_info *mtd, uint8_t *sect_buf, + int sect_no) +{ + int ret; + size_t retlen; + loff_t offset = (loff_t)sect_no << SECTOR_SHIFT; + + ret = mtd->read(mtd, offset, SECTOR_SIZE, &retlen, sect_buf); + if (ret < 0 || retlen != SECTOR_SIZE) + return -1; + + return 0; +} + +/* Read redundancy area (wrapper to MTD_READ_OOB */ +static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf) +{ + struct mtd_oob_ops ops; + int ret; + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.len = OOB_SIZE; + ops.oobbuf = buf; + ops.datbuf = NULL; + + ret = mtd->read_oob(mtd, offs, &ops); + if (ret < 0 || ops.retlen != OOB_SIZE) + return -1; + + return 0; +} + +/* Parity calculator on a word of n bit size */ +static int get_parity(int number, int size) +{ + int k; + int parity; + + parity = 1; + for (k = 0; k < size; k++) { + parity += (number >> k); + parity &= 1; + } + return parity; +} + +/* Read and validate the logical block address field stored in the OOB */ +static int get_logical_address(uint8_t *oob_buf) +{ + int block_address, parity; + int offset[2] = {6, 11}; /* offset of the 2 address fields within OOB */ + int j; + int ok = 0; + + /* + * Look for the first valid logical address + * Valid address has fixed pattern on most significant bits and + * parity check + */ + for (j = 0; j < ARRAY_SIZE(offset); j++) { + block_address = ((int)oob_buf[offset[j]] << 8) | + oob_buf[offset[j]+1]; + + /* Check for the signature bits in the address field (MSBits) */ + if ((block_address & ~0x7FF) == 0x1000) { + parity = block_address & 0x01; + block_address &= 0x7FF; + block_address >>= 1; + + if (get_parity(block_address, 10) != parity) { + DEBUG(MTD_DEBUG_LEVEL0, + "SSFDC_RO: logical address field%d" + "parity error(0x%04X)\n", j+1, + block_address); + } else { + ok = 1; + break; + } + } + } + + if ( !ok ) + block_address = -2; + + DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: get_logical_address() %d\n", + block_address); + + return block_address; +} + +/* Build the logic block map */ +static int build_logical_block_map(struct ssfdcr_record *ssfdc) +{ + unsigned long offset; + uint8_t oob_buf[OOB_SIZE]; + int ret, block_address, phys_block; + struct mtd_info *mtd = ssfdc->mbd.mtd; + + DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: build_block_map() nblks=%d (%luK)\n", + ssfdc->map_len, (unsigned long)ssfdc->map_len * + ssfdc->erase_size / 1024 ); + + /* Scan every physical block, skip CIS block */ + for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len; + phys_block++) { + offset = (unsigned long)phys_block * ssfdc->erase_size; + if (mtd->block_isbad(mtd, offset)) + continue; /* skip bad blocks */ + + ret = read_raw_oob(mtd, offset, oob_buf); + if (ret < 0) { + DEBUG(MTD_DEBUG_LEVEL0, + "SSFDC_RO: mtd read_oob() failed at %lu\n", + offset); + return -1; + } + block_address = get_logical_address(oob_buf); + + /* Skip invalid addresses */ + if (block_address >= 0 && + block_address < MAX_LOGIC_BLK_PER_ZONE) { + int zone_index; + + zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE; + block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE; + ssfdc->logic_block_map[block_address] = + (unsigned short)phys_block; + + DEBUG(MTD_DEBUG_LEVEL2, + "SSFDC_RO: build_block_map() phys_block=%d," + "logic_block_addr=%d, zone=%d\n", + phys_block, block_address, zone_index); + } + } + return 0; +} + +static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct ssfdcr_record *ssfdc; + int cis_sector; + + /* Check for small page NAND flash */ + if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE) + return; + + /* Check for SSDFC format by reading CIS/IDI sector */ + cis_sector = get_valid_cis_sector(mtd); + if (cis_sector == -1) + return; + + ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL); + if (!ssfdc) { + printk(KERN_WARNING + "SSFDC_RO: out of memory for data structures\n"); + return; + } + + ssfdc->mbd.mtd = mtd; + ssfdc->mbd.devnum = -1; + ssfdc->mbd.blksize = SECTOR_SIZE; + ssfdc->mbd.tr = tr; + ssfdc->mbd.readonly = 1; + + ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT); + ssfdc->erase_size = mtd->erasesize; + ssfdc->map_len = mtd->size / mtd->erasesize; + + DEBUG(MTD_DEBUG_LEVEL1, + "SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n", + ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len, + (ssfdc->map_len + MAX_PHYS_BLK_PER_ZONE - 1) / + MAX_PHYS_BLK_PER_ZONE); + + /* Set geometry */ + ssfdc->heads = 16; + ssfdc->sectors = 32; + get_chs( mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors); + ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) / + ((long)ssfdc->sectors * (long)ssfdc->heads)); + + DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n", + ssfdc->cylinders, ssfdc->heads , ssfdc->sectors, + (long)ssfdc->cylinders * (long)ssfdc->heads * + (long)ssfdc->sectors ); + + ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders * + (long)ssfdc->sectors; + + /* Allocate logical block map */ + ssfdc->logic_block_map = kmalloc( sizeof(ssfdc->logic_block_map[0]) * + ssfdc->map_len, GFP_KERNEL); + if (!ssfdc->logic_block_map) { + printk(KERN_WARNING + "SSFDC_RO: out of memory for data structures\n"); + goto out_err; + } + memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) * + ssfdc->map_len); + + /* Build logical block map */ + if (build_logical_block_map(ssfdc) < 0) + goto out_err; + + /* Register device + partitions */ + if (add_mtd_blktrans_dev(&ssfdc->mbd)) + goto out_err; + + printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n", + ssfdc->mbd.devnum + 'a', mtd->index, mtd->name); + return; + +out_err: + kfree(ssfdc->logic_block_map); + kfree(ssfdc); +} + +static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev) +{ + struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev; + + DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: remove_dev (i=%d)\n", dev->devnum); + + del_mtd_blktrans_dev(dev); + kfree(ssfdc->logic_block_map); + kfree(ssfdc); +} + +static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, + unsigned long logic_sect_no, char *buf) +{ + struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev; + int sectors_per_block, offset, block_address; + + sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT; + offset = (int)(logic_sect_no % sectors_per_block); + block_address = (int)(logic_sect_no / sectors_per_block); + + DEBUG(MTD_DEBUG_LEVEL3, + "SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d," + " block_addr=%d\n", logic_sect_no, sectors_per_block, offset, + block_address); + + if (block_address >= ssfdc->map_len) + BUG(); + + block_address = ssfdc->logic_block_map[block_address]; + + DEBUG(MTD_DEBUG_LEVEL3, + "SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n", + block_address); + + if (block_address < 0xffff) { + unsigned long sect_no; + + sect_no = (unsigned long)block_address * sectors_per_block + + offset; + + DEBUG(MTD_DEBUG_LEVEL3, + "SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n", + sect_no); + + if (read_physical_sector( ssfdc->mbd.mtd, buf, sect_no ) < 0) + return -EIO; + } else { + memset(buf, 0xff, SECTOR_SIZE); + } + + return 0; +} + +static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev; + + DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n", + ssfdc->cylinders, ssfdc->heads, ssfdc->sectors); + + geo->heads = ssfdc->heads; + geo->sectors = ssfdc->sectors; + geo->cylinders = ssfdc->cylinders; + + return 0; +} + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +static struct mtd_blktrans_ops ssfdcr_tr = { + .name = "ssfdc", + .major = SSFDCR_MAJOR, + .part_bits = SSFDCR_PARTN_BITS, + .getgeo = ssfdcr_getgeo, + .readsect = ssfdcr_readsect, + .add_mtd = ssfdcr_add_mtd, + .remove_dev = ssfdcr_remove_dev, + .owner = THIS_MODULE, +}; + +static int __init init_ssfdcr(void) +{ + printk(KERN_INFO "SSFDC read-only Flash Translation layer\n"); + + return register_mtd_blktrans(&ssfdcr_tr); +} + +static void __exit cleanup_ssfdcr(void) +{ + deregister_mtd_blktrans(&ssfdcr_tr); +} + +module_init(init_ssfdcr); +module_exit(cleanup_ssfdcr); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>"); +MODULE_DESCRIPTION("Flash Translation Layer for read-only SSFDC SmartMedia card"); diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index 6fad83f..7116096 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -1264,7 +1264,8 @@ static int __devinit bmac_probe(struct macio_dev *mdev, const struct of_device_i { int j, rev, ret; struct bmac_data *bp; - unsigned char *addr; + const unsigned char *prop_addr; + unsigned char addr[6]; struct net_device *dev; int is_bmac_plus = ((int)match->data) != 0; @@ -1272,14 +1273,16 @@ static int __devinit bmac_probe(struct macio_dev *mdev, const struct of_device_i printk(KERN_ERR "BMAC: can't use, need 3 addrs and 3 intrs\n"); return -ENODEV; } - addr = get_property(macio_get_of_node(mdev), "mac-address", NULL); - if (addr == NULL) { - addr = get_property(macio_get_of_node(mdev), "local-mac-address", NULL); - if (addr == NULL) { + prop_addr = get_property(macio_get_of_node(mdev), "mac-address", NULL); + if (prop_addr == NULL) { + prop_addr = get_property(macio_get_of_node(mdev), + "local-mac-address", NULL); + if (prop_addr == NULL) { printk(KERN_ERR "BMAC: Can't get mac-address\n"); return -ENODEV; } } + memcpy(addr, prop_addr, sizeof(addr)); dev = alloc_etherdev(PRIV_BYTES); if (!dev) { diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 0464e78..e56eac8 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -702,7 +702,8 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) desc[3].desc, desc[4].desc, desc[5].desc, - correlator); + correlator, + &correlator); } while ((lpar_rc == H_BUSY) && (retry_count--)); if(lpar_rc != H_SUCCESS && lpar_rc != H_DROPPED) { diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h index 8385bf8..f5b25bf 100644 --- a/drivers/net/ibmveth.h +++ b/drivers/net/ibmveth.h @@ -41,16 +41,6 @@ #define IbmVethMcastRemoveFilter 0x2UL #define IbmVethMcastClearFilterTable 0x3UL -/* hcall numbers */ -#define H_VIO_SIGNAL 0x104 -#define H_REGISTER_LOGICAL_LAN 0x114 -#define H_FREE_LOGICAL_LAN 0x118 -#define H_ADD_LOGICAL_LAN_BUFFER 0x11C -#define H_SEND_LOGICAL_LAN 0x120 -#define H_MULTICAST_CTRL 0x130 -#define H_CHANGE_LOGICAL_LAN_MAC 0x14C -#define H_FREE_LOGICAL_LAN_BUFFER 0x1D4 - /* hcall macros */ #define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \ plpar_hcall_norets(H_REGISTER_LOGICAL_LAN, ua, buflst, rxq, fltlst, mac) @@ -61,8 +51,21 @@ #define h_add_logical_lan_buffer(ua, buf) \ plpar_hcall_norets(H_ADD_LOGICAL_LAN_BUFFER, ua, buf) -#define h_send_logical_lan(ua, buf1, buf2, buf3, buf4, buf5, buf6, correlator) \ - plpar_hcall_8arg_2ret(H_SEND_LOGICAL_LAN, ua, buf1, buf2, buf3, buf4, buf5, buf6, correlator, &correlator) +static inline long h_send_logical_lan(unsigned long unit_address, + unsigned long desc1, unsigned long desc2, unsigned long desc3, + unsigned long desc4, unsigned long desc5, unsigned long desc6, + unsigned long corellator_in, unsigned long *corellator_out) +{ + long rc; + unsigned long retbuf[PLPAR_HCALL9_BUFSIZE]; + + rc = plpar_hcall9(H_SEND_LOGICAL_LAN, retbuf, unit_address, desc1, + desc2, desc3, desc4, desc5, desc6, corellator_in); + + *corellator_out = retbuf[0]; + + return rc; +} #define h_multicast_ctrl(ua, cmd, mac) \ plpar_hcall_norets(H_MULTICAST_CTRL, ua, cmd, mac) diff --git a/drivers/net/mace.c b/drivers/net/mace.c index 29e4b5a..5d80e0e 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -113,7 +113,7 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i struct device_node *mace = macio_get_of_node(mdev); struct net_device *dev; struct mace_data *mp; - unsigned char *addr; + const unsigned char *addr; int j, rev, rc = -EBUSY; if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) { diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 8890721..d64e718 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1697,10 +1697,10 @@ spider_net_setup_phy(struct spider_net_card *card) */ static int spider_net_download_firmware(struct spider_net_card *card, - u8 *firmware_ptr) + const void *firmware_ptr) { int sequencer, i; - u32 *fw_ptr = (u32 *)firmware_ptr; + const u32 *fw_ptr = firmware_ptr; /* stop sequencers */ spider_net_write_reg(card, SPIDER_NET_GSINIT, @@ -1757,7 +1757,7 @@ spider_net_init_firmware(struct spider_net_card *card) { struct firmware *firmware = NULL; struct device_node *dn; - u8 *fw_prop = NULL; + const u8 *fw_prop = NULL; int err = -ENOENT; int fw_size; @@ -1783,7 +1783,7 @@ try_host_fw: if (!dn) goto out_err; - fw_prop = (u8 *)get_property(dn, "firmware", &fw_size); + fw_prop = get_property(dn, "firmware", &fw_size); if (!fw_prop) goto out_err; @@ -1986,7 +1986,7 @@ spider_net_setup_netdev(struct spider_net_card *card) struct net_device *netdev = card->netdev; struct device_node *dn; struct sockaddr addr; - u8 *mac; + const u8 *mac; SET_MODULE_OWNER(netdev); SET_NETDEV_DEV(netdev, &card->pdev->dev); @@ -2019,7 +2019,7 @@ spider_net_setup_netdev(struct spider_net_card *card) if (!dn) return -EIO; - mac = (u8 *)get_property(dn, "local-mac-address", NULL); + mac = get_property(dn, "local-mac-address", NULL); if (!mac) return -EIO; memcpy(addr.sa_data, mac, ETH_ALEN); diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index b70bbd7..d7b1d18 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -2896,7 +2896,7 @@ static int __devinit gem_get_device_address(struct gem *gp) if (use_idprom) memcpy(dev->dev_addr, idprom->id_ethaddr, 6); #elif defined(CONFIG_PPC_PMAC) - unsigned char *addr; + const unsigned char *addr; addr = get_property(gp->of_node, "local-mac-address", NULL); if (addr == NULL) { diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 076bd6d..7288a3e 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -176,16 +176,16 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe return 0; } -static int get_children_props(struct device_node *dn, int **drc_indexes, - int **drc_names, int **drc_types, int **drc_power_domains) +static int get_children_props(struct device_node *dn, const int **drc_indexes, + const int **drc_names, const int **drc_types, + const int **drc_power_domains) { - int *indexes, *names; - int *types, *domains; + const int *indexes, *names, *types, *domains; - indexes = (int *) get_property(dn, "ibm,drc-indexes", NULL); - names = (int *) get_property(dn, "ibm,drc-names", NULL); - types = (int *) get_property(dn, "ibm,drc-types", NULL); - domains = (int *) get_property(dn, "ibm,drc-power-domains", NULL); + indexes = get_property(dn, "ibm,drc-indexes", NULL); + names = get_property(dn, "ibm,drc-names", NULL); + types = get_property(dn, "ibm,drc-types", NULL); + domains = get_property(dn, "ibm,drc-power-domains", NULL); if (!indexes || !names || !types || !domains) { /* Slot does not have dynamically-removable children */ @@ -212,13 +212,13 @@ static int get_children_props(struct device_node *dn, int **drc_indexes, int rpaphp_get_drc_props(struct device_node *dn, int *drc_index, char **drc_name, char **drc_type, int *drc_power_domain) { - int *indexes, *names; - int *types, *domains; - unsigned int *my_index; + const int *indexes, *names; + const int *types, *domains; + const unsigned int *my_index; char *name_tmp, *type_tmp; int i, rc; - my_index = (int *) get_property(dn, "ibm,my-drc-index", NULL); + my_index = get_property(dn, "ibm,my-drc-index", NULL); if (!my_index) { /* Node isn't DLPAR/hotplug capable */ return -EINVAL; @@ -265,10 +265,10 @@ static int is_php_type(char *drc_type) return 1; } -static int is_php_dn(struct device_node *dn, int **indexes, int **names, - int **types, int **power_domains) +static int is_php_dn(struct device_node *dn, const int **indexes, + const int **names, const int **types, const int **power_domains) { - int *drc_types; + const int *drc_types; int rc; rc = get_children_props(dn, indexes, names, &drc_types, power_domains); @@ -296,7 +296,7 @@ int rpaphp_add_slot(struct device_node *dn) struct slot *slot; int retval = 0; int i; - int *indexes, *names, *types, *power_domains; + const int *indexes, *names, *types, *power_domains; char *name, *type; dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name); diff --git a/drivers/s390/Kconfig b/drivers/s390/Kconfig index 4d36208..ae89b9b8 100644 --- a/drivers/s390/Kconfig +++ b/drivers/s390/Kconfig @@ -213,17 +213,35 @@ config MONREADER help Character device driver for reading z/VM monitor service records +config MONWRITER + tristate "API for writing z/VM monitor service records" + default "m" + help + Character device driver for writing z/VM monitor service records + endmenu menu "Cryptographic devices" -config Z90CRYPT +config ZCRYPT tristate "Support for PCI-attached cryptographic adapters" - default "m" - help + select ZCRYPT_MONOLITHIC if ZCRYPT="y" + default "m" + help Select this option if you want to use a PCI-attached cryptographic - adapter like the PCI Cryptographic Accelerator (PCICA) or the PCI - Cryptographic Coprocessor (PCICC). This option is also available - as a module called z90crypt.ko. + adapter like: + + PCI Cryptographic Accelerator (PCICA) + + PCI Cryptographic Coprocessor (PCICC) + + PCI-X Cryptographic Coprocessor (PCIXCC) + + Crypto Express2 Coprocessor (CEX2C) + + Crypto Express2 Accelerator (CEX2A) + +config ZCRYPT_MONOLITHIC + bool "Monolithic zcrypt module" + depends on ZCRYPT="m" + help + Select this option if you want to have a single module z90crypt.ko + that contains all parts of the crypto device driver (ap bus, + request router and all the card drivers). endmenu diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 25c1ef6..d0647d1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -184,7 +184,7 @@ dasd_state_known_to_basic(struct dasd_device * device) device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, 8 * sizeof (long)); debug_register_view(device->debug_area, &debug_sprintf_view); - debug_set_level(device->debug_area, DBF_EMERG); + debug_set_level(device->debug_area, DBF_WARNING); DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); device->state = DASD_STATE_BASIC; @@ -893,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) device = (struct dasd_device *) cqr->device; if (device == NULL || - device != dasd_device_from_cdev(cdev) || + device != dasd_device_from_cdev_locked(cdev) || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); @@ -970,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* first of all check for state change pending interrupt */ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; if ((irb->scsw.dstat & mask) == mask) { - device = dasd_device_from_cdev(cdev); + device = dasd_device_from_cdev_locked(cdev); if (!IS_ERR(device)) { dasd_handle_state_change_pending(device); dasd_put_device(device); @@ -2169,7 +2169,7 @@ dasd_init(void) goto failed; } debug_register_view(dasd_debug_area, &debug_sprintf_view); - debug_set_level(dasd_debug_area, DBF_EMERG); + debug_set_level(dasd_debug_area, DBF_WARNING); DBF_EVENT(DBF_EMERG, "%s", "debug area created"); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 9af02c7..91cf971 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -258,8 +258,12 @@ dasd_parse_keyword( char *parsestring ) { return residual_str; } if (strncmp("nopav", parsestring, length) == 0) { - dasd_nopav = 1; - MESSAGE(KERN_INFO, "%s", "disable PAV mode"); + if (MACHINE_IS_VM) + MESSAGE(KERN_INFO, "%s", "'nopav' not supported on VM"); + else { + dasd_nopav = 1; + MESSAGE(KERN_INFO, "%s", "disable PAV mode"); + } return residual_str; } if (strncmp("fixedbuffers", parsestring, length) == 0) { @@ -523,17 +527,17 @@ dasd_create_device(struct ccw_device *cdev) { struct dasd_devmap *devmap; struct dasd_device *device; + unsigned long flags; int rc; devmap = dasd_devmap_from_cdev(cdev); if (IS_ERR(devmap)) return (void *) devmap; - cdev->dev.driver_data = devmap; device = dasd_alloc_device(); if (IS_ERR(device)) return device; - atomic_set(&device->ref_count, 2); + atomic_set(&device->ref_count, 3); spin_lock(&dasd_devmap_lock); if (!devmap->device) { @@ -552,6 +556,11 @@ dasd_create_device(struct ccw_device *cdev) dasd_free_device(device); return ERR_PTR(rc); } + + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + cdev->dev.driver_data = device; + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); + return device; } @@ -569,6 +578,7 @@ dasd_delete_device(struct dasd_device *device) { struct ccw_device *cdev; struct dasd_devmap *devmap; + unsigned long flags; /* First remove device pointer from devmap. */ devmap = dasd_find_busid(device->cdev->dev.bus_id); @@ -582,9 +592,16 @@ dasd_delete_device(struct dasd_device *device) devmap->device = NULL; spin_unlock(&dasd_devmap_lock); - /* Drop ref_count by 2, one for the devmap reference and - * one for the passed reference. */ - atomic_sub(2, &device->ref_count); + /* Disconnect dasd_device structure from ccw_device structure. */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + device->cdev->dev.driver_data = NULL; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + + /* + * Drop ref_count by 3, one for the devmap reference, one for + * the cdev reference and one for the passed reference. + */ + atomic_sub(3, &device->ref_count); /* Wait for reference counter to drop to zero. */ wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0); @@ -593,9 +610,6 @@ dasd_delete_device(struct dasd_device *device) cdev = device->cdev; device->cdev = NULL; - /* Disconnect dasd_devmap structure from ccw_device structure. */ - cdev->dev.driver_data = NULL; - /* Put ccw_device structure. */ put_device(&cdev->dev); @@ -615,21 +629,32 @@ dasd_put_device_wake(struct dasd_device *device) /* * Return dasd_device structure associated with cdev. + * This function needs to be called with the ccw device + * lock held. It can be used from interrupt context. + */ +struct dasd_device * +dasd_device_from_cdev_locked(struct ccw_device *cdev) +{ + struct dasd_device *device = cdev->dev.driver_data; + + if (!device) + return ERR_PTR(-ENODEV); + dasd_get_device(device); + return device; +} + +/* + * Return dasd_device structure associated with cdev. */ struct dasd_device * dasd_device_from_cdev(struct ccw_device *cdev) { - struct dasd_devmap *devmap; struct dasd_device *device; + unsigned long flags; - device = ERR_PTR(-ENODEV); - spin_lock(&dasd_devmap_lock); - devmap = cdev->dev.driver_data; - if (devmap && devmap->device) { - device = devmap->device; - dasd_get_device(device); - } - spin_unlock(&dasd_devmap_lock); + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + device = dasd_device_from_cdev_locked(cdev); + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); return device; } @@ -730,16 +755,17 @@ static ssize_t dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct dasd_devmap *devmap; - char *dname; + struct dasd_device *device; + ssize_t len; - spin_lock(&dasd_devmap_lock); - dname = "none"; - devmap = dev->driver_data; - if (devmap && devmap->device && devmap->device->discipline) - dname = devmap->device->discipline->name; - spin_unlock(&dasd_devmap_lock); - return snprintf(buf, PAGE_SIZE, "%s\n", dname); + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (!IS_ERR(device) && device->discipline) { + len = snprintf(buf, PAGE_SIZE, "%s\n", + device->discipline->name); + dasd_put_device(device); + } else + len = snprintf(buf, PAGE_SIZE, "none\n"); + return len; } static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index da65f1b..e0bf30e 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -678,7 +678,7 @@ int __init dasd_eer_init(void) return 0; } -void __exit dasd_eer_exit(void) +void dasd_eer_exit(void) { WARN_ON(misc_deregister(&dasd_eer_dev) != 0); } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 3ccf06d..9f52004 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -534,6 +534,7 @@ int dasd_add_sysfs_files(struct ccw_device *); void dasd_remove_sysfs_files(struct ccw_device *); struct dasd_device *dasd_device_from_cdev(struct ccw_device *); +struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *); struct dasd_device *dasd_device_from_devindex(int); int dasd_parse(void); diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index ca7d51f..cab2c73 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -453,7 +453,7 @@ static int __init xpram_init(void) PRINT_WARN("No expanded memory available\n"); return -ENODEV; } - xpram_pages = xpram_highest_page_index(); + xpram_pages = xpram_highest_page_index() + 1; PRINT_INFO(" %u pages expanded memory found (%lu KB).\n", xpram_pages, (unsigned long) xpram_pages*4); rc = xpram_setup_sizes(xpram_pages); diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 0c0162ff..c3e97b4 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o +obj-$(CONFIG_MONWRITER) += monwriter.o diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c new file mode 100644 index 0000000..1e3939a --- /dev/null +++ b/drivers/s390/char/monwriter.c @@ -0,0 +1,292 @@ +/* + * drivers/s390/char/monwriter.c + * + * Character device driver for writing z/VM *MONITOR service records. + * + * Copyright (C) IBM Corp. 2006 + * + * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com> + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/ctype.h> +#include <linux/poll.h> +#include <asm/uaccess.h> +#include <asm/ebcdic.h> +#include <asm/io.h> +#include <asm/appldata.h> +#include <asm/monwriter.h> + +#define MONWRITE_MAX_DATALEN 4024 + +static int mon_max_bufs = 255; + +struct mon_buf { + struct list_head list; + struct monwrite_hdr hdr; + int diag_done; + char *data; +}; + +struct mon_private { + struct list_head list; + struct monwrite_hdr hdr; + size_t hdr_to_read; + size_t data_to_read; + struct mon_buf *current_buf; + int mon_buf_count; +}; + +/* + * helper functions + */ + +static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn) +{ + struct appldata_product_id id; + int rc; + + strcpy(id.prod_nr, "LNXAPPL"); + id.prod_fn = myhdr->applid; + id.record_nr = myhdr->record_num; + id.version_nr = myhdr->version; + id.release_nr = myhdr->release; + id.mod_lvl = myhdr->mod_level; + rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen); + if (rc <= 0) + return rc; + if (rc == 5) + return -EPERM; + printk("DIAG X'DC' error with return code: %i\n", rc); + return -EINVAL; +} + +static inline struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv, + struct monwrite_hdr *monhdr) +{ + struct mon_buf *entry, *next; + + list_for_each_entry_safe(entry, next, &monpriv->list, list) + if (entry->hdr.applid == monhdr->applid && + entry->hdr.record_num == monhdr->record_num && + entry->hdr.version == monhdr->version && + entry->hdr.release == monhdr->release && + entry->hdr.mod_level == monhdr->mod_level) + return entry; + return NULL; +} + +static int monwrite_new_hdr(struct mon_private *monpriv) +{ + struct monwrite_hdr *monhdr = &monpriv->hdr; + struct mon_buf *monbuf; + int rc; + + if (monhdr->datalen > MONWRITE_MAX_DATALEN || + monhdr->mon_function > MONWRITE_START_CONFIG || + monhdr->hdrlen != sizeof(struct monwrite_hdr)) + return -EINVAL; + monbuf = monwrite_find_hdr(monpriv, monhdr); + if (monbuf) { + if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) { + monhdr->datalen = monbuf->hdr.datalen; + rc = monwrite_diag(monhdr, monbuf->data, + APPLDATA_STOP_REC); + list_del(&monbuf->list); + monpriv->mon_buf_count--; + kfree(monbuf->data); + kfree(monbuf); + monbuf = NULL; + } + } else { + if (monpriv->mon_buf_count >= mon_max_bufs) + return -ENOSPC; + monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL); + if (!monbuf) + return -ENOMEM; + monbuf->data = kzalloc(monbuf->hdr.datalen, + GFP_KERNEL | GFP_DMA); + if (!monbuf->data) { + kfree(monbuf); + return -ENOMEM; + } + monbuf->hdr = *monhdr; + list_add_tail(&monbuf->list, &monpriv->list); + monpriv->mon_buf_count++; + } + monpriv->current_buf = monbuf; + return 0; +} + +static int monwrite_new_data(struct mon_private *monpriv) +{ + struct monwrite_hdr *monhdr = &monpriv->hdr; + struct mon_buf *monbuf = monpriv->current_buf; + int rc = 0; + + switch (monhdr->mon_function) { + case MONWRITE_START_INTERVAL: + if (!monbuf->diag_done) { + rc = monwrite_diag(monhdr, monbuf->data, + APPLDATA_START_INTERVAL_REC); + monbuf->diag_done = 1; + } + break; + case MONWRITE_START_CONFIG: + if (!monbuf->diag_done) { + rc = monwrite_diag(monhdr, monbuf->data, + APPLDATA_START_CONFIG_REC); + monbuf->diag_done = 1; + } + break; + case MONWRITE_GEN_EVENT: + rc = monwrite_diag(monhdr, monbuf->data, + APPLDATA_GEN_EVENT_REC); + list_del(&monpriv->current_buf->list); + kfree(monpriv->current_buf->data); + kfree(monpriv->current_buf); + monpriv->current_buf = NULL; + break; + default: + /* monhdr->mon_function is checked in monwrite_new_hdr */ + BUG(); + } + return rc; +} + +/* + * file operations + */ + +static int monwrite_open(struct inode *inode, struct file *filp) +{ + struct mon_private *monpriv; + + monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); + if (!monpriv) + return -ENOMEM; + INIT_LIST_HEAD(&monpriv->list); + monpriv->hdr_to_read = sizeof(monpriv->hdr); + filp->private_data = monpriv; + return nonseekable_open(inode, filp); +} + +static int monwrite_close(struct inode *inode, struct file *filp) +{ + struct mon_private *monpriv = filp->private_data; + struct mon_buf *entry, *next; + + list_for_each_entry_safe(entry, next, &monpriv->list, list) { + if (entry->hdr.mon_function != MONWRITE_GEN_EVENT) + monwrite_diag(&entry->hdr, entry->data, + APPLDATA_STOP_REC); + monpriv->mon_buf_count--; + list_del(&entry->list); + kfree(entry->data); + kfree(entry); + } + kfree(monpriv); + return 0; +} + +static ssize_t monwrite_write(struct file *filp, const char __user *data, + size_t count, loff_t *ppos) +{ + struct mon_private *monpriv = filp->private_data; + size_t len, written; + void *to; + int rc; + + for (written = 0; written < count; ) { + if (monpriv->hdr_to_read) { + len = min(count - written, monpriv->hdr_to_read); + to = (char *) &monpriv->hdr + + sizeof(monpriv->hdr) - monpriv->hdr_to_read; + if (copy_from_user(to, data + written, len)) { + rc = -EFAULT; + goto out_error; + } + monpriv->hdr_to_read -= len; + written += len; + if (monpriv->hdr_to_read > 0) + continue; + rc = monwrite_new_hdr(monpriv); + if (rc) + goto out_error; + monpriv->data_to_read = monpriv->current_buf ? + monpriv->current_buf->hdr.datalen : 0; + } + + if (monpriv->data_to_read) { + len = min(count - written, monpriv->data_to_read); + to = monpriv->current_buf->data + + monpriv->hdr.datalen - monpriv->data_to_read; + if (copy_from_user(to, data + written, len)) { + rc = -EFAULT; + goto out_error; + } + monpriv->data_to_read -= len; + written += len; + if (monpriv->data_to_read > 0) + continue; + rc = monwrite_new_data(monpriv); + if (rc) + goto out_error; + } + monpriv->hdr_to_read = sizeof(monpriv->hdr); + } + return written; + +out_error: + monpriv->data_to_read = 0; + monpriv->hdr_to_read = sizeof(struct monwrite_hdr); + return rc; +} + +static struct file_operations monwrite_fops = { + .owner = THIS_MODULE, + .open = &monwrite_open, + .release = &monwrite_close, + .write = &monwrite_write, +}; + +static struct miscdevice mon_dev = { + .name = "monwriter", + .fops = &monwrite_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +/* + * module init/exit + */ + +static int __init mon_init(void) +{ + if (MACHINE_IS_VM) + return misc_register(&mon_dev); + else + return -ENODEV; +} + +static void __exit mon_exit(void) +{ + WARN_ON(misc_deregister(&mon_dev) != 0); +} + +module_init(mon_init); +module_exit(mon_exit); + +module_param_named(max_bufs, mon_max_bufs, int, 0644); +MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers" + "that can be active at one time"); + +MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>"); +MODULE_DESCRIPTION("Character device driver for writing z/VM " + "APPLDATA monitor records."); +MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index 19762f3..1678b6c 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2004,2005 IBM Corporation - * Interface implementation for communication with the v/VM control program + * Interface implementation for communication with the z/VM control program * Author(s): Christian Borntraeger <cborntra@de.ibm.com> * * diff --git a/drivers/s390/char/vmcp.h b/drivers/s390/char/vmcp.h index 87389e7..8a5975f 100644 --- a/drivers/s390/char/vmcp.h +++ b/drivers/s390/char/vmcp.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2005 IBM Corporation - * Interface implementation for communication with the v/VM control program + * Interface implementation for communication with the z/VM control program * Version 1.0 * Author(s): Christian Borntraeger <cborntra@de.ibm.com> * diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index c28444a..3bb4e47 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -256,7 +256,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) /* trigger path verification. */ if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - else if (sch->vpm == mask) + else if (sch->lpm == mask) goto out_unreg; out_unlock: spin_unlock_irq(&sch->lock); @@ -378,6 +378,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) if (chp_mask == 0) { spin_unlock_irq(&sch->lock); + put_device(&sch->dev); return 0; } old_lpm = sch->lpm; @@ -392,7 +393,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) spin_unlock_irq(&sch->lock); put_device(&sch->dev); - return (res_data->fla_mask == 0xffff) ? -ENODEV : 0; + return 0; } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 89320c1..2e2882d 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -16,11 +16,10 @@ #include <linux/device.h> #include <linux/kernel_stat.h> #include <linux/interrupt.h> - #include <asm/cio.h> #include <asm/delay.h> #include <asm/irq.h> - +#include <asm/setup.h> #include "airq.h" #include "cio.h" #include "css.h" @@ -192,7 +191,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ sch->orb.pfch = sch->options.prefetch == 0; sch->orb.spnd = sch->options.suspend; sch->orb.ssic = sch->options.suspend && sch->options.inter; - sch->orb.lpm = (lpm != 0) ? (lpm & sch->opm) : sch->lpm; + sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm; #ifdef CONFIG_64BIT /* * for 64 bit we always support 64 bit IDAWs with 4k page size only @@ -570,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) sch->opm = 0xff; if (!cio_is_console(sch->schid)) chsc_validate_chpids(sch); - sch->lpm = sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom & - sch->opm; + sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, "Detected device %04x on subchannel 0.%x.%04X" @@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid) return -EBUSY; } -extern void do_reipl(unsigned long devno); -static int -__shutdown_subchannel_easy(struct subchannel_id schid, void *data) +struct sch_match_id { + struct subchannel_id schid; + struct ccw_dev_id devid; + int rc; +}; + +static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid, + void *data) { struct schib schib; + struct sch_match_id *match_id = data; if (stsch_err(schid, &schib)) return -ENXIO; + if (match_id && schib.pmcw.dnv && + (schib.pmcw.dev == match_id->devid.devno) && + (schid.ssid == match_id->devid.ssid)) { + match_id->schid = schid; + match_id->rc = 0; + } if (!schib.pmcw.ena) return 0; switch(__disable_subchannel_easy(schid, &schib)) { @@ -864,18 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data) return 0; } -void -clear_all_subchannels(void) +static int clear_all_subchannels_and_match(struct ccw_dev_id *devid, + struct subchannel_id *schid) { + struct sch_match_id match_id; + + match_id.devid = *devid; + match_id.rc = -ENODEV; local_irq_disable(); - for_each_subchannel(__shutdown_subchannel_easy, NULL); + for_each_subchannel(__shutdown_subchannel_easy_and_match, &match_id); + if (match_id.rc == 0) + *schid = match_id.schid; + return match_id.rc; } + +void clear_all_subchannels(void) +{ + local_irq_disable(); + for_each_subchannel(__shutdown_subchannel_easy_and_match, NULL); +} + +extern void do_reipl_asm(__u32 schid); + /* Make sure all subchannels are quiet before we re-ipl an lpar. */ -void -reipl(unsigned long devno) +void reipl_ccw_dev(struct ccw_dev_id *devid) { - clear_all_subchannels(); + struct subchannel_id schid; + + if (clear_all_subchannels_and_match(devid, &schid)) + panic("IPL Device not found\n"); cio_reset_channel_paths(); - do_reipl(devno); + do_reipl_asm(*((__u32*)&schid)); +} + +extern struct schib ipl_schib; + +/* + * ipl_save_parameters gets called very early. It is not allowed to access + * anything in the bss section at all. The bss section is not cleared yet, + * but may contain some ipl parameters written by the firmware. + * These parameters (if present) are copied to 0x2000. + * To avoid corruption of the ipl parameters, all variables used by this + * function must reside on the stack or in the data section. + */ +void ipl_save_parameters(void) +{ + struct subchannel_id schid; + unsigned int *ipl_ptr; + void *src, *dst; + + schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID; + if (!schid.one) + return; + if (stsch(schid, &ipl_schib)) + return; + if (!ipl_schib.pmcw.dnv) + return; + ipl_devno = ipl_schib.pmcw.dev; + ipl_flags |= IPL_DEVNO_VALID; + if (!ipl_schib.pmcw.qf) + return; + ipl_flags |= IPL_PARMBLOCK_VALID; + ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR; + src = (void *)(unsigned long)*ipl_ptr; + dst = (void *)IPL_PARMBLOCK_ORIGIN; + memmove(dst, src, PAGE_SIZE); + *ipl_ptr = IPL_PARMBLOCK_ORIGIN; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 13eeea3..7086a74 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -182,136 +182,141 @@ get_subchannel_by_schid(struct subchannel_id schid) return dev ? to_subchannel(dev) : NULL; } - -static inline int -css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid) +static inline int css_get_subchannel_status(struct subchannel *sch) { struct schib schib; - int cc; - cc = stsch(schid, &schib); - if (cc) - return CIO_GONE; - if (!schib.pmcw.dnv) + if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) return CIO_GONE; - if (sch && sch->schib.pmcw.dnv && - (schib.pmcw.dev != sch->schib.pmcw.dev)) + if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) return CIO_REVALIDATE; - if (sch && !sch->lpm) + if (!sch->lpm) return CIO_NO_PATH; return CIO_OPER; } - -static int -css_evaluate_subchannel(struct subchannel_id schid, int slow) + +static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) { int event, ret, disc; - struct subchannel *sch; unsigned long flags; + enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; - sch = get_subchannel_by_schid(schid); - disc = sch ? device_is_disconnected(sch) : 0; + spin_lock_irqsave(&sch->lock, flags); + disc = device_is_disconnected(sch); if (disc && slow) { - if (sch) - put_device(&sch->dev); - return 0; /* Already processed. */ + /* Disconnected devices are evaluated directly only.*/ + spin_unlock_irqrestore(&sch->lock, flags); + return 0; } - /* - * We've got a machine check, so running I/O won't get an interrupt. - * Kill any pending timers. - */ - if (sch) - device_kill_pending_timer(sch); + /* No interrupt after machine check - kill pending timers. */ + device_kill_pending_timer(sch); if (!disc && !slow) { - if (sch) - put_device(&sch->dev); - return -EAGAIN; /* Will be done on the slow path. */ + /* Non-disconnected devices are evaluated on the slow path. */ + spin_unlock_irqrestore(&sch->lock, flags); + return -EAGAIN; } - event = css_get_subchannel_status(sch, schid); + event = css_get_subchannel_status(sch); CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", - schid.ssid, schid.sch_no, event, - sch?(disc?"disconnected":"normal"):"unknown", - slow?"slow":"fast"); + sch->schid.ssid, sch->schid.sch_no, event, + disc ? "disconnected" : "normal", + slow ? "slow" : "fast"); + /* Analyze subchannel status. */ + action = NONE; switch (event) { case CIO_NO_PATH: - case CIO_GONE: - if (!sch) { - /* Never used this subchannel. Ignore. */ - ret = 0; + if (disc) { + /* Check if paths have become available. */ + action = REPROBE; break; } - if (disc && (event == CIO_NO_PATH)) { - /* - * Uargh, hack again. Because we don't get a machine - * check on configure on, our path bookkeeping can - * be out of date here (it's fine while we only do - * logical varying or get chsc machine checks). We - * need to force reprobing or we might miss devices - * coming operational again. It won't do harm in real - * no path situations. - */ - spin_lock_irqsave(&sch->lock, flags); - device_trigger_reprobe(sch); + /* fall through */ + case CIO_GONE: + /* Prevent unwanted effects when opening lock. */ + cio_disable_subchannel(sch); + device_set_disconnected(sch); + /* Ask driver what to do with device. */ + action = UNREGISTER; + if (sch->driver && sch->driver->notify) { spin_unlock_irqrestore(&sch->lock, flags); - ret = 0; - break; - } - if (sch->driver && sch->driver->notify && - sch->driver->notify(&sch->dev, event)) { - cio_disable_subchannel(sch); - device_set_disconnected(sch); - ret = 0; - break; + ret = sch->driver->notify(&sch->dev, event); + spin_lock_irqsave(&sch->lock, flags); + if (ret) + action = NONE; } - /* - * Unregister subchannel. - * The device will be killed automatically. - */ - cio_disable_subchannel(sch); - css_sch_device_unregister(sch); - /* Reset intparm to zeroes. */ - sch->schib.pmcw.intparm = 0; - cio_modify(sch); - put_device(&sch->dev); - ret = 0; break; case CIO_REVALIDATE: - /* - * Revalidation machine check. Sick. - * We don't notify the driver since we have to throw the device - * away in any case. - */ - if (!disc) { - css_sch_device_unregister(sch); - /* Reset intparm to zeroes. */ - sch->schib.pmcw.intparm = 0; - cio_modify(sch); - put_device(&sch->dev); - ret = css_probe_device(schid); - } else { - /* - * We can't immediately deregister the disconnected - * device since it might block. - */ - spin_lock_irqsave(&sch->lock, flags); - device_trigger_reprobe(sch); - spin_unlock_irqrestore(&sch->lock, flags); - ret = 0; - } + /* Device will be removed, so no notify necessary. */ + if (disc) + /* Reprobe because immediate unregister might block. */ + action = REPROBE; + else + action = UNREGISTER_PROBE; break; case CIO_OPER: - if (disc) { - spin_lock_irqsave(&sch->lock, flags); + if (disc) /* Get device operational again. */ - device_trigger_reprobe(sch); - spin_unlock_irqrestore(&sch->lock, flags); - } - ret = sch ? 0 : css_probe_device(schid); + action = REPROBE; + break; + } + /* Perform action. */ + ret = 0; + switch (action) { + case UNREGISTER: + case UNREGISTER_PROBE: + /* Unregister device (will use subchannel lock). */ + spin_unlock_irqrestore(&sch->lock, flags); + css_sch_device_unregister(sch); + spin_lock_irqsave(&sch->lock, flags); + + /* Reset intparm to zeroes. */ + sch->schib.pmcw.intparm = 0; + cio_modify(sch); + + /* Probe if necessary. */ + if (action == UNREGISTER_PROBE) + ret = css_probe_device(sch->schid); + break; + case REPROBE: + device_trigger_reprobe(sch); break; default: - BUG(); - ret = 0; + break; + } + spin_unlock_irqrestore(&sch->lock, flags); + + return ret; +} + +static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) +{ + struct schib schib; + + if (!slow) { + /* Will be done on the slow path. */ + return -EAGAIN; } + if (stsch(schid, &schib) || !schib.pmcw.dnv) { + /* Unusable - ignore. */ + return 0; + } + CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " + "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); + + return css_probe_device(schid); +} + +static int css_evaluate_subchannel(struct subchannel_id schid, int slow) +{ + struct subchannel *sch; + int ret; + + sch = get_subchannel_by_schid(schid); + if (sch) { + ret = css_evaluate_known_subchannel(sch, slow); + put_device(&sch->dev); + } else + ret = css_evaluate_new_subchannel(schid, slow); + return ret; } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 646da56..6889456 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -52,53 +52,81 @@ ccw_bus_match (struct device * dev, struct device_driver * drv) return 1; } -/* - * Hotplugging interface for ccw devices. - * Heavily modeled on pci and usb hotplug. - */ -static int -ccw_uevent (struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) +/* Store modalias string delimited by prefix/suffix string into buffer with + * specified size. Return length of resulting string (excluding trailing '\0') + * even if string doesn't fit buffer (snprintf semantics). */ +static int snprint_alias(char *buf, size_t size, const char *prefix, + struct ccw_device_id *id, const char *suffix) { - struct ccw_device *cdev = to_ccwdev(dev); - int i = 0; - int length = 0; + int len; - if (!cdev) - return -ENODEV; + len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type, + id->cu_model); + if (len > size) + return len; + buf += len; + size -= len; - /* what we want to pass to /sbin/hotplug */ + if (id->dev_type != 0) + len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type, + id->dev_model, suffix); + else + len += snprintf(buf, size, "dtdm%s", suffix); - envp[i++] = buffer; - length += scnprintf(buffer, buffer_size - length, "CU_TYPE=%04X", - cdev->id.cu_type); - if ((buffer_size - length <= 0) || (i >= num_envp)) - return -ENOMEM; - ++length; - buffer += length; + return len; +} +/* Set up environment variables for ccw device uevent. Return 0 on success, + * non-zero otherwise. */ +static int ccw_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct ccw_device *cdev = to_ccwdev(dev); + struct ccw_device_id *id = &(cdev->id); + int i = 0; + int len; + + /* CU_TYPE= */ + len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1; + if (len > buffer_size || i >= num_envp) + return -ENOMEM; envp[i++] = buffer; - length += scnprintf(buffer, buffer_size - length, "CU_MODEL=%02X", - cdev->id.cu_model); - if ((buffer_size - length <= 0) || (i >= num_envp)) + buffer += len; + buffer_size -= len; + + /* CU_MODEL= */ + len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1; + if (len > buffer_size || i >= num_envp) return -ENOMEM; - ++length; - buffer += length; + envp[i++] = buffer; + buffer += len; + buffer_size -= len; /* The next two can be zero, that's ok for us */ - envp[i++] = buffer; - length += scnprintf(buffer, buffer_size - length, "DEV_TYPE=%04X", - cdev->id.dev_type); - if ((buffer_size - length <= 0) || (i >= num_envp)) + /* DEV_TYPE= */ + len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1; + if (len > buffer_size || i >= num_envp) return -ENOMEM; - ++length; - buffer += length; + envp[i++] = buffer; + buffer += len; + buffer_size -= len; + /* DEV_MODEL= */ + len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X", + (unsigned char) id->dev_model) + 1; + if (len > buffer_size || i >= num_envp) + return -ENOMEM; envp[i++] = buffer; - length += scnprintf(buffer, buffer_size - length, "DEV_MODEL=%02X", - cdev->id.dev_model); - if ((buffer_size - length <= 0) || (i >= num_envp)) + buffer += len; + buffer_size -= len; + + /* MODALIAS= */ + len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1; + if (len > buffer_size || i >= num_envp) return -ENOMEM; + envp[i++] = buffer; + buffer += len; + buffer_size -= len; envp[i] = NULL; @@ -251,16 +279,11 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf) { struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); - int ret; + int len; - ret = sprintf(buf, "ccw:t%04Xm%02X", - id->cu_type, id->cu_model); - if (id->dev_type != 0) - ret += sprintf(buf + ret, "dt%04Xdm%02X\n", - id->dev_type, id->dev_model); - else - ret += sprintf(buf + ret, "dtdm\n"); - return ret; + len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1; + + return len > PAGE_SIZE ? PAGE_SIZE : len; } static ssize_t diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 35e162b..dace46f 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -232,10 +232,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) */ old_lpm = sch->lpm; stsch(sch->schid, &sch->schib); - sch->lpm = sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom & - sch->opm; + sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Check since device may again have become not operational. */ if (!sch->schib.pmcw.dnv) state = DEV_STATE_NOT_OPER; @@ -267,6 +264,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) notify = 1; } /* fill out sense information */ + 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; @@ -454,8 +452,8 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) return; } /* Start Path Group verification. */ - sch->vpm = 0; /* Start with no path groups set. */ cdev->private->state = DEV_STATE_VERIFY; + cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); } @@ -555,7 +553,19 @@ ccw_device_nopath_notify(void *data) void ccw_device_verify_done(struct ccw_device *cdev, int err) { - cdev->private->flags.doverify = 0; + struct subchannel *sch; + + sch = to_subchannel(cdev->dev.parent); + /* Update schib - pom may have changed. */ + stsch(sch->schid, &sch->schib); + /* Update lpm with verified path mask. */ + sch->lpm = sch->vpm; + /* Repeat path verification? */ + if (cdev->private->flags.doverify) { + cdev->private->flags.doverify = 0; + ccw_device_verify_start(cdev); + return; + } switch (err) { case -EOPNOTSUPP: /* path grouping not supported, just set online. */ cdev->private->options.pgroup = 0; @@ -613,6 +623,7 @@ ccw_device_online(struct ccw_device *cdev) if (!cdev->private->options.pgroup) { /* Start initial path verification. */ cdev->private->state = DEV_STATE_VERIFY; + cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); return 0; } @@ -659,7 +670,6 @@ ccw_device_offline(struct ccw_device *cdev) /* Are we doing path grouping? */ if (!cdev->private->options.pgroup) { /* No, set state offline immediately. */ - sch->vpm = 0; ccw_device_done(cdev, DEV_STATE_OFFLINE); return 0; } @@ -780,6 +790,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) } /* Device is idle, we can do the path verification. */ cdev->private->state = DEV_STATE_VERIFY; + cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); } @@ -1042,9 +1053,9 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event) } static void -ccw_device_wait4io_verify(struct ccw_device *cdev, enum dev_event dev_event) +ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event) { - /* When the I/O has terminated, we have to start verification. */ + /* Start verification after current task finished. */ cdev->private->flags.doverify = 1; } @@ -1110,10 +1121,7 @@ device_trigger_reprobe(struct subchannel *sch) * The pim, pam, pom values may not be accurate, but they are the best * we have before performing device selection :/ */ - sch->lpm = sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom & - sch->opm; + sch->lpm = sch->schib.pmcw.pam & sch->opm; /* Re-set some bits in the pmcw that were lost. */ sch->schib.pmcw.isc = 3; sch->schib.pmcw.csense = 1; @@ -1237,7 +1245,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, - [DEV_EVENT_VERIFY] = ccw_device_nop, + [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_ONLINE] = { [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, @@ -1280,7 +1288,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_online_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_wait4io_irq, [DEV_EVENT_TIMEOUT] = ccw_device_wait4io_timeout, - [DEV_EVENT_VERIFY] = ccw_device_wait4io_verify, + [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_QUIESCE] = { [DEV_EVENT_NOTOPER] = ccw_device_quiesce_done, @@ -1293,7 +1301,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, [DEV_EVENT_INTERRUPT] = ccw_device_start_id, [DEV_EVENT_TIMEOUT] = ccw_device_bug, - [DEV_EVENT_VERIFY] = ccw_device_nop, + [DEV_EVENT_VERIFY] = ccw_device_start_id, }, [DEV_STATE_DISCONNECTED_SENSE_ID] = { [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 9e3de0bd..93a897e 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -96,6 +96,12 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, ret = cio_set_options (sch, flags); if (ret) return ret; + /* Adjust requested path mask to excluded varied off paths. */ + if (lpm) { + lpm &= sch->opm; + if (lpm == 0) + return -EACCES; + } ret = cio_start_key (sch, cpa, lpm, key); if (ret == 0) cdev->private->intparm = intparm; @@ -250,7 +256,7 @@ ccw_device_get_path_mask(struct ccw_device *cdev) if (!sch) return 0; else - return sch->vpm; + return sch->lpm; } static void @@ -304,7 +310,7 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _ sch = to_subchannel(cdev->dev.parent); do { ret = cio_start (sch, ccw, lpm); - if ((ret == -EBUSY) || (ret == -EACCES)) { + if (ret == -EBUSY) { /* Try again later. */ spin_unlock_irq(&sch->lock); msleep(10); @@ -433,6 +439,13 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp if (!ciw || ciw->cmd == 0) return -EOPNOTSUPP; + /* Adjust requested path mask to excluded varied off paths. */ + if (lpm) { + lpm &= sch->opm; + if (lpm == 0) + return -EACCES; + } + rcd_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); if (!rcd_ccw) return -ENOMEM; diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 1693a10..8ca2d07 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -245,18 +245,17 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) memset(&cdev->private->irb, 0, sizeof(struct irb)); /* Try multiple times. */ - ret = -ENODEV; + ret = -EACCES; if (cdev->private->iretry > 0) { cdev->private->iretry--; ret = cio_start (sch, cdev->private->iccws, cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if ((ret != -EACCES) && (ret != -ENODEV)) + /* We expect an interrupt in case of success or busy + * indication. */ + if ((ret == 0) || (ret == -EBUSY)) return ret; } - /* PGID command failed on this path. Switch it off. */ - sch->lpm &= ~cdev->private->imask; - sch->vpm &= ~cdev->private->imask; + /* PGID command failed on this path. */ CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel " "0.%x.%04x, lpm %02X, became 'not operational'\n", cdev->private->devno, sch->schid.ssid, @@ -286,18 +285,17 @@ static int __ccw_device_do_nop(struct ccw_device *cdev) memset(&cdev->private->irb, 0, sizeof(struct irb)); /* Try multiple times. */ - ret = -ENODEV; + ret = -EACCES; if (cdev->private->iretry > 0) { cdev->private->iretry--; ret = cio_start (sch, cdev->private->iccws, cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if ((ret != -EACCES) && (ret != -ENODEV)) + /* We expect an interrupt in case of success or busy + * indication. */ + if ((ret == 0) || (ret == -EBUSY)) return ret; } - /* nop command failed on this path. Switch it off. */ - sch->lpm &= ~cdev->private->imask; - sch->vpm &= ~cdev->private->imask; + /* nop command failed on this path. */ CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel " "0.%x.%04x, lpm %02X, became 'not operational'\n", cdev->private->devno, sch->schid.ssid, @@ -372,27 +370,32 @@ static void __ccw_device_verify_start(struct ccw_device *cdev) { struct subchannel *sch; - __u8 imask, func; + __u8 func; int ret; sch = to_subchannel(cdev->dev.parent); - while (sch->vpm != sch->lpm) { - /* Find first unequal bit in vpm vs. lpm */ - for (imask = 0x80; imask != 0; imask >>= 1) - if ((sch->vpm & imask) != (sch->lpm & imask)) - break; - cdev->private->imask = imask; + /* Repeat for all paths. */ + for (; cdev->private->imask; cdev->private->imask >>= 1, + cdev->private->iretry = 5) { + if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) + /* Path not available, try next. */ + continue; if (cdev->private->options.pgroup) { - func = (sch->vpm & imask) ? - SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH; + if (sch->opm & cdev->private->imask) + func = SPID_FUNC_ESTABLISH; + else + func = SPID_FUNC_RESIGN; ret = __ccw_device_do_pgid(cdev, func); } else ret = __ccw_device_do_nop(cdev); + /* We expect an interrupt in case of success or busy + * indication. */ if (ret == 0 || ret == -EBUSY) return; - cdev->private->iretry = 5; + /* Permanent path failure, try next. */ } - ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); + /* Done with all paths. */ + ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV); } /* @@ -421,14 +424,14 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) else ret = __ccw_device_check_nop(cdev); memset(&cdev->private->irb, 0, sizeof(struct irb)); + switch (ret) { /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ case 0: - /* Establish or Resign Path Group done. Update vpm. */ - if ((sch->lpm & cdev->private->imask) != 0) - sch->vpm |= cdev->private->imask; - else - sch->vpm &= ~cdev->private->imask; + /* Path verification ccw finished successfully, update lpm. */ + sch->vpm |= sch->opm & cdev->private->imask; + /* Go on with next path. */ + cdev->private->imask >>= 1; cdev->private->iretry = 5; __ccw_device_verify_start(cdev); break; @@ -441,6 +444,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) cdev->private->options.pgroup = 0; else cdev->private->flags.pgid_single = 1; + /* Retry */ + sch->vpm = 0; + cdev->private->imask = 0x80; + cdev->private->iretry = 5; /* fall through. */ case -EAGAIN: /* Try again. */ __ccw_device_verify_start(cdev); @@ -449,8 +456,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_verify_done(cdev, -ETIME); break; case -EACCES: /* channel is not operational. */ - sch->lpm &= ~cdev->private->imask; - sch->vpm &= ~cdev->private->imask; + cdev->private->imask >>= 1; cdev->private->iretry = 5; __ccw_device_verify_start(cdev); break; @@ -463,19 +469,17 @@ ccw_device_verify_start(struct ccw_device *cdev) struct subchannel *sch = to_subchannel(cdev->dev.parent); cdev->private->flags.pgid_single = 0; + cdev->private->imask = 0x80; cdev->private->iretry = 5; - /* - * Update sch->lpm with current values to catch paths becoming - * available again. - */ + + /* Start with empty vpm. */ + sch->vpm = 0; + + /* Get current pam. */ if (stsch(sch->schid, &sch->schib)) { ccw_device_verify_done(cdev, -ENODEV); return; } - sch->lpm = sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom & - sch->opm; __ccw_device_verify_start(cdev); } @@ -524,7 +528,6 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) switch (ret) { /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ case 0: /* disband successful. */ - sch->vpm = 0; ccw_device_disband_done(cdev, ret); break; case -EOPNOTSUPP: diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 7c93a87..cde822d 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -115,7 +115,7 @@ qdio_min(int a,int b) static inline __u64 qdio_get_micros(void) { - return (get_clock() >> 10); /* time>>12 is microseconds */ + return (get_clock() >> 12); /* time>>12 is microseconds */ } /* @@ -1129,7 +1129,7 @@ out: #ifdef QDIO_USE_PROCESSING_STATE if (last_position>=0) - set_slsb(q, &last_position, SLSB_P_INPUT_NOT_INIT, &count); + set_slsb(q, &last_position, SLSB_P_INPUT_PROCESSING, &count); #endif /* QDIO_USE_PROCESSING_STATE */ QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int)); diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index ceb3ab3..1245693 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -191,49 +191,49 @@ enum qdio_irq_states { #if QDIO_VERBOSE_LEVEL>8 #define QDIO_PRINT_STUPID(x...) printk( KERN_DEBUG QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_STUPID(x...) +#define QDIO_PRINT_STUPID(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>7 #define QDIO_PRINT_ALL(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_ALL(x...) +#define QDIO_PRINT_ALL(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>6 #define QDIO_PRINT_INFO(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_INFO(x...) +#define QDIO_PRINT_INFO(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>5 #define QDIO_PRINT_WARN(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_WARN(x...) +#define QDIO_PRINT_WARN(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>4 #define QDIO_PRINT_ERR(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_ERR(x...) +#define QDIO_PRINT_ERR(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>3 #define QDIO_PRINT_CRIT(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_CRIT(x...) +#define QDIO_PRINT_CRIT(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>2 #define QDIO_PRINT_ALERT(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_ALERT(x...) +#define QDIO_PRINT_ALERT(x...) do { } while (0) #endif #if QDIO_VERBOSE_LEVEL>1 #define QDIO_PRINT_EMERG(x...) printk( QDIO_PRINTK_HEADER x) #else -#define QDIO_PRINT_EMERG(x...) +#define QDIO_PRINT_EMERG(x...) do { } while (0) #endif #define HEXDUMP16(importance,header,ptr) \ diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile index 15edebb..f0a12d2 100644 --- a/drivers/s390/crypto/Makefile +++ b/drivers/s390/crypto/Makefile @@ -2,5 +2,16 @@ # S/390 crypto devices # -z90crypt-objs := z90main.o z90hardware.o -obj-$(CONFIG_Z90CRYPT) += z90crypt.o +ifdef CONFIG_ZCRYPT_MONOLITHIC + +z90crypt-objs := zcrypt_mono.o ap_bus.o zcrypt_api.o \ + zcrypt_pcica.o zcrypt_pcicc.o zcrypt_pcixcc.o zcrypt_cex2a.o +obj-$(CONFIG_ZCRYPT) += z90crypt.o + +else + +ap-objs := ap_bus.o +obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o +obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o + +endif diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c new file mode 100644 index 0000000..6ed0985 --- /dev/null +++ b/drivers/s390/crypto/ap_bus.c @@ -0,0 +1,1221 @@ +/* + * linux/drivers/s390/crypto/ap_bus.c + * + * Copyright (C) 2006 IBM Corporation + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * Adjunct processor bus. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/notifier.h> +#include <linux/kthread.h> +#include <linux/mutex.h> +#include <asm/s390_rdev.h> + +#include "ap_bus.h" + +/* Some prototypes. */ +static void ap_scan_bus(void *); +static void ap_poll_all(unsigned long); +static void ap_poll_timeout(unsigned long); +static int ap_poll_thread_start(void); +static void ap_poll_thread_stop(void); + +/** + * Module description. + */ +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("Adjunct Processor Bus driver, " + "Copyright 2006 IBM Corporation"); +MODULE_LICENSE("GPL"); + +/** + * Module parameter + */ +int ap_domain_index = -1; /* Adjunct Processor Domain Index */ +module_param_named(domain, ap_domain_index, int, 0000); +MODULE_PARM_DESC(domain, "domain index for ap devices"); +EXPORT_SYMBOL(ap_domain_index); + +static int ap_thread_flag = 1; +module_param_named(poll_thread, ap_thread_flag, int, 0000); +MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on)."); + +static struct device *ap_root_device = NULL; + +/** + * Workqueue & timer for bus rescan. + */ +static struct workqueue_struct *ap_work_queue; +static struct timer_list ap_config_timer; +static int ap_config_time = AP_CONFIG_TIME; +static DECLARE_WORK(ap_config_work, ap_scan_bus, NULL); + +/** + * Tasklet & timer for AP request polling. + */ +static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0); +static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); +static atomic_t ap_poll_requests = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); +static struct task_struct *ap_poll_kthread = NULL; +static DEFINE_MUTEX(ap_poll_thread_mutex); + +/** + * Test if ap instructions are available. + * + * Returns 0 if the ap instructions are installed. + */ +static inline int ap_instructions_available(void) +{ + register unsigned long reg0 asm ("0") = AP_MKQID(0,0); + register unsigned long reg1 asm ("1") = -ENODEV; + register unsigned long reg2 asm ("2") = 0UL; + + asm volatile( + " .long 0xb2af0000\n" /* PQAP(TAPQ) */ + "0: la %1,0\n" + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (reg0), "+d" (reg1), "+d" (reg2) : : "cc" ); + return reg1; +} + +/** + * Test adjunct processor queue. + * @qid: the ap queue number + * @queue_depth: pointer to queue depth value + * @device_type: pointer to device type value + * + * Returns ap queue status structure. + */ +static inline struct ap_queue_status +ap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type) +{ + register unsigned long reg0 asm ("0") = qid; + register struct ap_queue_status reg1 asm ("1"); + register unsigned long reg2 asm ("2") = 0UL; + + asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */ + : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc"); + *device_type = (int) (reg2 >> 24); + *queue_depth = (int) (reg2 & 0xff); + return reg1; +} + +/** + * Reset adjunct processor queue. + * @qid: the ap queue number + * + * Returns ap queue status structure. + */ +static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid) +{ + register unsigned long reg0 asm ("0") = qid | 0x01000000UL; + register struct ap_queue_status reg1 asm ("1"); + register unsigned long reg2 asm ("2") = 0UL; + + asm volatile( + ".long 0xb2af0000" /* PQAP(RAPQ) */ + : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc"); + return reg1; +} + +/** + * Send message to adjunct processor queue. + * @qid: the ap queue number + * @psmid: the program supplied message identifier + * @msg: the message text + * @length: the message length + * + * Returns ap queue status structure. + * + * Condition code 1 on NQAP can't happen because the L bit is 1. + * + * Condition code 2 on NQAP also means the send is incomplete, + * because a segment boundary was reached. The NQAP is repeated. + */ +static inline struct ap_queue_status +__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) +{ + typedef struct { char _[length]; } msgblock; + register unsigned long reg0 asm ("0") = qid | 0x40000000UL; + register struct ap_queue_status reg1 asm ("1"); + register unsigned long reg2 asm ("2") = (unsigned long) msg; + register unsigned long reg3 asm ("3") = (unsigned long) length; + register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); + register unsigned long reg5 asm ("5") = (unsigned int) psmid; + + asm volatile ( + "0: .long 0xb2ad0042\n" /* DQAP */ + " brc 2,0b" + : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3) + : "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg) + : "cc" ); + return reg1; +} + +int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) +{ + struct ap_queue_status status; + + status = __ap_send(qid, psmid, msg, length); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + return 0; + case AP_RESPONSE_Q_FULL: + return -EBUSY; + default: /* Device is gone. */ + return -ENODEV; + } +} +EXPORT_SYMBOL(ap_send); + +/* + * Receive message from adjunct processor queue. + * @qid: the ap queue number + * @psmid: pointer to program supplied message identifier + * @msg: the message text + * @length: the message length + * + * Returns ap queue status structure. + * + * Condition code 1 on DQAP means the receive has taken place + * but only partially. The response is incomplete, hence the + * DQAP is repeated. + * + * Condition code 2 on DQAP also means the receive is incomplete, + * this time because a segment boundary was reached. Again, the + * DQAP is repeated. + * + * Note that gpr2 is used by the DQAP instruction to keep track of + * any 'residual' length, in case the instruction gets interrupted. + * Hence it gets zeroed before the instruction. + */ +static inline struct ap_queue_status +__ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) +{ + typedef struct { char _[length]; } msgblock; + register unsigned long reg0 asm("0") = qid | 0x80000000UL; + register struct ap_queue_status reg1 asm ("1"); + register unsigned long reg2 asm("2") = 0UL; + register unsigned long reg4 asm("4") = (unsigned long) msg; + register unsigned long reg5 asm("5") = (unsigned long) length; + register unsigned long reg6 asm("6") = 0UL; + register unsigned long reg7 asm("7") = 0UL; + + + asm volatile( + "0: .long 0xb2ae0064\n" + " brc 6,0b\n" + : "+d" (reg0), "=d" (reg1), "+d" (reg2), + "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7), + "=m" (*(msgblock *) msg) : : "cc" ); + *psmid = (((unsigned long long) reg6) << 32) + reg7; + return reg1; +} + +int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) +{ + struct ap_queue_status status; + + status = __ap_recv(qid, psmid, msg, length); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + return 0; + case AP_RESPONSE_NO_PENDING_REPLY: + if (status.queue_empty) + return -ENOENT; + return -EBUSY; + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(ap_recv); + +/** + * Check if an AP queue is available. The test is repeated for + * AP_MAX_RESET times. + * @qid: the ap queue number + * @queue_depth: pointer to queue depth value + * @device_type: pointer to device type value + */ +static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type) +{ + struct ap_queue_status status; + int t_depth, t_device_type, rc, i; + + rc = -EBUSY; + for (i = 0; i < AP_MAX_RESET; i++) { + status = ap_test_queue(qid, &t_depth, &t_device_type); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + *queue_depth = t_depth + 1; + *device_type = t_device_type; + rc = 0; + break; + case AP_RESPONSE_Q_NOT_AVAIL: + rc = -ENODEV; + break; + case AP_RESPONSE_RESET_IN_PROGRESS: + break; + case AP_RESPONSE_DECONFIGURED: + rc = -ENODEV; + break; + case AP_RESPONSE_CHECKSTOPPED: + rc = -ENODEV; + break; + case AP_RESPONSE_BUSY: + break; + default: + BUG(); + } + if (rc != -EBUSY) + break; + if (i < AP_MAX_RESET - 1) + udelay(5); + } + return rc; +} + +/** + * Reset an AP queue and wait for it to become available again. + * @qid: the ap queue number + */ +static int ap_init_queue(ap_qid_t qid) +{ + struct ap_queue_status status; + int rc, dummy, i; + + rc = -ENODEV; + status = ap_reset_queue(qid); + for (i = 0; i < AP_MAX_RESET; i++) { + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + if (status.queue_empty) + rc = 0; + break; + case AP_RESPONSE_Q_NOT_AVAIL: + case AP_RESPONSE_DECONFIGURED: + case AP_RESPONSE_CHECKSTOPPED: + i = AP_MAX_RESET; /* return with -ENODEV */ + break; + case AP_RESPONSE_RESET_IN_PROGRESS: + case AP_RESPONSE_BUSY: + default: + break; + } + if (rc != -ENODEV) + break; + if (i < AP_MAX_RESET - 1) { + udelay(5); + status = ap_test_queue(qid, &dummy, &dummy); + } + } + return rc; +} + +/** + * AP device related attributes. + */ +static ssize_t ap_hwtype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type); +} +static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); + +static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth); +} +static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL); + +static ssize_t ap_request_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + int rc; + + spin_lock_bh(&ap_dev->lock); + rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->total_request_count); + spin_unlock_bh(&ap_dev->lock); + return rc; +} + +static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); + +static ssize_t ap_modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ap:t%02X", to_ap_dev(dev)->device_type); +} + +static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL); + +static struct attribute *ap_dev_attrs[] = { + &dev_attr_hwtype.attr, + &dev_attr_depth.attr, + &dev_attr_request_count.attr, + &dev_attr_modalias.attr, + NULL +}; +static struct attribute_group ap_dev_attr_group = { + .attrs = ap_dev_attrs +}; + +/** + * AP bus driver registration/unregistration. + */ +static int ap_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + struct ap_driver *ap_drv = to_ap_drv(drv); + struct ap_device_id *id; + + /** + * Compare device type of the device with the list of + * supported types of the device_driver. + */ + for (id = ap_drv->ids; id->match_flags; id++) { + if ((id->match_flags & AP_DEVICE_ID_MATCH_DEVICE_TYPE) && + (id->dev_type != ap_dev->device_type)) + continue; + return 1; + } + return 0; +} + +/** + * uevent function for AP devices. It sets up a single environment + * variable DEV_TYPE which contains the hardware device type. + */ +static int ap_uevent (struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + int length; + + if (!ap_dev) + return -ENODEV; + + /* Set up DEV_TYPE environment variable. */ + envp[0] = buffer; + length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X", + ap_dev->device_type); + if (buffer_size - length <= 0) + return -ENOMEM; + envp[1] = 0; + return 0; +} + +static struct bus_type ap_bus_type = { + .name = "ap", + .match = &ap_bus_match, + .uevent = &ap_uevent, +}; + +static int ap_device_probe(struct device *dev) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + struct ap_driver *ap_drv = to_ap_drv(dev->driver); + int rc; + + ap_dev->drv = ap_drv; + rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; + if (rc) + ap_dev->unregistered = 1; + return rc; +} + +/** + * Flush all requests from the request/pending queue of an AP device. + * @ap_dev: pointer to the AP device. + */ +static inline void __ap_flush_queue(struct ap_device *ap_dev) +{ + struct ap_message *ap_msg, *next; + + list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) { + list_del_init(&ap_msg->list); + ap_dev->pendingq_count--; + ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); + } + list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) { + list_del_init(&ap_msg->list); + ap_dev->requestq_count--; + ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); + } +} + +void ap_flush_queue(struct ap_device *ap_dev) +{ + spin_lock_bh(&ap_dev->lock); + __ap_flush_queue(ap_dev); + spin_unlock_bh(&ap_dev->lock); +} +EXPORT_SYMBOL(ap_flush_queue); + +static int ap_device_remove(struct device *dev) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + struct ap_driver *ap_drv = ap_dev->drv; + + spin_lock_bh(&ap_dev->lock); + __ap_flush_queue(ap_dev); + /** + * set ->unregistered to 1 while holding the lock. This prevents + * new messages to be put on the queue from now on. + */ + ap_dev->unregistered = 1; + spin_unlock_bh(&ap_dev->lock); + if (ap_drv->remove) + ap_drv->remove(ap_dev); + return 0; +} + +int ap_driver_register(struct ap_driver *ap_drv, struct module *owner, + char *name) +{ + struct device_driver *drv = &ap_drv->driver; + + drv->bus = &ap_bus_type; + drv->probe = ap_device_probe; + drv->remove = ap_device_remove; + drv->owner = owner; + drv->name = name; + return driver_register(drv); +} +EXPORT_SYMBOL(ap_driver_register); + +void ap_driver_unregister(struct ap_driver *ap_drv) +{ + driver_unregister(&ap_drv->driver); +} +EXPORT_SYMBOL(ap_driver_unregister); + +/** + * AP bus attributes. + */ +static ssize_t ap_domain_show(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index); +} + +static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL); + +static ssize_t ap_config_time_show(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); +} + +static ssize_t ap_config_time_store(struct bus_type *bus, + const char *buf, size_t count) +{ + int time; + + if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120) + return -EINVAL; + ap_config_time = time; + if (!timer_pending(&ap_config_timer) || + !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) { + ap_config_timer.expires = jiffies + ap_config_time * HZ; + add_timer(&ap_config_timer); + } + return count; +} + +static BUS_ATTR(config_time, 0644, ap_config_time_show, ap_config_time_store); + +static ssize_t ap_poll_thread_show(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0); +} + +static ssize_t ap_poll_thread_store(struct bus_type *bus, + const char *buf, size_t count) +{ + int flag, rc; + + if (sscanf(buf, "%d\n", &flag) != 1) + return -EINVAL; + if (flag) { + rc = ap_poll_thread_start(); + if (rc) + return rc; + } + else + ap_poll_thread_stop(); + return count; +} + +static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store); + +static struct bus_attribute *const ap_bus_attrs[] = { + &bus_attr_ap_domain, + &bus_attr_config_time, + &bus_attr_poll_thread, + NULL +}; + +/** + * Pick one of the 16 ap domains. + */ +static inline int ap_select_domain(void) +{ + int queue_depth, device_type, count, max_count, best_domain; + int rc, i, j; + + /** + * We want to use a single domain. Either the one specified with + * the "domain=" parameter or the domain with the maximum number + * of devices. + */ + if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS) + /* Domain has already been selected. */ + return 0; + best_domain = -1; + max_count = 0; + for (i = 0; i < AP_DOMAINS; i++) { + count = 0; + for (j = 0; j < AP_DEVICES; j++) { + ap_qid_t qid = AP_MKQID(j, i); + rc = ap_query_queue(qid, &queue_depth, &device_type); + if (rc) + continue; + count++; + } + if (count > max_count) { + max_count = count; + best_domain = i; + } + } + if (best_domain >= 0){ + ap_domain_index = best_domain; + return 0; + } + return -ENODEV; +} + +/** + * Find the device type if query queue returned a device type of 0. + * @ap_dev: pointer to the AP device. + */ +static int ap_probe_device_type(struct ap_device *ap_dev) +{ + static unsigned char msg[] = { + 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50, + 0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01, + 0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00, + 0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00, + 0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20, + 0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53, + 0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22, + 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, + 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, + 0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66, + 0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44, + 0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22, + 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, + 0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77, + 0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00, + 0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00, + 0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01, + 0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c, + 0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68, + 0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66, + 0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0, + 0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8, + 0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04, + 0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57, + 0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d, + }; + struct ap_queue_status status; + unsigned long long psmid; + char *reply; + int rc, i; + + reply = (void *) get_zeroed_page(GFP_KERNEL); + if (!reply) { + rc = -ENOMEM; + goto out; + } + + status = __ap_send(ap_dev->qid, 0x0102030405060708ULL, + msg, sizeof(msg)); + if (status.response_code != AP_RESPONSE_NORMAL) { + rc = -ENODEV; + goto out_free; + } + + /* Wait for the test message to complete. */ + for (i = 0; i < 6; i++) { + mdelay(300); + status = __ap_recv(ap_dev->qid, &psmid, reply, 4096); + if (status.response_code == AP_RESPONSE_NORMAL && + psmid == 0x0102030405060708ULL) + break; + } + if (i < 6) { + /* Got an answer. */ + if (reply[0] == 0x00 && reply[1] == 0x86) + ap_dev->device_type = AP_DEVICE_TYPE_PCICC; + else + ap_dev->device_type = AP_DEVICE_TYPE_PCICA; + rc = 0; + } else + rc = -ENODEV; + +out_free: + free_page((unsigned long) reply); +out: + return rc; +} + +/** + * Scan the ap bus for new devices. + */ +static int __ap_scan_bus(struct device *dev, void *data) +{ + return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data; +} + +static void ap_device_release(struct device *dev) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + + kfree(ap_dev); +} + +static void ap_scan_bus(void *data) +{ + struct ap_device *ap_dev; + struct device *dev; + ap_qid_t qid; + int queue_depth, device_type; + int rc, i; + + if (ap_select_domain() != 0) + return; + for (i = 0; i < AP_DEVICES; i++) { + qid = AP_MKQID(i, ap_domain_index); + dev = bus_find_device(&ap_bus_type, NULL, + (void *)(unsigned long)qid, + __ap_scan_bus); + if (dev) { + put_device(dev); + continue; + } + rc = ap_query_queue(qid, &queue_depth, &device_type); + if (rc) + continue; + rc = ap_init_queue(qid); + if (rc) + continue; + ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL); + if (!ap_dev) + break; + ap_dev->qid = qid; + ap_dev->queue_depth = queue_depth; + spin_lock_init(&ap_dev->lock); + INIT_LIST_HEAD(&ap_dev->pendingq); + INIT_LIST_HEAD(&ap_dev->requestq); + if (device_type == 0) + ap_probe_device_type(ap_dev); + else + ap_dev->device_type = device_type; + + ap_dev->device.bus = &ap_bus_type; + ap_dev->device.parent = ap_root_device; + snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x", + AP_QID_DEVICE(ap_dev->qid)); + ap_dev->device.release = ap_device_release; + rc = device_register(&ap_dev->device); + if (rc) { + kfree(ap_dev); + continue; + } + /* Add device attributes. */ + rc = sysfs_create_group(&ap_dev->device.kobj, + &ap_dev_attr_group); + if (rc) + device_unregister(&ap_dev->device); + } +} + +static void +ap_config_timeout(unsigned long ptr) +{ + queue_work(ap_work_queue, &ap_config_work); + ap_config_timer.expires = jiffies + ap_config_time * HZ; + add_timer(&ap_config_timer); +} + +/** + * Set up the timer to run the poll tasklet + */ +static inline void ap_schedule_poll_timer(void) +{ + if (timer_pending(&ap_poll_timer)) + return; + mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME); +} + +/** + * Receive pending reply messages from an AP device. + * @ap_dev: pointer to the AP device + * @flags: pointer to control flags, bit 2^0 is set if another poll is + * required, bit 2^1 is set if the poll timer needs to get armed + * Returns 0 if the device is still present, -ENODEV if not. + */ +static inline int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags) +{ + struct ap_queue_status status; + struct ap_message *ap_msg; + + if (ap_dev->queue_count <= 0) + return 0; + status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid, + ap_dev->reply->message, ap_dev->reply->length); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + atomic_dec(&ap_poll_requests); + ap_dev->queue_count--; + list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { + if (ap_msg->psmid != ap_dev->reply->psmid) + continue; + list_del_init(&ap_msg->list); + ap_dev->pendingq_count--; + ap_dev->drv->receive(ap_dev, ap_msg, ap_dev->reply); + break; + } + if (ap_dev->queue_count > 0) + *flags |= 1; + break; + case AP_RESPONSE_NO_PENDING_REPLY: + if (status.queue_empty) { + /* The card shouldn't forget requests but who knows. */ + ap_dev->queue_count = 0; + list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); + ap_dev->requestq_count += ap_dev->pendingq_count; + ap_dev->pendingq_count = 0; + } else + *flags |= 2; + break; + default: + return -ENODEV; + } + return 0; +} + +/** + * Send messages from the request queue to an AP device. + * @ap_dev: pointer to the AP device + * @flags: pointer to control flags, bit 2^0 is set if another poll is + * required, bit 2^1 is set if the poll timer needs to get armed + * Returns 0 if the device is still present, -ENODEV if not. + */ +static inline int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) +{ + struct ap_queue_status status; + struct ap_message *ap_msg; + + if (ap_dev->requestq_count <= 0 || + ap_dev->queue_count >= ap_dev->queue_depth) + return 0; + /* Start the next request on the queue. */ + ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); + status = __ap_send(ap_dev->qid, ap_msg->psmid, + ap_msg->message, ap_msg->length); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + atomic_inc(&ap_poll_requests); + ap_dev->queue_count++; + list_move_tail(&ap_msg->list, &ap_dev->pendingq); + ap_dev->requestq_count--; + ap_dev->pendingq_count++; + if (ap_dev->queue_count < ap_dev->queue_depth && + ap_dev->requestq_count > 0) + *flags |= 1; + *flags |= 2; + break; + case AP_RESPONSE_Q_FULL: + *flags |= 2; + break; + case AP_RESPONSE_MESSAGE_TOO_BIG: + return -EINVAL; + default: + return -ENODEV; + } + return 0; +} + +/** + * Poll AP device for pending replies and send new messages. If either + * ap_poll_read or ap_poll_write returns -ENODEV unregister the device. + * @ap_dev: pointer to the bus device + * @flags: pointer to control flags, bit 2^0 is set if another poll is + * required, bit 2^1 is set if the poll timer needs to get armed + * Returns 0. + */ +static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags) +{ + int rc; + + rc = ap_poll_read(ap_dev, flags); + if (rc) + return rc; + return ap_poll_write(ap_dev, flags); +} + +/** + * Queue a message to a device. + * @ap_dev: pointer to the AP device + * @ap_msg: the message to be queued + */ +static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) +{ + struct ap_queue_status status; + + if (list_empty(&ap_dev->requestq) && + ap_dev->queue_count < ap_dev->queue_depth) { + status = __ap_send(ap_dev->qid, ap_msg->psmid, + ap_msg->message, ap_msg->length); + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + list_add_tail(&ap_msg->list, &ap_dev->pendingq); + atomic_inc(&ap_poll_requests); + ap_dev->pendingq_count++; + ap_dev->queue_count++; + ap_dev->total_request_count++; + break; + case AP_RESPONSE_Q_FULL: + list_add_tail(&ap_msg->list, &ap_dev->requestq); + ap_dev->requestq_count++; + ap_dev->total_request_count++; + return -EBUSY; + case AP_RESPONSE_MESSAGE_TOO_BIG: + ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); + return -EINVAL; + default: /* Device is gone. */ + ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); + return -ENODEV; + } + } else { + list_add_tail(&ap_msg->list, &ap_dev->requestq); + ap_dev->requestq_count++; + ap_dev->total_request_count++; + return -EBUSY; + } + ap_schedule_poll_timer(); + return 0; +} + +void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) +{ + unsigned long flags; + int rc; + + spin_lock_bh(&ap_dev->lock); + if (!ap_dev->unregistered) { + /* Make room on the queue by polling for finished requests. */ + rc = ap_poll_queue(ap_dev, &flags); + if (!rc) + rc = __ap_queue_message(ap_dev, ap_msg); + if (!rc) + wake_up(&ap_poll_wait); + } else { + ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); + rc = 0; + } + spin_unlock_bh(&ap_dev->lock); + if (rc == -ENODEV) + device_unregister(&ap_dev->device); +} +EXPORT_SYMBOL(ap_queue_message); + +/** + * Cancel a crypto request. This is done by removing the request + * from the devive pendingq or requestq queue. Note that the + * request stays on the AP queue. When it finishes the message + * reply will be discarded because the psmid can't be found. + * @ap_dev: AP device that has the message queued + * @ap_msg: the message that is to be removed + */ +void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg) +{ + struct ap_message *tmp; + + spin_lock_bh(&ap_dev->lock); + if (!list_empty(&ap_msg->list)) { + list_for_each_entry(tmp, &ap_dev->pendingq, list) + if (tmp->psmid == ap_msg->psmid) { + ap_dev->pendingq_count--; + goto found; + } + ap_dev->requestq_count--; + found: + list_del_init(&ap_msg->list); + } + spin_unlock_bh(&ap_dev->lock); +} +EXPORT_SYMBOL(ap_cancel_message); + +/** + * AP receive polling for finished AP requests + */ +static void ap_poll_timeout(unsigned long unused) +{ + tasklet_schedule(&ap_tasklet); +} + +/** + * Poll all AP devices on the bus in a round robin fashion. Continue + * polling until bit 2^0 of the control flags is not set. If bit 2^1 + * of the control flags has been set arm the poll timer. + */ +static int __ap_poll_all(struct device *dev, void *data) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + int rc; + + spin_lock(&ap_dev->lock); + if (!ap_dev->unregistered) { + rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data); + } else + rc = 0; + spin_unlock(&ap_dev->lock); + if (rc) + device_unregister(&ap_dev->device); + return 0; +} + +static void ap_poll_all(unsigned long dummy) +{ + unsigned long flags; + + do { + flags = 0; + bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); + } while (flags & 1); + if (flags & 2) + ap_schedule_poll_timer(); +} + +/** + * AP bus poll thread. The purpose of this thread is to poll for + * finished requests in a loop if there is a "free" cpu - that is + * a cpu that doesn't have anything better to do. The polling stops + * as soon as there is another task or if all messages have been + * delivered. + */ +static int ap_poll_thread(void *data) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int requests; + + set_user_nice(current, -20); + while (1) { + if (need_resched()) { + schedule(); + continue; + } + add_wait_queue(&ap_poll_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + requests = atomic_read(&ap_poll_requests); + if (requests <= 0) + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ap_poll_wait, &wait); + + local_bh_disable(); + flags = 0; + bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); + local_bh_enable(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&ap_poll_wait, &wait); + return 0; +} + +static int ap_poll_thread_start(void) +{ + int rc; + + mutex_lock(&ap_poll_thread_mutex); + if (!ap_poll_kthread) { + ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); + rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0; + if (rc) + ap_poll_kthread = NULL; + } + else + rc = 0; + mutex_unlock(&ap_poll_thread_mutex); + return rc; +} + +static void ap_poll_thread_stop(void) +{ + mutex_lock(&ap_poll_thread_mutex); + if (ap_poll_kthread) { + kthread_stop(ap_poll_kthread); + ap_poll_kthread = NULL; + } + mutex_unlock(&ap_poll_thread_mutex); +} + +/** + * The module initialization code. + */ +int __init ap_module_init(void) +{ + int rc, i; + + if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) { + printk(KERN_WARNING "Invalid param: domain = %d. " + " Not loading.\n", ap_domain_index); + return -EINVAL; + } + if (ap_instructions_available() != 0) { + printk(KERN_WARNING "AP instructions not installed.\n"); + return -ENODEV; + } + + /* Create /sys/bus/ap. */ + rc = bus_register(&ap_bus_type); + if (rc) + goto out; + for (i = 0; ap_bus_attrs[i]; i++) { + rc = bus_create_file(&ap_bus_type, ap_bus_attrs[i]); + if (rc) + goto out_bus; + } + + /* Create /sys/devices/ap. */ + ap_root_device = s390_root_dev_register("ap"); + rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0; + if (rc) + goto out_bus; + + ap_work_queue = create_singlethread_workqueue("kapwork"); + if (!ap_work_queue) { + rc = -ENOMEM; + goto out_root; + } + + if (ap_select_domain() == 0) + ap_scan_bus(NULL); + + /* Setup the ap bus rescan timer. */ + init_timer(&ap_config_timer); + ap_config_timer.function = ap_config_timeout; + ap_config_timer.data = 0; + ap_config_timer.expires = jiffies + ap_config_time * HZ; + add_timer(&ap_config_timer); + + /* Start the low priority AP bus poll thread. */ + if (ap_thread_flag) { + rc = ap_poll_thread_start(); + if (rc) + goto out_work; + } + + return 0; + +out_work: + del_timer_sync(&ap_config_timer); + del_timer_sync(&ap_poll_timer); + destroy_workqueue(ap_work_queue); +out_root: + s390_root_dev_unregister(ap_root_device); +out_bus: + while (i--) + bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); + bus_unregister(&ap_bus_type); +out: + return rc; +} + +static int __ap_match_all(struct device *dev, void *data) +{ + return 1; +} + +/** + * The module termination code + */ +void ap_module_exit(void) +{ + int i; + struct device *dev; + + ap_poll_thread_stop(); + del_timer_sync(&ap_config_timer); + del_timer_sync(&ap_poll_timer); + destroy_workqueue(ap_work_queue); + s390_root_dev_unregister(ap_root_device); + while ((dev = bus_find_device(&ap_bus_type, NULL, NULL, + __ap_match_all))) + { + device_unregister(dev); + put_device(dev); + } + for (i = 0; ap_bus_attrs[i]; i++) + bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); + bus_unregister(&ap_bus_type); +} + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +module_init(ap_module_init); +module_exit(ap_module_exit); +#endif diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h new file mode 100644 index 0000000..83b69c0 --- /dev/null +++ b/drivers/s390/crypto/ap_bus.h @@ -0,0 +1,158 @@ +/* + * linux/drivers/s390/crypto/ap_bus.h + * + * Copyright (C) 2006 IBM Corporation + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * Adjunct processor bus header file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _AP_BUS_H_ +#define _AP_BUS_H_ + +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/types.h> + +#define AP_DEVICES 64 /* Number of AP devices. */ +#define AP_DOMAINS 16 /* Number of AP domains. */ +#define AP_MAX_RESET 90 /* Maximum number of resets. */ +#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ +#define AP_POLL_TIME 1 /* Time in ticks between receive polls. */ + +extern int ap_domain_index; + +/** + * The ap_qid_t identifier of an ap queue. It contains a + * 6 bit device index and a 4 bit queue index (domain). + */ +typedef unsigned int ap_qid_t; + +#define AP_MKQID(_device,_queue) (((_device) & 63) << 8 | ((_queue) & 15)) +#define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63) +#define AP_QID_QUEUE(_qid) ((_qid) & 15) + +/** + * The ap queue status word is returned by all three AP functions + * (PQAP, NQAP and DQAP). There's a set of flags in the first + * byte, followed by a 1 byte response code. + */ +struct ap_queue_status { + unsigned int queue_empty : 1; + unsigned int replies_waiting : 1; + unsigned int queue_full : 1; + unsigned int pad1 : 5; + unsigned int response_code : 8; + unsigned int pad2 : 16; +}; + +#define AP_RESPONSE_NORMAL 0x00 +#define AP_RESPONSE_Q_NOT_AVAIL 0x01 +#define AP_RESPONSE_RESET_IN_PROGRESS 0x02 +#define AP_RESPONSE_DECONFIGURED 0x03 +#define AP_RESPONSE_CHECKSTOPPED 0x04 +#define AP_RESPONSE_BUSY 0x05 +#define AP_RESPONSE_Q_FULL 0x10 +#define AP_RESPONSE_NO_PENDING_REPLY 0x10 +#define AP_RESPONSE_INDEX_TOO_BIG 0x11 +#define AP_RESPONSE_NO_FIRST_PART 0x13 +#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15 + +/** + * Known device types + */ +#define AP_DEVICE_TYPE_PCICC 3 +#define AP_DEVICE_TYPE_PCICA 4 +#define AP_DEVICE_TYPE_PCIXCC 5 +#define AP_DEVICE_TYPE_CEX2A 6 +#define AP_DEVICE_TYPE_CEX2C 7 + +struct ap_device; +struct ap_message; + +struct ap_driver { + struct device_driver driver; + struct ap_device_id *ids; + + int (*probe)(struct ap_device *); + void (*remove)(struct ap_device *); + /* receive is called from tasklet context */ + void (*receive)(struct ap_device *, struct ap_message *, + struct ap_message *); +}; + +#define to_ap_drv(x) container_of((x), struct ap_driver, driver) + +int ap_driver_register(struct ap_driver *, struct module *, char *); +void ap_driver_unregister(struct ap_driver *); + +struct ap_device { + struct device device; + struct ap_driver *drv; /* Pointer to AP device driver. */ + spinlock_t lock; /* Per device lock. */ + + ap_qid_t qid; /* AP queue id. */ + int queue_depth; /* AP queue depth.*/ + int device_type; /* AP device type. */ + int unregistered; /* marks AP device as unregistered */ + + int queue_count; /* # messages currently on AP queue. */ + + struct list_head pendingq; /* List of message sent to AP queue. */ + int pendingq_count; /* # requests on pendingq list. */ + struct list_head requestq; /* List of message yet to be sent. */ + int requestq_count; /* # requests on requestq list. */ + int total_request_count; /* # requests ever for this AP device. */ + + struct ap_message *reply; /* Per device reply message. */ + + void *private; /* ap driver private pointer. */ +}; + +#define to_ap_dev(x) container_of((x), struct ap_device, device) + +struct ap_message { + struct list_head list; /* Request queueing. */ + unsigned long long psmid; /* Message id. */ + void *message; /* Pointer to message buffer. */ + size_t length; /* Message length. */ + + void *private; /* ap driver private pointer. */ +}; + +#define AP_DEVICE(dt) \ + .dev_type=(dt), \ + .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE, + +/** + * Note: don't use ap_send/ap_recv after using ap_queue_message + * for the first time. Otherwise the ap message queue will get + * confused. + */ +int ap_send(ap_qid_t, unsigned long long, void *, size_t); +int ap_recv(ap_qid_t, unsigned long long *, void *, size_t); + +void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg); +void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg); +void ap_flush_queue(struct ap_device *ap_dev); + +int ap_module_init(void); +void ap_module_exit(void); + +#endif /* _AP_BUS_H_ */ diff --git a/drivers/s390/crypto/z90common.h b/drivers/s390/crypto/z90common.h deleted file mode 100644 index dbbcda3..0000000 --- a/drivers/s390/crypto/z90common.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * linux/drivers/s390/crypto/z90common.h - * - * z90crypt 1.3.3 - * - * Copyright (C) 2001, 2005 IBM Corporation - * Author(s): Robert Burroughs (burrough@us.ibm.com) - * Eric Rossman (edrossma@us.ibm.com) - * - * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _Z90COMMON_H_ -#define _Z90COMMON_H_ - - -#define RESPBUFFSIZE 256 -#define PCI_FUNC_KEY_DECRYPT 0x5044 -#define PCI_FUNC_KEY_ENCRYPT 0x504B -extern int ext_bitlens; - -enum devstat { - DEV_GONE, - DEV_ONLINE, - DEV_QUEUE_FULL, - DEV_EMPTY, - DEV_NO_WORK, - DEV_BAD_MESSAGE, - DEV_TSQ_EXCEPTION, - DEV_RSQ_EXCEPTION, - DEV_SEN_EXCEPTION, - DEV_REC_EXCEPTION -}; - -enum hdstat { - HD_NOT_THERE, - HD_BUSY, - HD_DECONFIGURED, - HD_CHECKSTOPPED, - HD_ONLINE, - HD_TSQ_EXCEPTION -}; - -#define Z90C_NO_DEVICES 1 -#define Z90C_AMBIGUOUS_DOMAIN 2 -#define Z90C_INCORRECT_DOMAIN 3 -#define ENOTINIT 4 - -#define SEN_BUSY 7 -#define SEN_USER_ERROR 8 -#define SEN_QUEUE_FULL 11 -#define SEN_NOT_AVAIL 16 -#define SEN_PAD_ERROR 17 -#define SEN_RETRY 18 -#define SEN_RELEASED 24 - -#define REC_EMPTY 4 -#define REC_BUSY 6 -#define REC_OPERAND_INV 8 -#define REC_OPERAND_SIZE 9 -#define REC_EVEN_MOD 10 -#define REC_NO_WORK 11 -#define REC_HARDWAR_ERR 12 -#define REC_NO_RESPONSE 13 -#define REC_RETRY_DEV 14 -#define REC_USER_GONE 15 -#define REC_BAD_MESSAGE 16 -#define REC_INVALID_PAD 17 -#define REC_USE_PCICA 18 - -#define WRONG_DEVICE_TYPE 20 - -#define REC_FATAL_ERROR 32 -#define SEN_FATAL_ERROR 33 -#define TSQ_FATAL_ERROR 34 -#define RSQ_FATAL_ERROR 35 - -#define Z90CRYPT_NUM_TYPES 6 -#define PCICA 0 -#define PCICC 1 -#define PCIXCC_MCL2 2 -#define PCIXCC_MCL3 3 -#define CEX2C 4 -#define CEX2A 5 -#define NILDEV -1 -#define ANYDEV -1 -#define PCIXCC_UNK -2 - -enum hdevice_type { - PCICC_HW = 3, - PCICA_HW = 4, - PCIXCC_HW = 5, - CEX2A_HW = 6, - CEX2C_HW = 7 -}; - -struct CPRBX { - unsigned short cprb_len; - unsigned char cprb_ver_id; - unsigned char pad_000[3]; - unsigned char func_id[2]; - unsigned char cprb_flags[4]; - unsigned int req_parml; - unsigned int req_datal; - unsigned int rpl_msgbl; - unsigned int rpld_parml; - unsigned int rpl_datal; - unsigned int rpld_datal; - unsigned int req_extbl; - unsigned char pad_001[4]; - unsigned int rpld_extbl; - unsigned char req_parmb[16]; - unsigned char req_datab[16]; - unsigned char rpl_parmb[16]; - unsigned char rpl_datab[16]; - unsigned char req_extb[16]; - unsigned char rpl_extb[16]; - unsigned short ccp_rtcode; - unsigned short ccp_rscode; - unsigned int mac_data_len; - unsigned char logon_id[8]; - unsigned char mac_value[8]; - unsigned char mac_content_flgs; - unsigned char pad_002; - unsigned short domain; - unsigned char pad_003[12]; - unsigned char pad_004[36]; -}; - -#ifndef DEV_NAME -#define DEV_NAME "z90crypt" -#endif -#define PRINTK(fmt, args...) \ - printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) -#define PRINTKN(fmt, args...) \ - printk(KERN_DEBUG DEV_NAME ": " fmt, ## args) -#define PRINTKW(fmt, args...) \ - printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) -#define PRINTKC(fmt, args...) \ - printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) - -#ifdef Z90CRYPT_DEBUG -#define PDEBUG(fmt, args...) \ - printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) -#else -#define PDEBUG(fmt, args...) do {} while (0) -#endif - -#define UMIN(a,b) ((a) < (b) ? (a) : (b)) -#define IS_EVEN(x) ((x) == (2 * ((x) / 2))) - -#endif diff --git a/drivers/s390/crypto/z90crypt.h b/drivers/s390/crypto/z90crypt.h deleted file mode 100644 index 0ca1d12..0000000 --- a/drivers/s390/crypto/z90crypt.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * linux/drivers/s390/crypto/z90crypt.h - * - * z90crypt 1.3.3 (kernel-private header) - * - * Copyright (C) 2001, 2005 IBM Corporation - * Author(s): Robert Burroughs (burrough@us.ibm.com) - * Eric Rossman (edrossma@us.ibm.com) - * - * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _Z90CRYPT_H_ -#define _Z90CRYPT_H_ - -#include <asm/z90crypt.h> - -/** - * local errno definitions - */ -#define ENOBUFF 129 // filp->private_data->...>work_elem_p->buffer is NULL -#define EWORKPEND 130 // user issues ioctl while another pending -#define ERELEASED 131 // user released while ioctl pending -#define EQUIESCE 132 // z90crypt quiescing (no more work allowed) -#define ETIMEOUT 133 // request timed out -#define EUNKNOWN 134 // some unrecognized error occured (retry may succeed) -#define EGETBUFF 135 // Error getting buffer or hardware lacks capability - // (retry in software) - -/** - * DEPRECATED STRUCTURES - */ - -/** - * This structure is DEPRECATED and the corresponding ioctl() has been - * replaced with individual ioctl()s for each piece of data! - * This structure will NOT survive past version 1.3.1, so switch to the - * new ioctl()s. - */ -#define MASK_LENGTH 64 // mask length -struct ica_z90_status { - int totalcount; - int leedslitecount; // PCICA - int leeds2count; // PCICC - // int PCIXCCCount; is not in struct for backward compatibility - int requestqWaitCount; - int pendingqWaitCount; - int totalOpenCount; - int cryptoDomain; - // status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3, - // 5=CEX2C - unsigned char status[MASK_LENGTH]; - // qdepth: # work elements waiting for each device - unsigned char qdepth[MASK_LENGTH]; -}; - -#endif /* _Z90CRYPT_H_ */ diff --git a/drivers/s390/crypto/z90hardware.c b/drivers/s390/crypto/z90hardware.c deleted file mode 100644 index be60795..0000000 --- a/drivers/s390/crypto/z90hardware.c +++ /dev/null @@ -1,2531 +0,0 @@ -/* - * linux/drivers/s390/crypto/z90hardware.c - * - * z90crypt 1.3.3 - * - * Copyright (C) 2001, 2005 IBM Corporation - * Author(s): Robert Burroughs (burrough@us.ibm.com) - * Eric Rossman (edrossma@us.ibm.com) - * - * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <asm/uaccess.h> -#include <linux/compiler.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/module.h> -#include "z90crypt.h" -#include "z90common.h" - -struct cca_token_hdr { - unsigned char token_identifier; - unsigned char version; - unsigned short token_length; - unsigned char reserved[4]; -}; - -#define CCA_TKN_HDR_ID_EXT 0x1E - -struct cca_private_ext_ME_sec { - unsigned char section_identifier; - unsigned char version; - unsigned short section_length; - unsigned char private_key_hash[20]; - unsigned char reserved1[4]; - unsigned char key_format; - unsigned char reserved2; - unsigned char key_name_hash[20]; - unsigned char key_use_flags[4]; - unsigned char reserved3[6]; - unsigned char reserved4[24]; - unsigned char confounder[24]; - unsigned char exponent[128]; - unsigned char modulus[128]; -}; - -#define CCA_PVT_USAGE_ALL 0x80 - -struct cca_public_sec { - unsigned char section_identifier; - unsigned char version; - unsigned short section_length; - unsigned char reserved[2]; - unsigned short exponent_len; - unsigned short modulus_bit_len; - unsigned short modulus_byte_len; - unsigned char exponent[3]; -}; - -struct cca_private_ext_ME { - struct cca_token_hdr pvtMEHdr; - struct cca_private_ext_ME_sec pvtMESec; - struct cca_public_sec pubMESec; -}; - -struct cca_public_key { - struct cca_token_hdr pubHdr; - struct cca_public_sec pubSec; -}; - -struct cca_pvt_ext_CRT_sec { - unsigned char section_identifier; - unsigned char version; - unsigned short section_length; - unsigned char private_key_hash[20]; - unsigned char reserved1[4]; - unsigned char key_format; - unsigned char reserved2; - unsigned char key_name_hash[20]; - unsigned char key_use_flags[4]; - unsigned short p_len; - unsigned short q_len; - unsigned short dp_len; - unsigned short dq_len; - unsigned short u_len; - unsigned short mod_len; - unsigned char reserved3[4]; - unsigned short pad_len; - unsigned char reserved4[52]; - unsigned char confounder[8]; -}; - -#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08 -#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40 - -struct cca_private_ext_CRT { - struct cca_token_hdr pvtCrtHdr; - struct cca_pvt_ext_CRT_sec pvtCrtSec; - struct cca_public_sec pubCrtSec; -}; - -struct ap_status_word { - unsigned char q_stat_flags; - unsigned char response_code; - unsigned char reserved[2]; -}; - -#define AP_Q_STATUS_EMPTY 0x80 -#define AP_Q_STATUS_REPLIES_WAITING 0x40 -#define AP_Q_STATUS_ARRAY_FULL 0x20 - -#define AP_RESPONSE_NORMAL 0x00 -#define AP_RESPONSE_Q_NOT_AVAIL 0x01 -#define AP_RESPONSE_RESET_IN_PROGRESS 0x02 -#define AP_RESPONSE_DECONFIGURED 0x03 -#define AP_RESPONSE_CHECKSTOPPED 0x04 -#define AP_RESPONSE_BUSY 0x05 -#define AP_RESPONSE_Q_FULL 0x10 -#define AP_RESPONSE_NO_PENDING_REPLY 0x10 -#define AP_RESPONSE_INDEX_TOO_BIG 0x11 -#define AP_RESPONSE_NO_FIRST_PART 0x13 -#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15 - -#define AP_MAX_CDX_BITL 4 -#define AP_RQID_RESERVED_BITL 4 -#define SKIP_BITL (AP_MAX_CDX_BITL + AP_RQID_RESERVED_BITL) - -struct type4_hdr { - unsigned char reserved1; - unsigned char msg_type_code; - unsigned short msg_len; - unsigned char request_code; - unsigned char msg_fmt; - unsigned short reserved2; -}; - -#define TYPE4_TYPE_CODE 0x04 -#define TYPE4_REQU_CODE 0x40 - -#define TYPE4_SME_LEN 0x0188 -#define TYPE4_LME_LEN 0x0308 -#define TYPE4_SCR_LEN 0x01E0 -#define TYPE4_LCR_LEN 0x03A0 - -#define TYPE4_SME_FMT 0x00 -#define TYPE4_LME_FMT 0x10 -#define TYPE4_SCR_FMT 0x40 -#define TYPE4_LCR_FMT 0x50 - -struct type4_sme { - struct type4_hdr header; - unsigned char message[128]; - unsigned char exponent[128]; - unsigned char modulus[128]; -}; - -struct type4_lme { - struct type4_hdr header; - unsigned char message[256]; - unsigned char exponent[256]; - unsigned char modulus[256]; -}; - -struct type4_scr { - struct type4_hdr header; - unsigned char message[128]; - unsigned char dp[72]; - unsigned char dq[64]; - unsigned char p[72]; - unsigned char q[64]; - unsigned char u[72]; -}; - -struct type4_lcr { - struct type4_hdr header; - unsigned char message[256]; - unsigned char dp[136]; - unsigned char dq[128]; - unsigned char p[136]; - unsigned char q[128]; - unsigned char u[136]; -}; - -union type4_msg { - struct type4_sme sme; - struct type4_lme lme; - struct type4_scr scr; - struct type4_lcr lcr; -}; - -struct type84_hdr { - unsigned char reserved1; - unsigned char code; - unsigned short len; - unsigned char reserved2[4]; -}; - -#define TYPE84_RSP_CODE 0x84 - -struct type6_hdr { - unsigned char reserved1; - unsigned char type; - unsigned char reserved2[2]; - unsigned char right[4]; - unsigned char reserved3[2]; - unsigned char reserved4[2]; - unsigned char apfs[4]; - unsigned int offset1; - unsigned int offset2; - unsigned int offset3; - unsigned int offset4; - unsigned char agent_id[16]; - unsigned char rqid[2]; - unsigned char reserved5[2]; - unsigned char function_code[2]; - unsigned char reserved6[2]; - unsigned int ToCardLen1; - unsigned int ToCardLen2; - unsigned int ToCardLen3; - unsigned int ToCardLen4; - unsigned int FromCardLen1; - unsigned int FromCardLen2; - unsigned int FromCardLen3; - unsigned int FromCardLen4; -}; - -struct CPRB { - unsigned char cprb_len[2]; - unsigned char cprb_ver_id; - unsigned char pad_000; - unsigned char srpi_rtcode[4]; - unsigned char srpi_verb; - unsigned char flags; - unsigned char func_id[2]; - unsigned char checkpoint_flag; - unsigned char resv2; - unsigned char req_parml[2]; - unsigned char req_parmp[4]; - unsigned char req_datal[4]; - unsigned char req_datap[4]; - unsigned char rpl_parml[2]; - unsigned char pad_001[2]; - unsigned char rpl_parmp[4]; - unsigned char rpl_datal[4]; - unsigned char rpl_datap[4]; - unsigned char ccp_rscode[2]; - unsigned char ccp_rtcode[2]; - unsigned char repd_parml[2]; - unsigned char mac_data_len[2]; - unsigned char repd_datal[4]; - unsigned char req_pc[2]; - unsigned char res_origin[8]; - unsigned char mac_value[8]; - unsigned char logon_id[8]; - unsigned char usage_domain[2]; - unsigned char resv3[18]; - unsigned char svr_namel[2]; - unsigned char svr_name[8]; -}; - -struct type6_msg { - struct type6_hdr header; - struct CPRB CPRB; -}; - -struct type86_hdr { - unsigned char reserved1; - unsigned char type; - unsigned char format; - unsigned char reserved2; - unsigned char reply_code; - unsigned char reserved3[3]; -}; - -#define TYPE86_RSP_CODE 0x86 -#define TYPE86_FMT2 0x02 - -struct type86_fmt2_msg { - struct type86_hdr header; - unsigned char reserved[4]; - unsigned char apfs[4]; - unsigned int count1; - unsigned int offset1; - unsigned int count2; - unsigned int offset2; - unsigned int count3; - unsigned int offset3; - unsigned int count4; - unsigned int offset4; -}; - -static struct type6_hdr static_type6_hdr = { - 0x00, - 0x06, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - 0x00000058, - 0x00000000, - 0x00000000, - 0x00000000, - {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50, - 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01}, - {0x00,0x00}, - {0x00,0x00}, - {0x50,0x44}, - {0x00,0x00}, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static struct type6_hdr static_type6_hdrX = { - 0x00, - 0x06, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - 0x00000058, - 0x00000000, - 0x00000000, - 0x00000000, - {0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x50,0x44}, - {0x00,0x00}, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static struct CPRB static_cprb = { - {0x70,0x00}, - 0x41, - 0x00, - {0x00,0x00,0x00,0x00}, - 0x00, - 0x00, - {0x54,0x32}, - 0x01, - 0x00, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x00,0x00}, - {0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00}, - {0x08,0x00}, - {0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20} -}; - -struct function_and_rules_block { - unsigned char function_code[2]; - unsigned char ulen[2]; - unsigned char only_rule[8]; -}; - -static struct function_and_rules_block static_pkd_function_and_rules = { - {0x50,0x44}, - {0x0A,0x00}, - {'P','K','C','S','-','1','.','2'} -}; - -static struct function_and_rules_block static_pke_function_and_rules = { - {0x50,0x4B}, - {0x0A,0x00}, - {'P','K','C','S','-','1','.','2'} -}; - -struct T6_keyBlock_hdr { - unsigned char blen[2]; - unsigned char ulen[2]; - unsigned char flags[2]; -}; - -static struct T6_keyBlock_hdr static_T6_keyBlock_hdr = { - {0x89,0x01}, - {0x87,0x01}, - {0x00} -}; - -static struct CPRBX static_cprbx = { - 0x00DC, - 0x02, - {0x00,0x00,0x00}, - {0x54,0x32}, - {0x00,0x00,0x00,0x00}, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - {0x00,0x00,0x00,0x00}, - 0x00000000, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - 0x0000, - 0x0000, - 0x00000000, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - 0x00, - 0x00, - 0x0000, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} -}; - -static struct function_and_rules_block static_pkd_function_and_rulesX_MCL2 = { - {0x50,0x44}, - {0x00,0x0A}, - {'P','K','C','S','-','1','.','2'} -}; - -static struct function_and_rules_block static_pke_function_and_rulesX_MCL2 = { - {0x50,0x4B}, - {0x00,0x0A}, - {'Z','E','R','O','-','P','A','D'} -}; - -static struct function_and_rules_block static_pkd_function_and_rulesX = { - {0x50,0x44}, - {0x00,0x0A}, - {'Z','E','R','O','-','P','A','D'} -}; - -static struct function_and_rules_block static_pke_function_and_rulesX = { - {0x50,0x4B}, - {0x00,0x0A}, - {'M','R','P',' ',' ',' ',' ',' '} -}; - -static unsigned char static_PKE_function_code[2] = {0x50, 0x4B}; - -struct T6_keyBlock_hdrX { - unsigned short blen; - unsigned short ulen; - unsigned char flags[2]; -}; - -static unsigned char static_pad[256] = { -0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57, -0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39, -0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D, -0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F, -0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45, -0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F, -0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D, -0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9, -0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B, -0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD, -0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1, -0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23, -0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43, -0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F, -0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD, -0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09 -}; - -static struct cca_private_ext_ME static_pvt_me_key = { - { - 0x1E, - 0x00, - 0x0183, - {0x00,0x00,0x00,0x00} - }, - - { - 0x02, - 0x00, - 0x016C, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00}, - 0x00, - 0x00, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00}, - {0x80,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} - }, - - { - 0x04, - 0x00, - 0x000F, - {0x00,0x00}, - 0x0003, - 0x0000, - 0x0000, - {0x01,0x00,0x01} - } -}; - -static struct cca_public_key static_public_key = { - { - 0x1E, - 0x00, - 0x0000, - {0x00,0x00,0x00,0x00} - }, - - { - 0x04, - 0x00, - 0x0000, - {0x00,0x00}, - 0x0000, - 0x0000, - 0x0000, - {0x01,0x00,0x01} - } -}; - -#define FIXED_TYPE6_ME_LEN 0x0000025F - -#define FIXED_TYPE6_ME_EN_LEN 0x000000F0 - -#define FIXED_TYPE6_ME_LENX 0x000002CB - -#define FIXED_TYPE6_ME_EN_LENX 0x0000015C - -static struct cca_public_sec static_cca_pub_sec = { - 0x04, - 0x00, - 0x000f, - {0x00,0x00}, - 0x0003, - 0x0000, - 0x0000, - {0x01,0x00,0x01} -}; - -#define FIXED_TYPE6_CR_LEN 0x00000177 - -#define FIXED_TYPE6_CR_LENX 0x000001E3 - -#define MAX_RESPONSE_SIZE 0x00000710 - -#define MAX_RESPONSEX_SIZE 0x0000077C - -#define RESPONSE_CPRB_SIZE 0x000006B8 -#define RESPONSE_CPRBX_SIZE 0x00000724 - -struct type50_hdr { - u8 reserved1; - u8 msg_type_code; - u16 msg_len; - u8 reserved2; - u8 ignored; - u16 reserved3; -}; - -#define TYPE50_TYPE_CODE 0x50 - -#define TYPE50_MEB1_LEN (sizeof(struct type50_meb1_msg)) -#define TYPE50_MEB2_LEN (sizeof(struct type50_meb2_msg)) -#define TYPE50_CRB1_LEN (sizeof(struct type50_crb1_msg)) -#define TYPE50_CRB2_LEN (sizeof(struct type50_crb2_msg)) - -#define TYPE50_MEB1_FMT 0x0001 -#define TYPE50_MEB2_FMT 0x0002 -#define TYPE50_CRB1_FMT 0x0011 -#define TYPE50_CRB2_FMT 0x0012 - -struct type50_meb1_msg { - struct type50_hdr header; - u16 keyblock_type; - u8 reserved[6]; - u8 exponent[128]; - u8 modulus[128]; - u8 message[128]; -}; - -struct type50_meb2_msg { - struct type50_hdr header; - u16 keyblock_type; - u8 reserved[6]; - u8 exponent[256]; - u8 modulus[256]; - u8 message[256]; -}; - -struct type50_crb1_msg { - struct type50_hdr header; - u16 keyblock_type; - u8 reserved[6]; - u8 p[64]; - u8 q[64]; - u8 dp[64]; - u8 dq[64]; - u8 u[64]; - u8 message[128]; -}; - -struct type50_crb2_msg { - struct type50_hdr header; - u16 keyblock_type; - u8 reserved[6]; - u8 p[128]; - u8 q[128]; - u8 dp[128]; - u8 dq[128]; - u8 u[128]; - u8 message[256]; -}; - -union type50_msg { - struct type50_meb1_msg meb1; - struct type50_meb2_msg meb2; - struct type50_crb1_msg crb1; - struct type50_crb2_msg crb2; -}; - -struct type80_hdr { - u8 reserved1; - u8 type; - u16 len; - u8 code; - u8 reserved2[3]; - u8 reserved3[8]; -}; - -#define TYPE80_RSP_CODE 0x80 - -struct error_hdr { - unsigned char reserved1; - unsigned char type; - unsigned char reserved2[2]; - unsigned char reply_code; - unsigned char reserved3[3]; -}; - -#define TYPE82_RSP_CODE 0x82 -#define TYPE88_RSP_CODE 0x88 - -#define REP82_ERROR_MACHINE_FAILURE 0x10 -#define REP82_ERROR_PREEMPT_FAILURE 0x12 -#define REP82_ERROR_CHECKPT_FAILURE 0x14 -#define REP82_ERROR_MESSAGE_TYPE 0x20 -#define REP82_ERROR_INVALID_COMM_CD 0x21 -#define REP82_ERROR_INVALID_MSG_LEN 0x23 -#define REP82_ERROR_RESERVD_FIELD 0x24 -#define REP82_ERROR_FORMAT_FIELD 0x29 -#define REP82_ERROR_INVALID_COMMAND 0x30 -#define REP82_ERROR_MALFORMED_MSG 0x40 -#define REP82_ERROR_RESERVED_FIELDO 0x50 -#define REP82_ERROR_WORD_ALIGNMENT 0x60 -#define REP82_ERROR_MESSAGE_LENGTH 0x80 -#define REP82_ERROR_OPERAND_INVALID 0x82 -#define REP82_ERROR_OPERAND_SIZE 0x84 -#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85 -#define REP82_ERROR_RESERVED_FIELD 0x88 -#define REP82_ERROR_TRANSPORT_FAIL 0x90 -#define REP82_ERROR_PACKET_TRUNCATED 0xA0 -#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0 - -#define REP88_ERROR_MODULE_FAILURE 0x10 -#define REP88_ERROR_MODULE_TIMEOUT 0x11 -#define REP88_ERROR_MODULE_NOTINIT 0x13 -#define REP88_ERROR_MODULE_NOTAVAIL 0x14 -#define REP88_ERROR_MODULE_DISABLED 0x15 -#define REP88_ERROR_MODULE_IN_DIAGN 0x17 -#define REP88_ERROR_FASTPATH_DISABLD 0x19 -#define REP88_ERROR_MESSAGE_TYPE 0x20 -#define REP88_ERROR_MESSAGE_MALFORMD 0x22 -#define REP88_ERROR_MESSAGE_LENGTH 0x23 -#define REP88_ERROR_RESERVED_FIELD 0x24 -#define REP88_ERROR_KEY_TYPE 0x34 -#define REP88_ERROR_INVALID_KEY 0x82 -#define REP88_ERROR_OPERAND 0x84 -#define REP88_ERROR_OPERAND_EVEN_MOD 0x85 - -#define CALLER_HEADER 12 - -static inline int -testq(int q_nr, int *q_depth, int *dev_type, struct ap_status_word *stat) -{ - int ccode; - - asm volatile -#ifdef CONFIG_64BIT - (" llgfr 0,%4 \n" - " slgr 1,1 \n" - " lgr 2,1 \n" - "0: .long 0xb2af0000 \n" - "1: ipm %0 \n" - " srl %0,28 \n" - " iihh %0,0 \n" - " iihl %0,0 \n" - " lgr %1,1 \n" - " lgr %3,2 \n" - " srl %3,24 \n" - " sll 2,24 \n" - " srl 2,24 \n" - " lgr %2,2 \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h5 \n" - " jg 2b \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 8 \n" - " .quad 0b,3b \n" - " .quad 1b,3b \n" - ".previous" - :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type) - :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION) - :"cc","0","1","2","memory"); -#else - (" lr 0,%4 \n" - " slr 1,1 \n" - " lr 2,1 \n" - "0: .long 0xb2af0000 \n" - "1: ipm %0 \n" - " srl %0,28 \n" - " lr %1,1 \n" - " lr %3,2 \n" - " srl %3,24 \n" - " sll 2,24 \n" - " srl 2,24 \n" - " lr %2,2 \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h5 \n" - " bras 1,4f \n" - " .long 2b \n" - "4: \n" - " l 1,0(1) \n" - " br 1 \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 4 \n" - " .long 0b,3b \n" - " .long 1b,3b \n" - ".previous" - :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type) - :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION) - :"cc","0","1","2","memory"); -#endif - return ccode; -} - -static inline int -resetq(int q_nr, struct ap_status_word *stat_p) -{ - int ccode; - - asm volatile -#ifdef CONFIG_64BIT - (" llgfr 0,%2 \n" - " lghi 1,1 \n" - " sll 1,24 \n" - " or 0,1 \n" - " slgr 1,1 \n" - " lgr 2,1 \n" - "0: .long 0xb2af0000 \n" - "1: ipm %0 \n" - " srl %0,28 \n" - " iihh %0,0 \n" - " iihl %0,0 \n" - " lgr %1,1 \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h3 \n" - " jg 2b \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 8 \n" - " .quad 0b,3b \n" - " .quad 1b,3b \n" - ".previous" - :"=d" (ccode),"=d" (*stat_p) - :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION) - :"cc","0","1","2","memory"); -#else - (" lr 0,%2 \n" - " lhi 1,1 \n" - " sll 1,24 \n" - " or 0,1 \n" - " slr 1,1 \n" - " lr 2,1 \n" - "0: .long 0xb2af0000 \n" - "1: ipm %0 \n" - " srl %0,28 \n" - " lr %1,1 \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h3 \n" - " bras 1,4f \n" - " .long 2b \n" - "4: \n" - " l 1,0(1) \n" - " br 1 \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 4 \n" - " .long 0b,3b \n" - " .long 1b,3b \n" - ".previous" - :"=d" (ccode),"=d" (*stat_p) - :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION) - :"cc","0","1","2","memory"); -#endif - return ccode; -} - -static inline int -sen(int msg_len, unsigned char *msg_ext, struct ap_status_word *stat) -{ - int ccode; - - asm volatile -#ifdef CONFIG_64BIT - (" lgr 6,%3 \n" - " llgfr 7,%2 \n" - " llgt 0,0(6) \n" - " lghi 1,64 \n" - " sll 1,24 \n" - " or 0,1 \n" - " la 6,4(6) \n" - " llgt 2,0(6) \n" - " llgt 3,4(6) \n" - " la 6,8(6) \n" - " slr 1,1 \n" - "0: .long 0xb2ad0026 \n" - "1: brc 2,0b \n" - " ipm %0 \n" - " srl %0,28 \n" - " iihh %0,0 \n" - " iihl %0,0 \n" - " lgr %1,1 \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h4 \n" - " jg 2b \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 8 \n" - " .quad 0b,3b \n" - " .quad 1b,3b \n" - ".previous" - :"=d" (ccode),"=d" (*stat) - :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION) - :"cc","0","1","2","3","6","7","memory"); -#else - (" lr 6,%3 \n" - " lr 7,%2 \n" - " l 0,0(6) \n" - " lhi 1,64 \n" - " sll 1,24 \n" - " or 0,1 \n" - " la 6,4(6) \n" - " l 2,0(6) \n" - " l 3,4(6) \n" - " la 6,8(6) \n" - " slr 1,1 \n" - "0: .long 0xb2ad0026 \n" - "1: brc 2,0b \n" - " ipm %0 \n" - " srl %0,28 \n" - " lr %1,1 \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h4 \n" - " bras 1,4f \n" - " .long 2b \n" - "4: \n" - " l 1,0(1) \n" - " br 1 \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 4 \n" - " .long 0b,3b \n" - " .long 1b,3b \n" - ".previous" - :"=d" (ccode),"=d" (*stat) - :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION) - :"cc","0","1","2","3","6","7","memory"); -#endif - return ccode; -} - -static inline int -rec(int q_nr, int buff_l, unsigned char *rsp, unsigned char *id, - struct ap_status_word *st) -{ - int ccode; - - asm volatile -#ifdef CONFIG_64BIT - (" llgfr 0,%2 \n" - " lgr 3,%4 \n" - " lgr 6,%3 \n" - " llgfr 7,%5 \n" - " lghi 1,128 \n" - " sll 1,24 \n" - " or 0,1 \n" - " slgr 1,1 \n" - " lgr 2,1 \n" - " lgr 4,1 \n" - " lgr 5,1 \n" - "0: .long 0xb2ae0046 \n" - "1: brc 2,0b \n" - " brc 4,0b \n" - " ipm %0 \n" - " srl %0,28 \n" - " iihh %0,0 \n" - " iihl %0,0 \n" - " lgr %1,1 \n" - " st 4,0(3) \n" - " st 5,4(3) \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h6 \n" - " jg 2b \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 8 \n" - " .quad 0b,3b \n" - " .quad 1b,3b \n" - ".previous" - :"=d"(ccode),"=d"(*st) - :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION) - :"cc","0","1","2","3","4","5","6","7","memory"); -#else - (" lr 0,%2 \n" - " lr 3,%4 \n" - " lr 6,%3 \n" - " lr 7,%5 \n" - " lhi 1,128 \n" - " sll 1,24 \n" - " or 0,1 \n" - " slr 1,1 \n" - " lr 2,1 \n" - " lr 4,1 \n" - " lr 5,1 \n" - "0: .long 0xb2ae0046 \n" - "1: brc 2,0b \n" - " brc 4,0b \n" - " ipm %0 \n" - " srl %0,28 \n" - " lr %1,1 \n" - " st 4,0(3) \n" - " st 5,4(3) \n" - "2: \n" - ".section .fixup,\"ax\" \n" - "3: \n" - " lhi %0,%h6 \n" - " bras 1,4f \n" - " .long 2b \n" - "4: \n" - " l 1,0(1) \n" - " br 1 \n" - ".previous \n" - ".section __ex_table,\"a\" \n" - " .align 4 \n" - " .long 0b,3b \n" - " .long 1b,3b \n" - ".previous" - :"=d"(ccode),"=d"(*st) - :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION) - :"cc","0","1","2","3","4","5","6","7","memory"); -#endif - return ccode; -} - -static inline void -itoLe2(int *i_p, unsigned char *lechars) -{ - *lechars = *((unsigned char *) i_p + sizeof(int) - 1); - *(lechars + 1) = *((unsigned char *) i_p + sizeof(int) - 2); -} - -static inline void -le2toI(unsigned char *lechars, int *i_p) -{ - unsigned char *ic_p; - *i_p = 0; - ic_p = (unsigned char *) i_p; - *(ic_p + 2) = *(lechars + 1); - *(ic_p + 3) = *(lechars); -} - -static inline int -is_empty(unsigned char *ptr, int len) -{ - return !memcmp(ptr, (unsigned char *) &static_pvt_me_key+60, len); -} - -enum hdstat -query_online(int deviceNr, int cdx, int resetNr, int *q_depth, int *dev_type) -{ - int q_nr, i, t_depth, t_dev_type; - enum devstat ccode; - struct ap_status_word stat_word; - enum hdstat stat; - int break_out; - - q_nr = (deviceNr << SKIP_BITL) + cdx; - stat = HD_BUSY; - ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word); - PDEBUG("ccode %d response_code %02X\n", ccode, stat_word.response_code); - break_out = 0; - for (i = 0; i < resetNr; i++) { - if (ccode > 3) { - PRINTKC("Exception testing device %d\n", i); - return HD_TSQ_EXCEPTION; - } - switch (ccode) { - case 0: - PDEBUG("t_dev_type %d\n", t_dev_type); - break_out = 1; - stat = HD_ONLINE; - *q_depth = t_depth + 1; - switch (t_dev_type) { - case PCICA_HW: - *dev_type = PCICA; - break; - case PCICC_HW: - *dev_type = PCICC; - break; - case PCIXCC_HW: - *dev_type = PCIXCC_UNK; - break; - case CEX2C_HW: - *dev_type = CEX2C; - break; - case CEX2A_HW: - *dev_type = CEX2A; - break; - default: - *dev_type = NILDEV; - break; - } - PDEBUG("available device %d: Q depth = %d, dev " - "type = %d, stat = %02X%02X%02X%02X\n", - deviceNr, *q_depth, *dev_type, - stat_word.q_stat_flags, - stat_word.response_code, - stat_word.reserved[0], - stat_word.reserved[1]); - break; - case 3: - switch (stat_word.response_code) { - case AP_RESPONSE_NORMAL: - stat = HD_ONLINE; - break_out = 1; - *q_depth = t_depth + 1; - *dev_type = t_dev_type; - PDEBUG("cc3, available device " - "%d: Q depth = %d, dev " - "type = %d, stat = " - "%02X%02X%02X%02X\n", - deviceNr, *q_depth, - *dev_type, - stat_word.q_stat_flags, - stat_word.response_code, - stat_word.reserved[0], - stat_word.reserved[1]); - break; - case AP_RESPONSE_Q_NOT_AVAIL: - stat = HD_NOT_THERE; - break_out = 1; - break; - case AP_RESPONSE_RESET_IN_PROGRESS: - PDEBUG("device %d in reset\n", - deviceNr); - break; - case AP_RESPONSE_DECONFIGURED: - stat = HD_DECONFIGURED; - break_out = 1; - break; - case AP_RESPONSE_CHECKSTOPPED: - stat = HD_CHECKSTOPPED; - break_out = 1; - break; - case AP_RESPONSE_BUSY: - PDEBUG("device %d busy\n", - deviceNr); - break; - default: - break; - } - break; - default: - stat = HD_NOT_THERE; - break_out = 1; - break; - } - if (break_out) - break; - - udelay(5); - - ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word); - } - return stat; -} - -enum devstat -reset_device(int deviceNr, int cdx, int resetNr) -{ - int q_nr, ccode = 0, dummy_qdepth, dummy_devType, i; - struct ap_status_word stat_word; - enum devstat stat; - int break_out; - - q_nr = (deviceNr << SKIP_BITL) + cdx; - stat = DEV_GONE; - ccode = resetq(q_nr, &stat_word); - if (ccode > 3) - return DEV_RSQ_EXCEPTION; - - break_out = 0; - for (i = 0; i < resetNr; i++) { - switch (ccode) { - case 0: - stat = DEV_ONLINE; - if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY) - break_out = 1; - break; - case 3: - switch (stat_word.response_code) { - case AP_RESPONSE_NORMAL: - stat = DEV_ONLINE; - if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY) - break_out = 1; - break; - case AP_RESPONSE_Q_NOT_AVAIL: - case AP_RESPONSE_DECONFIGURED: - case AP_RESPONSE_CHECKSTOPPED: - stat = DEV_GONE; - break_out = 1; - break; - case AP_RESPONSE_RESET_IN_PROGRESS: - case AP_RESPONSE_BUSY: - default: - break; - } - break; - default: - stat = DEV_GONE; - break_out = 1; - break; - } - if (break_out == 1) - break; - udelay(5); - - ccode = testq(q_nr, &dummy_qdepth, &dummy_devType, &stat_word); - if (ccode > 3) { - stat = DEV_TSQ_EXCEPTION; - break; - } - } - PDEBUG("Number of testq's needed for reset: %d\n", i); - - if (i >= resetNr) { - stat = DEV_GONE; - } - - return stat; -} - -#ifdef DEBUG_HYDRA_MSGS -static inline void -print_buffer(unsigned char *buffer, int bufflen) -{ - int i; - for (i = 0; i < bufflen; i += 16) { - PRINTK("%04X: %02X%02X%02X%02X %02X%02X%02X%02X " - "%02X%02X%02X%02X %02X%02X%02X%02X\n", i, - buffer[i+0], buffer[i+1], buffer[i+2], buffer[i+3], - buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7], - buffer[i+8], buffer[i+9], buffer[i+10], buffer[i+11], - buffer[i+12], buffer[i+13], buffer[i+14], buffer[i+15]); - } -} -#endif - -enum devstat -send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext) -{ - struct ap_status_word stat_word; - enum devstat stat; - int ccode; - u32 *q_nr_p = (u32 *)msg_ext; - - *q_nr_p = (dev_nr << SKIP_BITL) + cdx; - PDEBUG("msg_len passed to sen: %d\n", msg_len); - PDEBUG("q number passed to sen: %02x%02x%02x%02x\n", - msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3]); - stat = DEV_GONE; - -#ifdef DEBUG_HYDRA_MSGS - PRINTK("Request header: %02X%02X%02X%02X %02X%02X%02X%02X " - "%02X%02X%02X%02X\n", - msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3], - msg_ext[4], msg_ext[5], msg_ext[6], msg_ext[7], - msg_ext[8], msg_ext[9], msg_ext[10], msg_ext[11]); - print_buffer(msg_ext+CALLER_HEADER, msg_len); -#endif - - ccode = sen(msg_len, msg_ext, &stat_word); - if (ccode > 3) - return DEV_SEN_EXCEPTION; - - PDEBUG("nq cc: %u, st: %02x%02x%02x%02x\n", - ccode, stat_word.q_stat_flags, stat_word.response_code, - stat_word.reserved[0], stat_word.reserved[1]); - switch (ccode) { - case 0: - stat = DEV_ONLINE; - break; - case 1: - stat = DEV_GONE; - break; - case 3: - switch (stat_word.response_code) { - case AP_RESPONSE_NORMAL: - stat = DEV_ONLINE; - break; - case AP_RESPONSE_Q_FULL: - stat = DEV_QUEUE_FULL; - break; - default: - stat = DEV_GONE; - break; - } - break; - default: - stat = DEV_GONE; - break; - } - - return stat; -} - -enum devstat -receive_from_AP(int dev_nr, int cdx, int resplen, unsigned char *resp, - unsigned char *psmid) -{ - int ccode; - struct ap_status_word stat_word; - enum devstat stat; - - memset(resp, 0x00, 8); - - ccode = rec((dev_nr << SKIP_BITL) + cdx, resplen, resp, psmid, - &stat_word); - if (ccode > 3) - return DEV_REC_EXCEPTION; - - PDEBUG("dq cc: %u, st: %02x%02x%02x%02x\n", - ccode, stat_word.q_stat_flags, stat_word.response_code, - stat_word.reserved[0], stat_word.reserved[1]); - - stat = DEV_GONE; - switch (ccode) { - case 0: - stat = DEV_ONLINE; -#ifdef DEBUG_HYDRA_MSGS - print_buffer(resp, resplen); -#endif - break; - case 3: - switch (stat_word.response_code) { - case AP_RESPONSE_NORMAL: - stat = DEV_ONLINE; - break; - case AP_RESPONSE_NO_PENDING_REPLY: - if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY) - stat = DEV_EMPTY; - else - stat = DEV_NO_WORK; - break; - case AP_RESPONSE_INDEX_TOO_BIG: - case AP_RESPONSE_NO_FIRST_PART: - case AP_RESPONSE_MESSAGE_TOO_BIG: - stat = DEV_BAD_MESSAGE; - break; - default: - break; - } - break; - default: - break; - } - - return stat; -} - -static inline int -pad_msg(unsigned char *buffer, int totalLength, int msgLength) -{ - int pad_len; - - for (pad_len = 0; pad_len < (totalLength - msgLength); pad_len++) - if (buffer[pad_len] != 0x00) - break; - pad_len -= 3; - if (pad_len < 8) - return SEN_PAD_ERROR; - - buffer[0] = 0x00; - buffer[1] = 0x02; - - memcpy(buffer+2, static_pad, pad_len); - - buffer[pad_len + 2] = 0x00; - - return 0; -} - -static inline int -is_common_public_key(unsigned char *key, int len) -{ - int i; - - for (i = 0; i < len; i++) - if (key[i]) - break; - key += i; - len -= i; - if (((len == 1) && (key[0] == 3)) || - ((len == 3) && (key[0] == 1) && (key[1] == 0) && (key[2] == 1))) - return 1; - - return 0; -} - -static int -ICAMEX_msg_to_type4MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p, - union type4_msg *z90cMsg_p) -{ - int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len; - unsigned char *mod_tgt, *exp_tgt, *inp_tgt; - union type4_msg *tmp_type4_msg; - - mod_len = icaMex_p->inputdatalength; - - msg_size = ((mod_len <= 128) ? TYPE4_SME_LEN : TYPE4_LME_LEN) + - CALLER_HEADER; - - memset(z90cMsg_p, 0, msg_size); - - tmp_type4_msg = (union type4_msg *) - ((unsigned char *) z90cMsg_p + CALLER_HEADER); - - tmp_type4_msg->sme.header.msg_type_code = TYPE4_TYPE_CODE; - tmp_type4_msg->sme.header.request_code = TYPE4_REQU_CODE; - - if (mod_len <= 128) { - tmp_type4_msg->sme.header.msg_fmt = TYPE4_SME_FMT; - tmp_type4_msg->sme.header.msg_len = TYPE4_SME_LEN; - mod_tgt = tmp_type4_msg->sme.modulus; - mod_tgt_len = sizeof(tmp_type4_msg->sme.modulus); - exp_tgt = tmp_type4_msg->sme.exponent; - exp_tgt_len = sizeof(tmp_type4_msg->sme.exponent); - inp_tgt = tmp_type4_msg->sme.message; - inp_tgt_len = sizeof(tmp_type4_msg->sme.message); - } else { - tmp_type4_msg->lme.header.msg_fmt = TYPE4_LME_FMT; - tmp_type4_msg->lme.header.msg_len = TYPE4_LME_LEN; - mod_tgt = tmp_type4_msg->lme.modulus; - mod_tgt_len = sizeof(tmp_type4_msg->lme.modulus); - exp_tgt = tmp_type4_msg->lme.exponent; - exp_tgt_len = sizeof(tmp_type4_msg->lme.exponent); - inp_tgt = tmp_type4_msg->lme.message; - inp_tgt_len = sizeof(tmp_type4_msg->lme.message); - } - - mod_tgt += (mod_tgt_len - mod_len); - if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len)) - return SEN_RELEASED; - if (is_empty(mod_tgt, mod_len)) - return SEN_USER_ERROR; - exp_tgt += (exp_tgt_len - mod_len); - if (copy_from_user(exp_tgt, icaMex_p->b_key, mod_len)) - return SEN_RELEASED; - if (is_empty(exp_tgt, mod_len)) - return SEN_USER_ERROR; - inp_tgt += (inp_tgt_len - mod_len); - if (copy_from_user(inp_tgt, icaMex_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(inp_tgt, mod_len)) - return SEN_USER_ERROR; - - *z90cMsg_l_p = msg_size - CALLER_HEADER; - - return 0; -} - -static int -ICACRT_msg_to_type4CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, - int *z90cMsg_l_p, union type4_msg *z90cMsg_p) -{ - int mod_len, short_len, long_len, tmp_size, p_tgt_len, q_tgt_len, - dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len; - unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt; - union type4_msg *tmp_type4_msg; - - mod_len = icaMsg_p->inputdatalength; - short_len = mod_len / 2; - long_len = mod_len / 2 + 8; - - tmp_size = ((mod_len <= 128) ? TYPE4_SCR_LEN : TYPE4_LCR_LEN) + - CALLER_HEADER; - - memset(z90cMsg_p, 0, tmp_size); - - tmp_type4_msg = (union type4_msg *) - ((unsigned char *) z90cMsg_p + CALLER_HEADER); - - tmp_type4_msg->scr.header.msg_type_code = TYPE4_TYPE_CODE; - tmp_type4_msg->scr.header.request_code = TYPE4_REQU_CODE; - if (mod_len <= 128) { - tmp_type4_msg->scr.header.msg_fmt = TYPE4_SCR_FMT; - tmp_type4_msg->scr.header.msg_len = TYPE4_SCR_LEN; - p_tgt = tmp_type4_msg->scr.p; - p_tgt_len = sizeof(tmp_type4_msg->scr.p); - q_tgt = tmp_type4_msg->scr.q; - q_tgt_len = sizeof(tmp_type4_msg->scr.q); - dp_tgt = tmp_type4_msg->scr.dp; - dp_tgt_len = sizeof(tmp_type4_msg->scr.dp); - dq_tgt = tmp_type4_msg->scr.dq; - dq_tgt_len = sizeof(tmp_type4_msg->scr.dq); - u_tgt = tmp_type4_msg->scr.u; - u_tgt_len = sizeof(tmp_type4_msg->scr.u); - inp_tgt = tmp_type4_msg->scr.message; - inp_tgt_len = sizeof(tmp_type4_msg->scr.message); - } else { - tmp_type4_msg->lcr.header.msg_fmt = TYPE4_LCR_FMT; - tmp_type4_msg->lcr.header.msg_len = TYPE4_LCR_LEN; - p_tgt = tmp_type4_msg->lcr.p; - p_tgt_len = sizeof(tmp_type4_msg->lcr.p); - q_tgt = tmp_type4_msg->lcr.q; - q_tgt_len = sizeof(tmp_type4_msg->lcr.q); - dp_tgt = tmp_type4_msg->lcr.dp; - dp_tgt_len = sizeof(tmp_type4_msg->lcr.dp); - dq_tgt = tmp_type4_msg->lcr.dq; - dq_tgt_len = sizeof(tmp_type4_msg->lcr.dq); - u_tgt = tmp_type4_msg->lcr.u; - u_tgt_len = sizeof(tmp_type4_msg->lcr.u); - inp_tgt = tmp_type4_msg->lcr.message; - inp_tgt_len = sizeof(tmp_type4_msg->lcr.message); - } - - p_tgt += (p_tgt_len - long_len); - if (copy_from_user(p_tgt, icaMsg_p->np_prime, long_len)) - return SEN_RELEASED; - if (is_empty(p_tgt, long_len)) - return SEN_USER_ERROR; - q_tgt += (q_tgt_len - short_len); - if (copy_from_user(q_tgt, icaMsg_p->nq_prime, short_len)) - return SEN_RELEASED; - if (is_empty(q_tgt, short_len)) - return SEN_USER_ERROR; - dp_tgt += (dp_tgt_len - long_len); - if (copy_from_user(dp_tgt, icaMsg_p->bp_key, long_len)) - return SEN_RELEASED; - if (is_empty(dp_tgt, long_len)) - return SEN_USER_ERROR; - dq_tgt += (dq_tgt_len - short_len); - if (copy_from_user(dq_tgt, icaMsg_p->bq_key, short_len)) - return SEN_RELEASED; - if (is_empty(dq_tgt, short_len)) - return SEN_USER_ERROR; - u_tgt += (u_tgt_len - long_len); - if (copy_from_user(u_tgt, icaMsg_p->u_mult_inv, long_len)) - return SEN_RELEASED; - if (is_empty(u_tgt, long_len)) - return SEN_USER_ERROR; - inp_tgt += (inp_tgt_len - mod_len); - if (copy_from_user(inp_tgt, icaMsg_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(inp_tgt, mod_len)) - return SEN_USER_ERROR; - - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -static int -ICAMEX_msg_to_type6MEX_de_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx, - int *z90cMsg_l_p, struct type6_msg *z90cMsg_p) -{ - int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l; - unsigned char *temp; - struct type6_hdr *tp6Hdr_p; - struct CPRB *cprb_p; - struct cca_private_ext_ME *key_p; - static int deprecated_msg_count = 0; - - mod_len = icaMsg_p->inputdatalength; - tmp_size = FIXED_TYPE6_ME_LEN + mod_len; - total_CPRB_len = tmp_size - sizeof(struct type6_hdr); - parmBlock_l = total_CPRB_len - sizeof(struct CPRB); - tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER; - - memset(z90cMsg_p, 0, tmp_size); - - temp = (unsigned char *)z90cMsg_p + CALLER_HEADER; - memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr)); - tp6Hdr_p = (struct type6_hdr *)temp; - tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4); - tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE; - - temp += sizeof(struct type6_hdr); - memcpy(temp, &static_cprb, sizeof(struct CPRB)); - cprb_p = (struct CPRB *) temp; - cprb_p->usage_domain[0]= (unsigned char)cdx; - itoLe2(&parmBlock_l, cprb_p->req_parml); - itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml); - - temp += sizeof(struct CPRB); - memcpy(temp, &static_pkd_function_and_rules, - sizeof(struct function_and_rules_block)); - - temp += sizeof(struct function_and_rules_block); - vud_len = 2 + icaMsg_p->inputdatalength; - itoLe2(&vud_len, temp); - - temp += 2; - if (copy_from_user(temp, icaMsg_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(temp, mod_len)) - return SEN_USER_ERROR; - - temp += mod_len; - memcpy(temp, &static_T6_keyBlock_hdr, sizeof(struct T6_keyBlock_hdr)); - - temp += sizeof(struct T6_keyBlock_hdr); - memcpy(temp, &static_pvt_me_key, sizeof(struct cca_private_ext_ME)); - key_p = (struct cca_private_ext_ME *)temp; - temp = key_p->pvtMESec.exponent + sizeof(key_p->pvtMESec.exponent) - - mod_len; - if (copy_from_user(temp, icaMsg_p->b_key, mod_len)) - return SEN_RELEASED; - if (is_empty(temp, mod_len)) - return SEN_USER_ERROR; - - if (is_common_public_key(temp, mod_len)) { - if (deprecated_msg_count < 20) { - PRINTK("Common public key used for modex decrypt\n"); - deprecated_msg_count++; - if (deprecated_msg_count == 20) - PRINTK("No longer issuing messages about common" - " public key for modex decrypt.\n"); - } - return SEN_NOT_AVAIL; - } - - temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus) - - mod_len; - if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len)) - return SEN_RELEASED; - if (is_empty(temp, mod_len)) - return SEN_USER_ERROR; - - key_p->pubMESec.modulus_bit_len = 8 * mod_len; - - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -static int -ICAMEX_msg_to_type6MEX_en_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx, - int *z90cMsg_l_p, struct type6_msg *z90cMsg_p) -{ - int mod_len, vud_len, exp_len, key_len; - int pad_len, tmp_size, total_CPRB_len, parmBlock_l, i; - unsigned char *temp_exp, *exp_p, *temp; - struct type6_hdr *tp6Hdr_p; - struct CPRB *cprb_p; - struct cca_public_key *key_p; - struct T6_keyBlock_hdr *keyb_p; - - temp_exp = kmalloc(256, GFP_KERNEL); - if (!temp_exp) - return EGETBUFF; - mod_len = icaMsg_p->inputdatalength; - if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len)) { - kfree(temp_exp); - return SEN_RELEASED; - } - if (is_empty(temp_exp, mod_len)) { - kfree(temp_exp); - return SEN_USER_ERROR; - } - - exp_p = temp_exp; - for (i = 0; i < mod_len; i++) - if (exp_p[i]) - break; - if (i >= mod_len) { - kfree(temp_exp); - return SEN_USER_ERROR; - } - - exp_len = mod_len - i; - exp_p += i; - - PDEBUG("exp_len after computation: %08x\n", exp_len); - tmp_size = FIXED_TYPE6_ME_EN_LEN + 2 * mod_len + exp_len; - total_CPRB_len = tmp_size - sizeof(struct type6_hdr); - parmBlock_l = total_CPRB_len - sizeof(struct CPRB); - tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER; - - vud_len = 2 + mod_len; - memset(z90cMsg_p, 0, tmp_size); - - temp = (unsigned char *)z90cMsg_p + CALLER_HEADER; - memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr)); - tp6Hdr_p = (struct type6_hdr *)temp; - tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4); - tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE; - memcpy(tp6Hdr_p->function_code, static_PKE_function_code, - sizeof(static_PKE_function_code)); - temp += sizeof(struct type6_hdr); - memcpy(temp, &static_cprb, sizeof(struct CPRB)); - cprb_p = (struct CPRB *) temp; - cprb_p->usage_domain[0]= (unsigned char)cdx; - itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml); - temp += sizeof(struct CPRB); - memcpy(temp, &static_pke_function_and_rules, - sizeof(struct function_and_rules_block)); - temp += sizeof(struct function_and_rules_block); - temp += 2; - if (copy_from_user(temp, icaMsg_p->inputdata, mod_len)) { - kfree(temp_exp); - return SEN_RELEASED; - } - if (is_empty(temp, mod_len)) { - kfree(temp_exp); - return SEN_USER_ERROR; - } - if ((temp[0] != 0x00) || (temp[1] != 0x02)) { - kfree(temp_exp); - return SEN_NOT_AVAIL; - } - for (i = 2; i < mod_len; i++) - if (temp[i] == 0x00) - break; - if ((i < 9) || (i > (mod_len - 2))) { - kfree(temp_exp); - return SEN_NOT_AVAIL; - } - pad_len = i + 1; - vud_len = mod_len - pad_len; - memmove(temp, temp+pad_len, vud_len); - temp -= 2; - vud_len += 2; - itoLe2(&vud_len, temp); - temp += (vud_len); - keyb_p = (struct T6_keyBlock_hdr *)temp; - temp += sizeof(struct T6_keyBlock_hdr); - memcpy(temp, &static_public_key, sizeof(static_public_key)); - key_p = (struct cca_public_key *)temp; - temp = key_p->pubSec.exponent; - memcpy(temp, exp_p, exp_len); - kfree(temp_exp); - temp += exp_len; - if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len)) - return SEN_RELEASED; - if (is_empty(temp, mod_len)) - return SEN_USER_ERROR; - key_p->pubSec.modulus_bit_len = 8 * mod_len; - key_p->pubSec.modulus_byte_len = mod_len; - key_p->pubSec.exponent_len = exp_len; - key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len; - key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr); - key_p->pubHdr.token_length = key_len; - key_len += 4; - itoLe2(&key_len, keyb_p->ulen); - key_len += 2; - itoLe2(&key_len, keyb_p->blen); - parmBlock_l -= pad_len; - itoLe2(&parmBlock_l, cprb_p->req_parml); - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -static int -ICACRT_msg_to_type6CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx, - int *z90cMsg_l_p, struct type6_msg *z90cMsg_p) -{ - int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len; - int long_len, pad_len, keyPartsLen, tmp_l; - unsigned char *tgt_p, *temp; - struct type6_hdr *tp6Hdr_p; - struct CPRB *cprb_p; - struct cca_token_hdr *keyHdr_p; - struct cca_pvt_ext_CRT_sec *pvtSec_p; - struct cca_public_sec *pubSec_p; - - mod_len = icaMsg_p->inputdatalength; - short_len = mod_len / 2; - long_len = 8 + short_len; - keyPartsLen = 3 * long_len + 2 * short_len; - pad_len = (8 - (keyPartsLen % 8)) % 8; - keyPartsLen += pad_len + mod_len; - tmp_size = FIXED_TYPE6_CR_LEN + keyPartsLen + mod_len; - total_CPRB_len = tmp_size - sizeof(struct type6_hdr); - parmBlock_l = total_CPRB_len - sizeof(struct CPRB); - vud_len = 2 + mod_len; - tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER; - - memset(z90cMsg_p, 0, tmp_size); - tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER; - memcpy(tgt_p, &static_type6_hdr, sizeof(struct type6_hdr)); - tp6Hdr_p = (struct type6_hdr *)tgt_p; - tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4); - tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE; - tgt_p += sizeof(struct type6_hdr); - cprb_p = (struct CPRB *) tgt_p; - memcpy(tgt_p, &static_cprb, sizeof(struct CPRB)); - cprb_p->usage_domain[0]= *((unsigned char *)(&(cdx))+3); - itoLe2(&parmBlock_l, cprb_p->req_parml); - memcpy(cprb_p->rpl_parml, cprb_p->req_parml, - sizeof(cprb_p->req_parml)); - tgt_p += sizeof(struct CPRB); - memcpy(tgt_p, &static_pkd_function_and_rules, - sizeof(struct function_and_rules_block)); - tgt_p += sizeof(struct function_and_rules_block); - itoLe2(&vud_len, tgt_p); - tgt_p += 2; - if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, mod_len)) - return SEN_USER_ERROR; - tgt_p += mod_len; - tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) + - sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen; - itoLe2(&tmp_l, tgt_p); - temp = tgt_p + 2; - tmp_l -= 2; - itoLe2(&tmp_l, temp); - tgt_p += sizeof(struct T6_keyBlock_hdr); - keyHdr_p = (struct cca_token_hdr *)tgt_p; - keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT; - tmp_l -= 4; - keyHdr_p->token_length = tmp_l; - tgt_p += sizeof(struct cca_token_hdr); - pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p; - pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT; - pvtSec_p->section_length = - sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen; - pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL; - pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL; - pvtSec_p->p_len = long_len; - pvtSec_p->q_len = short_len; - pvtSec_p->dp_len = long_len; - pvtSec_p->dq_len = short_len; - pvtSec_p->u_len = long_len; - pvtSec_p->mod_len = mod_len; - pvtSec_p->pad_len = pad_len; - tgt_p += sizeof(struct cca_pvt_ext_CRT_sec); - if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, long_len)) - return SEN_USER_ERROR; - tgt_p += long_len; - if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, short_len)) - return SEN_USER_ERROR; - tgt_p += short_len; - if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, long_len)) - return SEN_USER_ERROR; - tgt_p += long_len; - if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, short_len)) - return SEN_USER_ERROR; - tgt_p += short_len; - if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, long_len)) - return SEN_USER_ERROR; - tgt_p += long_len; - tgt_p += pad_len; - memset(tgt_p, 0xFF, mod_len); - tgt_p += mod_len; - memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec)); - pubSec_p = (struct cca_public_sec *) tgt_p; - pubSec_p->modulus_bit_len = 8 * mod_len; - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -static int -ICAMEX_msg_to_type6MEX_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx, - int *z90cMsg_l_p, struct type6_msg *z90cMsg_p, - int dev_type) -{ - int mod_len, exp_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l; - int key_len, i; - unsigned char *temp_exp, *tgt_p, *temp, *exp_p; - struct type6_hdr *tp6Hdr_p; - struct CPRBX *cprbx_p; - struct cca_public_key *key_p; - struct T6_keyBlock_hdrX *keyb_p; - - temp_exp = kmalloc(256, GFP_KERNEL); - if (!temp_exp) - return EGETBUFF; - mod_len = icaMsg_p->inputdatalength; - if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len)) { - kfree(temp_exp); - return SEN_RELEASED; - } - if (is_empty(temp_exp, mod_len)) { - kfree(temp_exp); - return SEN_USER_ERROR; - } - exp_p = temp_exp; - for (i = 0; i < mod_len; i++) - if (exp_p[i]) - break; - if (i >= mod_len) { - kfree(temp_exp); - return SEN_USER_ERROR; - } - exp_len = mod_len - i; - exp_p += i; - PDEBUG("exp_len after computation: %08x\n", exp_len); - tmp_size = FIXED_TYPE6_ME_EN_LENX + 2 * mod_len + exp_len; - total_CPRB_len = tmp_size - sizeof(struct type6_hdr); - parmBlock_l = total_CPRB_len - sizeof(struct CPRBX); - tmp_size = tmp_size + CALLER_HEADER; - vud_len = 2 + mod_len; - memset(z90cMsg_p, 0, tmp_size); - tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER; - memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr)); - tp6Hdr_p = (struct type6_hdr *)tgt_p; - tp6Hdr_p->ToCardLen1 = total_CPRB_len; - tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE; - memcpy(tp6Hdr_p->function_code, static_PKE_function_code, - sizeof(static_PKE_function_code)); - tgt_p += sizeof(struct type6_hdr); - memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX)); - cprbx_p = (struct CPRBX *) tgt_p; - cprbx_p->domain = (unsigned short)cdx; - cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE; - tgt_p += sizeof(struct CPRBX); - if (dev_type == PCIXCC_MCL2) - memcpy(tgt_p, &static_pke_function_and_rulesX_MCL2, - sizeof(struct function_and_rules_block)); - else - memcpy(tgt_p, &static_pke_function_and_rulesX, - sizeof(struct function_and_rules_block)); - tgt_p += sizeof(struct function_and_rules_block); - - tgt_p += 2; - if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len)) { - kfree(temp_exp); - return SEN_RELEASED; - } - if (is_empty(tgt_p, mod_len)) { - kfree(temp_exp); - return SEN_USER_ERROR; - } - tgt_p -= 2; - *((short *)tgt_p) = (short) vud_len; - tgt_p += vud_len; - keyb_p = (struct T6_keyBlock_hdrX *)tgt_p; - tgt_p += sizeof(struct T6_keyBlock_hdrX); - memcpy(tgt_p, &static_public_key, sizeof(static_public_key)); - key_p = (struct cca_public_key *)tgt_p; - temp = key_p->pubSec.exponent; - memcpy(temp, exp_p, exp_len); - kfree(temp_exp); - temp += exp_len; - if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len)) - return SEN_RELEASED; - if (is_empty(temp, mod_len)) - return SEN_USER_ERROR; - key_p->pubSec.modulus_bit_len = 8 * mod_len; - key_p->pubSec.modulus_byte_len = mod_len; - key_p->pubSec.exponent_len = exp_len; - key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len; - key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr); - key_p->pubHdr.token_length = key_len; - key_len += 4; - keyb_p->ulen = (unsigned short)key_len; - key_len += 2; - keyb_p->blen = (unsigned short)key_len; - cprbx_p->req_parml = parmBlock_l; - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -static int -ICACRT_msg_to_type6CRT_msgX(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx, - int *z90cMsg_l_p, struct type6_msg *z90cMsg_p, - int dev_type) -{ - int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len; - int long_len, pad_len, keyPartsLen, tmp_l; - unsigned char *tgt_p, *temp; - struct type6_hdr *tp6Hdr_p; - struct CPRBX *cprbx_p; - struct cca_token_hdr *keyHdr_p; - struct cca_pvt_ext_CRT_sec *pvtSec_p; - struct cca_public_sec *pubSec_p; - - mod_len = icaMsg_p->inputdatalength; - short_len = mod_len / 2; - long_len = 8 + short_len; - keyPartsLen = 3 * long_len + 2 * short_len; - pad_len = (8 - (keyPartsLen % 8)) % 8; - keyPartsLen += pad_len + mod_len; - tmp_size = FIXED_TYPE6_CR_LENX + keyPartsLen + mod_len; - total_CPRB_len = tmp_size - sizeof(struct type6_hdr); - parmBlock_l = total_CPRB_len - sizeof(struct CPRBX); - vud_len = 2 + mod_len; - tmp_size = tmp_size + CALLER_HEADER; - memset(z90cMsg_p, 0, tmp_size); - tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER; - memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr)); - tp6Hdr_p = (struct type6_hdr *)tgt_p; - tp6Hdr_p->ToCardLen1 = total_CPRB_len; - tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE; - tgt_p += sizeof(struct type6_hdr); - cprbx_p = (struct CPRBX *) tgt_p; - memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX)); - cprbx_p->domain = (unsigned short)cdx; - cprbx_p->req_parml = parmBlock_l; - cprbx_p->rpl_msgbl = parmBlock_l; - tgt_p += sizeof(struct CPRBX); - if (dev_type == PCIXCC_MCL2) - memcpy(tgt_p, &static_pkd_function_and_rulesX_MCL2, - sizeof(struct function_and_rules_block)); - else - memcpy(tgt_p, &static_pkd_function_and_rulesX, - sizeof(struct function_and_rules_block)); - tgt_p += sizeof(struct function_and_rules_block); - *((short *)tgt_p) = (short) vud_len; - tgt_p += 2; - if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, mod_len)) - return SEN_USER_ERROR; - tgt_p += mod_len; - tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) + - sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen; - *((short *)tgt_p) = (short) tmp_l; - temp = tgt_p + 2; - tmp_l -= 2; - *((short *)temp) = (short) tmp_l; - tgt_p += sizeof(struct T6_keyBlock_hdr); - keyHdr_p = (struct cca_token_hdr *)tgt_p; - keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT; - tmp_l -= 4; - keyHdr_p->token_length = tmp_l; - tgt_p += sizeof(struct cca_token_hdr); - pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p; - pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT; - pvtSec_p->section_length = - sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen; - pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL; - pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL; - pvtSec_p->p_len = long_len; - pvtSec_p->q_len = short_len; - pvtSec_p->dp_len = long_len; - pvtSec_p->dq_len = short_len; - pvtSec_p->u_len = long_len; - pvtSec_p->mod_len = mod_len; - pvtSec_p->pad_len = pad_len; - tgt_p += sizeof(struct cca_pvt_ext_CRT_sec); - if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, long_len)) - return SEN_USER_ERROR; - tgt_p += long_len; - if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, short_len)) - return SEN_USER_ERROR; - tgt_p += short_len; - if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, long_len)) - return SEN_USER_ERROR; - tgt_p += long_len; - if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, short_len)) - return SEN_USER_ERROR; - tgt_p += short_len; - if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len)) - return SEN_RELEASED; - if (is_empty(tgt_p, long_len)) - return SEN_USER_ERROR; - tgt_p += long_len; - tgt_p += pad_len; - memset(tgt_p, 0xFF, mod_len); - tgt_p += mod_len; - memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec)); - pubSec_p = (struct cca_public_sec *) tgt_p; - pubSec_p->modulus_bit_len = 8 * mod_len; - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -static int -ICAMEX_msg_to_type50MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p, - union type50_msg *z90cMsg_p) -{ - int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len; - unsigned char *mod_tgt, *exp_tgt, *inp_tgt; - union type50_msg *tmp_type50_msg; - - mod_len = icaMex_p->inputdatalength; - - msg_size = ((mod_len <= 128) ? TYPE50_MEB1_LEN : TYPE50_MEB2_LEN) + - CALLER_HEADER; - - memset(z90cMsg_p, 0, msg_size); - - tmp_type50_msg = (union type50_msg *) - ((unsigned char *) z90cMsg_p + CALLER_HEADER); - - tmp_type50_msg->meb1.header.msg_type_code = TYPE50_TYPE_CODE; - - if (mod_len <= 128) { - tmp_type50_msg->meb1.header.msg_len = TYPE50_MEB1_LEN; - tmp_type50_msg->meb1.keyblock_type = TYPE50_MEB1_FMT; - mod_tgt = tmp_type50_msg->meb1.modulus; - mod_tgt_len = sizeof(tmp_type50_msg->meb1.modulus); - exp_tgt = tmp_type50_msg->meb1.exponent; - exp_tgt_len = sizeof(tmp_type50_msg->meb1.exponent); - inp_tgt = tmp_type50_msg->meb1.message; - inp_tgt_len = sizeof(tmp_type50_msg->meb1.message); - } else { - tmp_type50_msg->meb2.header.msg_len = TYPE50_MEB2_LEN; - tmp_type50_msg->meb2.keyblock_type = TYPE50_MEB2_FMT; - mod_tgt = tmp_type50_msg->meb2.modulus; - mod_tgt_len = sizeof(tmp_type50_msg->meb2.modulus); - exp_tgt = tmp_type50_msg->meb2.exponent; - exp_tgt_len = sizeof(tmp_type50_msg->meb2.exponent); - inp_tgt = tmp_type50_msg->meb2.message; - inp_tgt_len = sizeof(tmp_type50_msg->meb2.message); - } - - mod_tgt += (mod_tgt_len - mod_len); - if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len)) - return SEN_RELEASED; - if (is_empty(mod_tgt, mod_len)) - return SEN_USER_ERROR; - exp_tgt += (exp_tgt_len - mod_len); - if (copy_from_user(exp_tgt, icaMex_p->b_key, mod_len)) - return SEN_RELEASED; - if (is_empty(exp_tgt, mod_len)) - return SEN_USER_ERROR; - inp_tgt += (inp_tgt_len - mod_len); - if (copy_from_user(inp_tgt, icaMex_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(inp_tgt, mod_len)) - return SEN_USER_ERROR; - - *z90cMsg_l_p = msg_size - CALLER_HEADER; - - return 0; -} - -static int -ICACRT_msg_to_type50CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, - int *z90cMsg_l_p, union type50_msg *z90cMsg_p) -{ - int mod_len, short_len, long_len, tmp_size, p_tgt_len, q_tgt_len, - dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len, long_offset; - unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt, - temp[8]; - union type50_msg *tmp_type50_msg; - - mod_len = icaMsg_p->inputdatalength; - short_len = mod_len / 2; - long_len = mod_len / 2 + 8; - long_offset = 0; - - if (long_len > 128) { - memset(temp, 0x00, sizeof(temp)); - if (copy_from_user(temp, icaMsg_p->np_prime, long_len-128)) - return SEN_RELEASED; - if (!is_empty(temp, 8)) - return SEN_NOT_AVAIL; - if (copy_from_user(temp, icaMsg_p->bp_key, long_len-128)) - return SEN_RELEASED; - if (!is_empty(temp, 8)) - return SEN_NOT_AVAIL; - if (copy_from_user(temp, icaMsg_p->u_mult_inv, long_len-128)) - return SEN_RELEASED; - if (!is_empty(temp, 8)) - return SEN_NOT_AVAIL; - long_offset = long_len - 128; - long_len = 128; - } - - tmp_size = ((long_len <= 64) ? TYPE50_CRB1_LEN : TYPE50_CRB2_LEN) + - CALLER_HEADER; - - memset(z90cMsg_p, 0, tmp_size); - - tmp_type50_msg = (union type50_msg *) - ((unsigned char *) z90cMsg_p + CALLER_HEADER); - - tmp_type50_msg->crb1.header.msg_type_code = TYPE50_TYPE_CODE; - if (long_len <= 64) { - tmp_type50_msg->crb1.header.msg_len = TYPE50_CRB1_LEN; - tmp_type50_msg->crb1.keyblock_type = TYPE50_CRB1_FMT; - p_tgt = tmp_type50_msg->crb1.p; - p_tgt_len = sizeof(tmp_type50_msg->crb1.p); - q_tgt = tmp_type50_msg->crb1.q; - q_tgt_len = sizeof(tmp_type50_msg->crb1.q); - dp_tgt = tmp_type50_msg->crb1.dp; - dp_tgt_len = sizeof(tmp_type50_msg->crb1.dp); - dq_tgt = tmp_type50_msg->crb1.dq; - dq_tgt_len = sizeof(tmp_type50_msg->crb1.dq); - u_tgt = tmp_type50_msg->crb1.u; - u_tgt_len = sizeof(tmp_type50_msg->crb1.u); - inp_tgt = tmp_type50_msg->crb1.message; - inp_tgt_len = sizeof(tmp_type50_msg->crb1.message); - } else { - tmp_type50_msg->crb2.header.msg_len = TYPE50_CRB2_LEN; - tmp_type50_msg->crb2.keyblock_type = TYPE50_CRB2_FMT; - p_tgt = tmp_type50_msg->crb2.p; - p_tgt_len = sizeof(tmp_type50_msg->crb2.p); - q_tgt = tmp_type50_msg->crb2.q; - q_tgt_len = sizeof(tmp_type50_msg->crb2.q); - dp_tgt = tmp_type50_msg->crb2.dp; - dp_tgt_len = sizeof(tmp_type50_msg->crb2.dp); - dq_tgt = tmp_type50_msg->crb2.dq; - dq_tgt_len = sizeof(tmp_type50_msg->crb2.dq); - u_tgt = tmp_type50_msg->crb2.u; - u_tgt_len = sizeof(tmp_type50_msg->crb2.u); - inp_tgt = tmp_type50_msg->crb2.message; - inp_tgt_len = sizeof(tmp_type50_msg->crb2.message); - } - - p_tgt += (p_tgt_len - long_len); - if (copy_from_user(p_tgt, icaMsg_p->np_prime + long_offset, long_len)) - return SEN_RELEASED; - if (is_empty(p_tgt, long_len)) - return SEN_USER_ERROR; - q_tgt += (q_tgt_len - short_len); - if (copy_from_user(q_tgt, icaMsg_p->nq_prime, short_len)) - return SEN_RELEASED; - if (is_empty(q_tgt, short_len)) - return SEN_USER_ERROR; - dp_tgt += (dp_tgt_len - long_len); - if (copy_from_user(dp_tgt, icaMsg_p->bp_key + long_offset, long_len)) - return SEN_RELEASED; - if (is_empty(dp_tgt, long_len)) - return SEN_USER_ERROR; - dq_tgt += (dq_tgt_len - short_len); - if (copy_from_user(dq_tgt, icaMsg_p->bq_key, short_len)) - return SEN_RELEASED; - if (is_empty(dq_tgt, short_len)) - return SEN_USER_ERROR; - u_tgt += (u_tgt_len - long_len); - if (copy_from_user(u_tgt, icaMsg_p->u_mult_inv + long_offset, long_len)) - return SEN_RELEASED; - if (is_empty(u_tgt, long_len)) - return SEN_USER_ERROR; - inp_tgt += (inp_tgt_len - mod_len); - if (copy_from_user(inp_tgt, icaMsg_p->inputdata, mod_len)) - return SEN_RELEASED; - if (is_empty(inp_tgt, mod_len)) - return SEN_USER_ERROR; - - *z90cMsg_l_p = tmp_size - CALLER_HEADER; - - return 0; -} - -int -convert_request(unsigned char *buffer, int func, unsigned short function, - int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p) -{ - if (dev_type == PCICA) { - if (func == ICARSACRT) - return ICACRT_msg_to_type4CRT_msg( - (struct ica_rsa_modexpo_crt *) buffer, - msg_l_p, (union type4_msg *) msg_p); - else - return ICAMEX_msg_to_type4MEX_msg( - (struct ica_rsa_modexpo *) buffer, - msg_l_p, (union type4_msg *) msg_p); - } - if (dev_type == PCICC) { - if (func == ICARSACRT) - return ICACRT_msg_to_type6CRT_msg( - (struct ica_rsa_modexpo_crt *) buffer, - cdx, msg_l_p, (struct type6_msg *)msg_p); - if (function == PCI_FUNC_KEY_ENCRYPT) - return ICAMEX_msg_to_type6MEX_en_msg( - (struct ica_rsa_modexpo *) buffer, - cdx, msg_l_p, (struct type6_msg *) msg_p); - else - return ICAMEX_msg_to_type6MEX_de_msg( - (struct ica_rsa_modexpo *) buffer, - cdx, msg_l_p, (struct type6_msg *) msg_p); - } - if ((dev_type == PCIXCC_MCL2) || - (dev_type == PCIXCC_MCL3) || - (dev_type == CEX2C)) { - if (func == ICARSACRT) - return ICACRT_msg_to_type6CRT_msgX( - (struct ica_rsa_modexpo_crt *) buffer, - cdx, msg_l_p, (struct type6_msg *) msg_p, - dev_type); - else - return ICAMEX_msg_to_type6MEX_msgX( - (struct ica_rsa_modexpo *) buffer, - cdx, msg_l_p, (struct type6_msg *) msg_p, - dev_type); - } - if (dev_type == CEX2A) { - if (func == ICARSACRT) - return ICACRT_msg_to_type50CRT_msg( - (struct ica_rsa_modexpo_crt *) buffer, - msg_l_p, (union type50_msg *) msg_p); - else - return ICAMEX_msg_to_type50MEX_msg( - (struct ica_rsa_modexpo *) buffer, - msg_l_p, (union type50_msg *) msg_p); - } - - return 0; -} - -int ext_bitlens_msg_count = 0; -static inline void -unset_ext_bitlens(void) -{ - if (!ext_bitlens_msg_count) { - PRINTK("Unable to use coprocessors for extended bitlengths. " - "Using PCICAs/CEX2As (if present) for extended " - "bitlengths. This is not an error.\n"); - ext_bitlens_msg_count++; - } - ext_bitlens = 0; -} - -int -convert_response(unsigned char *response, unsigned char *buffer, - int *respbufflen_p, unsigned char *resp_buff) -{ - struct ica_rsa_modexpo *icaMsg_p = (struct ica_rsa_modexpo *) buffer; - struct error_hdr *errh_p = (struct error_hdr *) response; - struct type80_hdr *t80h_p = (struct type80_hdr *) response; - struct type84_hdr *t84h_p = (struct type84_hdr *) response; - struct type86_fmt2_msg *t86m_p = (struct type86_fmt2_msg *) response; - int reply_code, service_rc, service_rs, src_l; - unsigned char *src_p, *tgt_p; - struct CPRB *cprb_p; - struct CPRBX *cprbx_p; - - src_p = 0; - reply_code = 0; - service_rc = 0; - service_rs = 0; - src_l = 0; - switch (errh_p->type) { - case TYPE82_RSP_CODE: - case TYPE88_RSP_CODE: - reply_code = errh_p->reply_code; - src_p = (unsigned char *)errh_p; - PRINTK("Hardware error: Type %02X Message Header: " - "%02x%02x%02x%02x%02x%02x%02x%02x\n", - errh_p->type, - src_p[0], src_p[1], src_p[2], src_p[3], - src_p[4], src_p[5], src_p[6], src_p[7]); - break; - case TYPE80_RSP_CODE: - src_l = icaMsg_p->outputdatalength; - src_p = response + (int)t80h_p->len - src_l; - break; - case TYPE84_RSP_CODE: - src_l = icaMsg_p->outputdatalength; - src_p = response + (int)t84h_p->len - src_l; - break; - case TYPE86_RSP_CODE: - reply_code = t86m_p->header.reply_code; - if (reply_code != 0) - break; - cprb_p = (struct CPRB *) - (response + sizeof(struct type86_fmt2_msg)); - cprbx_p = (struct CPRBX *) cprb_p; - if (cprb_p->cprb_ver_id != 0x02) { - le2toI(cprb_p->ccp_rtcode, &service_rc); - if (service_rc != 0) { - le2toI(cprb_p->ccp_rscode, &service_rs); - if ((service_rc == 8) && (service_rs == 66)) - PDEBUG("Bad block format on PCICC\n"); - else if ((service_rc == 8) && (service_rs == 65)) - PDEBUG("Probably an even modulus on " - "PCICC\n"); - else if ((service_rc == 8) && (service_rs == 770)) { - PDEBUG("Invalid key length on PCICC\n"); - unset_ext_bitlens(); - return REC_USE_PCICA; - } - else if ((service_rc == 8) && (service_rs == 783)) { - PDEBUG("Extended bitlengths not enabled" - "on PCICC\n"); - unset_ext_bitlens(); - return REC_USE_PCICA; - } - else - PRINTK("service rc/rs (PCICC): %d/%d\n", - service_rc, service_rs); - return REC_OPERAND_INV; - } - src_p = (unsigned char *)cprb_p + sizeof(struct CPRB); - src_p += 4; - le2toI(src_p, &src_l); - src_l -= 2; - src_p += 2; - } else { - service_rc = (int)cprbx_p->ccp_rtcode; - if (service_rc != 0) { - service_rs = (int) cprbx_p->ccp_rscode; - if ((service_rc == 8) && (service_rs == 66)) - PDEBUG("Bad block format on PCIXCC\n"); - else if ((service_rc == 8) && (service_rs == 65)) - PDEBUG("Probably an even modulus on " - "PCIXCC\n"); - else if ((service_rc == 8) && (service_rs == 770)) { - PDEBUG("Invalid key length on PCIXCC\n"); - unset_ext_bitlens(); - return REC_USE_PCICA; - } - else if ((service_rc == 8) && (service_rs == 783)) { - PDEBUG("Extended bitlengths not enabled" - "on PCIXCC\n"); - unset_ext_bitlens(); - return REC_USE_PCICA; - } - else - PRINTK("service rc/rs (PCIXCC): %d/%d\n", - service_rc, service_rs); - return REC_OPERAND_INV; - } - src_p = (unsigned char *) - cprbx_p + sizeof(struct CPRBX); - src_p += 4; - src_l = (int)(*((short *) src_p)); - src_l -= 2; - src_p += 2; - } - break; - default: - src_p = (unsigned char *)errh_p; - PRINTK("Unrecognized Message Header: " - "%02x%02x%02x%02x%02x%02x%02x%02x\n", - src_p[0], src_p[1], src_p[2], src_p[3], - src_p[4], src_p[5], src_p[6], src_p[7]); - return REC_BAD_MESSAGE; - } - - if (reply_code) - switch (reply_code) { - case REP82_ERROR_MACHINE_FAILURE: - if (errh_p->type == TYPE82_RSP_CODE) - PRINTKW("Machine check failure\n"); - else - PRINTKW("Module failure\n"); - return REC_HARDWAR_ERR; - case REP82_ERROR_OPERAND_INVALID: - return REC_OPERAND_INV; - case REP88_ERROR_MESSAGE_MALFORMD: - PRINTKW("Message malformed\n"); - return REC_OPERAND_INV; - case REP82_ERROR_OPERAND_SIZE: - return REC_OPERAND_SIZE; - case REP82_ERROR_EVEN_MOD_IN_OPND: - return REC_EVEN_MOD; - case REP82_ERROR_MESSAGE_TYPE: - return WRONG_DEVICE_TYPE; - case REP82_ERROR_TRANSPORT_FAIL: - PRINTKW("Transport failed (APFS = %02X%02X%02X%02X)\n", - t86m_p->apfs[0], t86m_p->apfs[1], - t86m_p->apfs[2], t86m_p->apfs[3]); - return REC_HARDWAR_ERR; - default: - PRINTKW("reply code = %d\n", reply_code); - return REC_HARDWAR_ERR; - } - - if (service_rc != 0) - return REC_OPERAND_INV; - - if ((src_l > icaMsg_p->outputdatalength) || - (src_l > RESPBUFFSIZE) || - (src_l <= 0)) - return REC_OPERAND_SIZE; - - PDEBUG("Length returned = %d\n", src_l); - tgt_p = resp_buff + icaMsg_p->outputdatalength - src_l; - memcpy(tgt_p, src_p, src_l); - if ((errh_p->type == TYPE86_RSP_CODE) && (resp_buff < tgt_p)) { - memset(resp_buff, 0, icaMsg_p->outputdatalength - src_l); - if (pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l)) - return REC_INVALID_PAD; - } - *respbufflen_p = icaMsg_p->outputdatalength; - if (*respbufflen_p == 0) - PRINTK("Zero *respbufflen_p\n"); - - return 0; -} - diff --git a/drivers/s390/crypto/z90main.c b/drivers/s390/crypto/z90main.c deleted file mode 100644 index b2f20ab..0000000 --- a/drivers/s390/crypto/z90main.c +++ /dev/null @@ -1,3379 +0,0 @@ -/* - * linux/drivers/s390/crypto/z90main.c - * - * z90crypt 1.3.3 - * - * Copyright (C) 2001, 2005 IBM Corporation - * Author(s): Robert Burroughs (burrough@us.ibm.com) - * Eric Rossman (edrossma@us.ibm.com) - * - * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <asm/uaccess.h> // copy_(from|to)_user -#include <linux/compat.h> -#include <linux/compiler.h> -#include <linux/delay.h> // mdelay -#include <linux/init.h> -#include <linux/interrupt.h> // for tasklets -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/proc_fs.h> -#include <linux/syscalls.h> -#include "z90crypt.h" -#include "z90common.h" - -/** - * Defaults that may be modified. - */ - -/** - * You can specify a different minor at compile time. - */ -#ifndef Z90CRYPT_MINOR -#define Z90CRYPT_MINOR MISC_DYNAMIC_MINOR -#endif - -/** - * You can specify a different domain at compile time or on the insmod - * command line. - */ -#ifndef DOMAIN_INDEX -#define DOMAIN_INDEX -1 -#endif - -/** - * This is the name under which the device is registered in /proc/modules. - */ -#define REG_NAME "z90crypt" - -/** - * Cleanup should run every CLEANUPTIME seconds and should clean up requests - * older than CLEANUPTIME seconds in the past. - */ -#ifndef CLEANUPTIME -#define CLEANUPTIME 15 -#endif - -/** - * Config should run every CONFIGTIME seconds - */ -#ifndef CONFIGTIME -#define CONFIGTIME 30 -#endif - -/** - * The first execution of the config task should take place - * immediately after initialization - */ -#ifndef INITIAL_CONFIGTIME -#define INITIAL_CONFIGTIME 1 -#endif - -/** - * Reader should run every READERTIME milliseconds - * With the 100Hz patch for s390, z90crypt can lock the system solid while - * under heavy load. We'll try to avoid that. - */ -#ifndef READERTIME -#if HZ > 1000 -#define READERTIME 2 -#else -#define READERTIME 10 -#endif -#endif - -/** - * turn long device array index into device pointer - */ -#define LONG2DEVPTR(ndx) (z90crypt.device_p[(ndx)]) - -/** - * turn short device array index into long device array index - */ -#define SHRT2LONG(ndx) (z90crypt.overall_device_x.device_index[(ndx)]) - -/** - * turn short device array index into device pointer - */ -#define SHRT2DEVPTR(ndx) LONG2DEVPTR(SHRT2LONG(ndx)) - -/** - * Status for a work-element - */ -#define STAT_DEFAULT 0x00 // request has not been processed - -#define STAT_ROUTED 0x80 // bit 7: requests get routed to specific device - // else, device is determined each write -#define STAT_FAILED 0x40 // bit 6: this bit is set if the request failed - // before being sent to the hardware. -#define STAT_WRITTEN 0x30 // bits 5-4: work to be done, not sent to device -// 0x20 // UNUSED state -#define STAT_READPEND 0x10 // bits 5-4: work done, we're returning data now -#define STAT_NOWORK 0x00 // bits off: no work on any queue -#define STAT_RDWRMASK 0x30 // mask for bits 5-4 - -/** - * Macros to check the status RDWRMASK - */ -#define CHK_RDWRMASK(statbyte) ((statbyte) & STAT_RDWRMASK) -#define SET_RDWRMASK(statbyte, newval) \ - {(statbyte) &= ~STAT_RDWRMASK; (statbyte) |= newval;} - -/** - * Audit Trail. Progress of a Work element - * audit[0]: Unless noted otherwise, these bits are all set by the process - */ -#define FP_COPYFROM 0x80 // Caller's buffer has been copied to work element -#define FP_BUFFREQ 0x40 // Low Level buffer requested -#define FP_BUFFGOT 0x20 // Low Level buffer obtained -#define FP_SENT 0x10 // Work element sent to a crypto device - // (may be set by process or by reader task) -#define FP_PENDING 0x08 // Work element placed on pending queue - // (may be set by process or by reader task) -#define FP_REQUEST 0x04 // Work element placed on request queue -#define FP_ASLEEP 0x02 // Work element about to sleep -#define FP_AWAKE 0x01 // Work element has been awakened - -/** - * audit[1]: These bits are set by the reader task and/or the cleanup task - */ -#define FP_NOTPENDING 0x80 // Work element removed from pending queue -#define FP_AWAKENING 0x40 // Caller about to be awakened -#define FP_TIMEDOUT 0x20 // Caller timed out -#define FP_RESPSIZESET 0x10 // Response size copied to work element -#define FP_RESPADDRCOPIED 0x08 // Response address copied to work element -#define FP_RESPBUFFCOPIED 0x04 // Response buffer copied to work element -#define FP_REMREQUEST 0x02 // Work element removed from request queue -#define FP_SIGNALED 0x01 // Work element was awakened by a signal - -/** - * audit[2]: unused - */ - -/** - * state of the file handle in private_data.status - */ -#define STAT_OPEN 0 -#define STAT_CLOSED 1 - -/** - * PID() expands to the process ID of the current process - */ -#define PID() (current->pid) - -/** - * Selected Constants. The number of APs and the number of devices - */ -#ifndef Z90CRYPT_NUM_APS -#define Z90CRYPT_NUM_APS 64 -#endif -#ifndef Z90CRYPT_NUM_DEVS -#define Z90CRYPT_NUM_DEVS Z90CRYPT_NUM_APS -#endif - -/** - * Buffer size for receiving responses. The maximum Response Size - * is actually the maximum request size, since in an error condition - * the request itself may be returned unchanged. - */ -#define MAX_RESPONSE_SIZE 0x0000077C - -/** - * A count and status-byte mask - */ -struct status { - int st_count; // # of enabled devices - int disabled_count; // # of disabled devices - int user_disabled_count; // # of devices disabled via proc fs - unsigned char st_mask[Z90CRYPT_NUM_APS]; // current status mask -}; - -/** - * The array of device indexes is a mechanism for fast indexing into - * a long (and sparse) array. For instance, if APs 3, 9 and 47 are - * installed, z90CDeviceIndex[0] is 3, z90CDeviceIndex[1] is 9, and - * z90CDeviceIndex[2] is 47. - */ -struct device_x { - int device_index[Z90CRYPT_NUM_DEVS]; -}; - -/** - * All devices are arranged in a single array: 64 APs - */ -struct device { - int dev_type; // PCICA, PCICC, PCIXCC_MCL2, - // PCIXCC_MCL3, CEX2C, CEX2A - enum devstat dev_stat; // current device status - int dev_self_x; // Index in array - int disabled; // Set when device is in error - int user_disabled; // Set when device is disabled by user - int dev_q_depth; // q depth - unsigned char * dev_resp_p; // Response buffer address - int dev_resp_l; // Response Buffer length - int dev_caller_count; // Number of callers - int dev_total_req_cnt; // # requests for device since load - struct list_head dev_caller_list; // List of callers -}; - -/** - * There's a struct status and a struct device_x for each device type. - */ -struct hdware_block { - struct status hdware_mask; - struct status type_mask[Z90CRYPT_NUM_TYPES]; - struct device_x type_x_addr[Z90CRYPT_NUM_TYPES]; - unsigned char device_type_array[Z90CRYPT_NUM_APS]; -}; - -/** - * z90crypt is the topmost data structure in the hierarchy. - */ -struct z90crypt { - int max_count; // Nr of possible crypto devices - struct status mask; - int q_depth_array[Z90CRYPT_NUM_DEVS]; - int dev_type_array[Z90CRYPT_NUM_DEVS]; - struct device_x overall_device_x; // array device indexes - struct device * device_p[Z90CRYPT_NUM_DEVS]; - int terminating; - int domain_established;// TRUE: domain has been found - int cdx; // Crypto Domain Index - int len; // Length of this data structure - struct hdware_block *hdware_info; -}; - -/** - * An array of these structures is pointed to from dev_caller - * The length of the array depends on the device type. For APs, - * there are 8. - * - * The caller buffer is allocated to the user at OPEN. At WRITE, - * it contains the request; at READ, the response. The function - * send_to_crypto_device converts the request to device-dependent - * form and use the caller's OPEN-allocated buffer for the response. - * - * For the contents of caller_dev_dep_req and caller_dev_dep_req_p - * because that points to it, see the discussion in z90hardware.c. - * Search for "extended request message block". - */ -struct caller { - int caller_buf_l; // length of original request - unsigned char * caller_buf_p; // Original request on WRITE - int caller_dev_dep_req_l; // len device dependent request - unsigned char * caller_dev_dep_req_p; // Device dependent form - unsigned char caller_id[8]; // caller-supplied message id - struct list_head caller_liste; - unsigned char caller_dev_dep_req[MAX_RESPONSE_SIZE]; -}; - -/** - * Function prototypes from z90hardware.c - */ -enum hdstat query_online(int deviceNr, int cdx, int resetNr, int *q_depth, - int *dev_type); -enum devstat reset_device(int deviceNr, int cdx, int resetNr); -enum devstat send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext); -enum devstat receive_from_AP(int dev_nr, int cdx, int resplen, - unsigned char *resp, unsigned char *psmid); -int convert_request(unsigned char *buffer, int func, unsigned short function, - int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p); -int convert_response(unsigned char *response, unsigned char *buffer, - int *respbufflen_p, unsigned char *resp_buff); - -/** - * Low level function prototypes - */ -static int create_z90crypt(int *cdx_p); -static int refresh_z90crypt(int *cdx_p); -static int find_crypto_devices(struct status *deviceMask); -static int create_crypto_device(int index); -static int destroy_crypto_device(int index); -static void destroy_z90crypt(void); -static int refresh_index_array(struct status *status_str, - struct device_x *index_array); -static int probe_device_type(struct device *devPtr); -static int probe_PCIXCC_type(struct device *devPtr); - -/** - * proc fs definitions - */ -static struct proc_dir_entry *z90crypt_entry; - -/** - * data structures - */ - -/** - * work_element.opener points back to this structure - */ -struct priv_data { - pid_t opener_pid; - unsigned char status; // 0: open 1: closed -}; - -/** - * A work element is allocated for each request - */ -struct work_element { - struct priv_data *priv_data; - pid_t pid; - int devindex; // index of device processing this w_e - // (If request did not specify device, - // -1 until placed onto a queue) - int devtype; - struct list_head liste; // used for requestq and pendingq - char buffer[128]; // local copy of user request - int buff_size; // size of the buffer for the request - char resp_buff[RESPBUFFSIZE]; - int resp_buff_size; - char __user * resp_addr; // address of response in user space - unsigned int funccode; // function code of request - wait_queue_head_t waitq; - unsigned long requestsent; // time at which the request was sent - atomic_t alarmrung; // wake-up signal - unsigned char caller_id[8]; // pid + counter, for this w_e - unsigned char status[1]; // bits to mark status of the request - unsigned char audit[3]; // record of work element's progress - unsigned char * requestptr; // address of request buffer - int retcode; // return code of request -}; - -/** - * High level function prototypes - */ -static int z90crypt_open(struct inode *, struct file *); -static int z90crypt_release(struct inode *, struct file *); -static ssize_t z90crypt_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t z90crypt_write(struct file *, const char __user *, - size_t, loff_t *); -static long z90crypt_unlocked_ioctl(struct file *, unsigned int, unsigned long); -static long z90crypt_compat_ioctl(struct file *, unsigned int, unsigned long); - -static void z90crypt_reader_task(unsigned long); -static void z90crypt_schedule_reader_task(unsigned long); -static void z90crypt_config_task(unsigned long); -static void z90crypt_cleanup_task(unsigned long); - -static int z90crypt_status(char *, char **, off_t, int, int *, void *); -static int z90crypt_status_write(struct file *, const char __user *, - unsigned long, void *); - -/** - * Storage allocated at initialization and used throughout the life of - * this insmod - */ -static int domain = DOMAIN_INDEX; -static struct z90crypt z90crypt; -static int quiesce_z90crypt; -static spinlock_t queuespinlock; -static struct list_head request_list; -static int requestq_count; -static struct list_head pending_list; -static int pendingq_count; - -static struct tasklet_struct reader_tasklet; -static struct timer_list reader_timer; -static struct timer_list config_timer; -static struct timer_list cleanup_timer; -static atomic_t total_open; -static atomic_t z90crypt_step; - -static struct file_operations z90crypt_fops = { - .owner = THIS_MODULE, - .read = z90crypt_read, - .write = z90crypt_write, - .unlocked_ioctl = z90crypt_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = z90crypt_compat_ioctl, -#endif - .open = z90crypt_open, - .release = z90crypt_release -}; - -static struct miscdevice z90crypt_misc_device = { - .minor = Z90CRYPT_MINOR, - .name = DEV_NAME, - .fops = &z90crypt_fops, -}; - -/** - * Documentation values. - */ -MODULE_AUTHOR("zSeries Linux Crypto Team: Robert H. Burroughs, Eric D. Rossman" - "and Jochen Roehrig"); -MODULE_DESCRIPTION("zSeries Linux Cryptographic Coprocessor device driver, " - "Copyright 2001, 2005 IBM Corporation"); -MODULE_LICENSE("GPL"); -module_param(domain, int, 0); -MODULE_PARM_DESC(domain, "domain index for device"); - -#ifdef CONFIG_COMPAT -/** - * ioctl32 conversion routines - */ -struct ica_rsa_modexpo_32 { // For 32-bit callers - compat_uptr_t inputdata; - unsigned int inputdatalength; - compat_uptr_t outputdata; - unsigned int outputdatalength; - compat_uptr_t b_key; - compat_uptr_t n_modulus; -}; - -static long -trans_modexpo32(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct ica_rsa_modexpo_32 __user *mex32u = compat_ptr(arg); - struct ica_rsa_modexpo_32 mex32k; - struct ica_rsa_modexpo __user *mex64; - long ret = 0; - unsigned int i; - - if (!access_ok(VERIFY_WRITE, mex32u, sizeof(struct ica_rsa_modexpo_32))) - return -EFAULT; - mex64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo)); - if (!access_ok(VERIFY_WRITE, mex64, sizeof(struct ica_rsa_modexpo))) - return -EFAULT; - if (copy_from_user(&mex32k, mex32u, sizeof(struct ica_rsa_modexpo_32))) - return -EFAULT; - if (__put_user(compat_ptr(mex32k.inputdata), &mex64->inputdata) || - __put_user(mex32k.inputdatalength, &mex64->inputdatalength) || - __put_user(compat_ptr(mex32k.outputdata), &mex64->outputdata) || - __put_user(mex32k.outputdatalength, &mex64->outputdatalength) || - __put_user(compat_ptr(mex32k.b_key), &mex64->b_key) || - __put_user(compat_ptr(mex32k.n_modulus), &mex64->n_modulus)) - return -EFAULT; - ret = z90crypt_unlocked_ioctl(filp, cmd, (unsigned long)mex64); - if (!ret) - if (__get_user(i, &mex64->outputdatalength) || - __put_user(i, &mex32u->outputdatalength)) - ret = -EFAULT; - return ret; -} - -struct ica_rsa_modexpo_crt_32 { // For 32-bit callers - compat_uptr_t inputdata; - unsigned int inputdatalength; - compat_uptr_t outputdata; - unsigned int outputdatalength; - compat_uptr_t bp_key; - compat_uptr_t bq_key; - compat_uptr_t np_prime; - compat_uptr_t nq_prime; - compat_uptr_t u_mult_inv; -}; - -static long -trans_modexpo_crt32(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct ica_rsa_modexpo_crt_32 __user *crt32u = compat_ptr(arg); - struct ica_rsa_modexpo_crt_32 crt32k; - struct ica_rsa_modexpo_crt __user *crt64; - long ret = 0; - unsigned int i; - - if (!access_ok(VERIFY_WRITE, crt32u, - sizeof(struct ica_rsa_modexpo_crt_32))) - return -EFAULT; - crt64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo_crt)); - if (!access_ok(VERIFY_WRITE, crt64, sizeof(struct ica_rsa_modexpo_crt))) - return -EFAULT; - if (copy_from_user(&crt32k, crt32u, - sizeof(struct ica_rsa_modexpo_crt_32))) - return -EFAULT; - if (__put_user(compat_ptr(crt32k.inputdata), &crt64->inputdata) || - __put_user(crt32k.inputdatalength, &crt64->inputdatalength) || - __put_user(compat_ptr(crt32k.outputdata), &crt64->outputdata) || - __put_user(crt32k.outputdatalength, &crt64->outputdatalength) || - __put_user(compat_ptr(crt32k.bp_key), &crt64->bp_key) || - __put_user(compat_ptr(crt32k.bq_key), &crt64->bq_key) || - __put_user(compat_ptr(crt32k.np_prime), &crt64->np_prime) || - __put_user(compat_ptr(crt32k.nq_prime), &crt64->nq_prime) || - __put_user(compat_ptr(crt32k.u_mult_inv), &crt64->u_mult_inv)) - return -EFAULT; - ret = z90crypt_unlocked_ioctl(filp, cmd, (unsigned long)crt64); - if (!ret) - if (__get_user(i, &crt64->outputdatalength) || - __put_user(i, &crt32u->outputdatalength)) - ret = -EFAULT; - return ret; -} - -static long -z90crypt_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case ICAZ90STATUS: - case Z90QUIESCE: - case Z90STAT_TOTALCOUNT: - case Z90STAT_PCICACOUNT: - case Z90STAT_PCICCCOUNT: - case Z90STAT_PCIXCCCOUNT: - case Z90STAT_PCIXCCMCL2COUNT: - case Z90STAT_PCIXCCMCL3COUNT: - case Z90STAT_CEX2CCOUNT: - case Z90STAT_REQUESTQ_COUNT: - case Z90STAT_PENDINGQ_COUNT: - case Z90STAT_TOTALOPEN_COUNT: - case Z90STAT_DOMAIN_INDEX: - case Z90STAT_STATUS_MASK: - case Z90STAT_QDEPTH_MASK: - case Z90STAT_PERDEV_REQCNT: - return z90crypt_unlocked_ioctl(filp, cmd, arg); - case ICARSAMODEXPO: - return trans_modexpo32(filp, cmd, arg); - case ICARSACRT: - return trans_modexpo_crt32(filp, cmd, arg); - default: - return -ENOIOCTLCMD; - } -} -#endif - -/** - * The module initialization code. - */ -static int __init -z90crypt_init_module(void) -{ - int result, nresult; - struct proc_dir_entry *entry; - - PDEBUG("PID %d\n", PID()); - - if ((domain < -1) || (domain > 15)) { - PRINTKW("Invalid param: domain = %d. Not loading.\n", domain); - return -EINVAL; - } - - /* Register as misc device with given minor (or get a dynamic one). */ - result = misc_register(&z90crypt_misc_device); - if (result < 0) { - PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n", - z90crypt_misc_device.minor, result); - return result; - } - - PDEBUG("Registered " DEV_NAME " with result %d\n", result); - - result = create_z90crypt(&domain); - if (result != 0) { - PRINTKW("create_z90crypt (domain index %d) failed with %d.\n", - domain, result); - result = -ENOMEM; - goto init_module_cleanup; - } - - if (result == 0) { - PRINTKN("Version %d.%d.%d loaded, built on %s %s\n", - z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT, - __DATE__, __TIME__); - PDEBUG("create_z90crypt (domain index %d) successful.\n", - domain); - } else - PRINTK("No devices at startup\n"); - - /* Initialize globals. */ - spin_lock_init(&queuespinlock); - - INIT_LIST_HEAD(&pending_list); - pendingq_count = 0; - - INIT_LIST_HEAD(&request_list); - requestq_count = 0; - - quiesce_z90crypt = 0; - - atomic_set(&total_open, 0); - atomic_set(&z90crypt_step, 0); - - /* Set up the cleanup task. */ - init_timer(&cleanup_timer); - cleanup_timer.function = z90crypt_cleanup_task; - cleanup_timer.data = 0; - cleanup_timer.expires = jiffies + (CLEANUPTIME * HZ); - add_timer(&cleanup_timer); - - /* Set up the proc file system */ - entry = create_proc_entry("driver/z90crypt", 0644, 0); - if (entry) { - entry->nlink = 1; - entry->data = 0; - entry->read_proc = z90crypt_status; - entry->write_proc = z90crypt_status_write; - } - else - PRINTK("Couldn't create z90crypt proc entry\n"); - z90crypt_entry = entry; - - /* Set up the configuration task. */ - init_timer(&config_timer); - config_timer.function = z90crypt_config_task; - config_timer.data = 0; - config_timer.expires = jiffies + (INITIAL_CONFIGTIME * HZ); - add_timer(&config_timer); - - /* Set up the reader task */ - tasklet_init(&reader_tasklet, z90crypt_reader_task, 0); - init_timer(&reader_timer); - reader_timer.function = z90crypt_schedule_reader_task; - reader_timer.data = 0; - reader_timer.expires = jiffies + (READERTIME * HZ / 1000); - add_timer(&reader_timer); - - return 0; // success - -init_module_cleanup: - if ((nresult = misc_deregister(&z90crypt_misc_device))) - PRINTK("misc_deregister failed with %d.\n", nresult); - else - PDEBUG("misc_deregister successful.\n"); - - return result; // failure -} - -/** - * The module termination code - */ -static void __exit -z90crypt_cleanup_module(void) -{ - int nresult; - - PDEBUG("PID %d\n", PID()); - - remove_proc_entry("driver/z90crypt", 0); - - if ((nresult = misc_deregister(&z90crypt_misc_device))) - PRINTK("misc_deregister failed with %d.\n", nresult); - else - PDEBUG("misc_deregister successful.\n"); - - /* Remove the tasks */ - tasklet_kill(&reader_tasklet); - del_timer(&reader_timer); - del_timer(&config_timer); - del_timer(&cleanup_timer); - - destroy_z90crypt(); - - PRINTKN("Unloaded.\n"); -} - -/** - * Functions running under a process id - * - * The I/O functions: - * z90crypt_open - * z90crypt_release - * z90crypt_read - * z90crypt_write - * z90crypt_unlocked_ioctl - * z90crypt_status - * z90crypt_status_write - * disable_card - * enable_card - * - * Helper functions: - * z90crypt_rsa - * z90crypt_prepare - * z90crypt_send - * z90crypt_process_results - * - */ -static int -z90crypt_open(struct inode *inode, struct file *filp) -{ - struct priv_data *private_data_p; - - if (quiesce_z90crypt) - return -EQUIESCE; - - private_data_p = kzalloc(sizeof(struct priv_data), GFP_KERNEL); - if (!private_data_p) { - PRINTK("Memory allocate failed\n"); - return -ENOMEM; - } - - private_data_p->status = STAT_OPEN; - private_data_p->opener_pid = PID(); - filp->private_data = private_data_p; - atomic_inc(&total_open); - - return 0; -} - -static int -z90crypt_release(struct inode *inode, struct file *filp) -{ - struct priv_data *private_data_p = filp->private_data; - - PDEBUG("PID %d (filp %p)\n", PID(), filp); - - private_data_p->status = STAT_CLOSED; - memset(private_data_p, 0, sizeof(struct priv_data)); - kfree(private_data_p); - atomic_dec(&total_open); - - return 0; -} - -/* - * there are two read functions, of which compile options will choose one - * without USE_GET_RANDOM_BYTES - * => read() always returns -EPERM; - * otherwise - * => read() uses get_random_bytes() kernel function - */ -#ifndef USE_GET_RANDOM_BYTES -/** - * z90crypt_read will not be supported beyond z90crypt 1.3.1 - */ -static ssize_t -z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) -{ - PDEBUG("filp %p (PID %d)\n", filp, PID()); - return -EPERM; -} -#else // we want to use get_random_bytes -/** - * read() just returns a string of random bytes. Since we have no way - * to generate these cryptographically, we just execute get_random_bytes - * for the length specified. - */ -#include <linux/random.h> -static ssize_t -z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) -{ - unsigned char *temp_buff; - - PDEBUG("filp %p (PID %d)\n", filp, PID()); - - if (quiesce_z90crypt) - return -EQUIESCE; - if (count < 0) { - PRINTK("Requested random byte count negative: %ld\n", count); - return -EINVAL; - } - if (count > RESPBUFFSIZE) { - PDEBUG("count[%d] > RESPBUFFSIZE", count); - return -EINVAL; - } - if (count == 0) - return 0; - temp_buff = kmalloc(RESPBUFFSIZE, GFP_KERNEL); - if (!temp_buff) { - PRINTK("Memory allocate failed\n"); - return -ENOMEM; - } - get_random_bytes(temp_buff, count); - - if (copy_to_user(buf, temp_buff, count) != 0) { - kfree(temp_buff); - return -EFAULT; - } - kfree(temp_buff); - return count; -} -#endif - -/** - * Write is is not allowed - */ -static ssize_t -z90crypt_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) -{ - PDEBUG("filp %p (PID %d)\n", filp, PID()); - return -EPERM; -} - -/** - * New status functions - */ -static inline int -get_status_totalcount(void) -{ - return z90crypt.hdware_info->hdware_mask.st_count; -} - -static inline int -get_status_PCICAcount(void) -{ - return z90crypt.hdware_info->type_mask[PCICA].st_count; -} - -static inline int -get_status_PCICCcount(void) -{ - return z90crypt.hdware_info->type_mask[PCICC].st_count; -} - -static inline int -get_status_PCIXCCcount(void) -{ - return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count + - z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count; -} - -static inline int -get_status_PCIXCCMCL2count(void) -{ - return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count; -} - -static inline int -get_status_PCIXCCMCL3count(void) -{ - return z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count; -} - -static inline int -get_status_CEX2Ccount(void) -{ - return z90crypt.hdware_info->type_mask[CEX2C].st_count; -} - -static inline int -get_status_CEX2Acount(void) -{ - return z90crypt.hdware_info->type_mask[CEX2A].st_count; -} - -static inline int -get_status_requestq_count(void) -{ - return requestq_count; -} - -static inline int -get_status_pendingq_count(void) -{ - return pendingq_count; -} - -static inline int -get_status_totalopen_count(void) -{ - return atomic_read(&total_open); -} - -static inline int -get_status_domain_index(void) -{ - return z90crypt.cdx; -} - -static inline unsigned char * -get_status_status_mask(unsigned char status[Z90CRYPT_NUM_APS]) -{ - int i, ix; - - memcpy(status, z90crypt.hdware_info->device_type_array, - Z90CRYPT_NUM_APS); - - for (i = 0; i < get_status_totalcount(); i++) { - ix = SHRT2LONG(i); - if (LONG2DEVPTR(ix)->user_disabled) - status[ix] = 0x0d; - } - - return status; -} - -static inline unsigned char * -get_status_qdepth_mask(unsigned char qdepth[Z90CRYPT_NUM_APS]) -{ - int i, ix; - - memset(qdepth, 0, Z90CRYPT_NUM_APS); - - for (i = 0; i < get_status_totalcount(); i++) { - ix = SHRT2LONG(i); - qdepth[ix] = LONG2DEVPTR(ix)->dev_caller_count; - } - - return qdepth; -} - -static inline unsigned int * -get_status_perdevice_reqcnt(unsigned int reqcnt[Z90CRYPT_NUM_APS]) -{ - int i, ix; - - memset(reqcnt, 0, Z90CRYPT_NUM_APS * sizeof(int)); - - for (i = 0; i < get_status_totalcount(); i++) { - ix = SHRT2LONG(i); - reqcnt[ix] = LONG2DEVPTR(ix)->dev_total_req_cnt; - } - - return reqcnt; -} - -static inline void -init_work_element(struct work_element *we_p, - struct priv_data *priv_data, pid_t pid) -{ - int step; - - we_p->requestptr = (unsigned char *)we_p + sizeof(struct work_element); - /* Come up with a unique id for this caller. */ - step = atomic_inc_return(&z90crypt_step); - memcpy(we_p->caller_id+0, (void *) &pid, sizeof(pid)); - memcpy(we_p->caller_id+4, (void *) &step, sizeof(step)); - we_p->pid = pid; - we_p->priv_data = priv_data; - we_p->status[0] = STAT_DEFAULT; - we_p->audit[0] = 0x00; - we_p->audit[1] = 0x00; - we_p->audit[2] = 0x00; - we_p->resp_buff_size = 0; - we_p->retcode = 0; - we_p->devindex = -1; - we_p->devtype = -1; - atomic_set(&we_p->alarmrung, 0); - init_waitqueue_head(&we_p->waitq); - INIT_LIST_HEAD(&(we_p->liste)); -} - -static inline int -allocate_work_element(struct work_element **we_pp, - struct priv_data *priv_data_p, pid_t pid) -{ - struct work_element *we_p; - - we_p = (struct work_element *) get_zeroed_page(GFP_KERNEL); - if (!we_p) - return -ENOMEM; - init_work_element(we_p, priv_data_p, pid); - *we_pp = we_p; - return 0; -} - -static inline void -remove_device(struct device *device_p) -{ - if (!device_p || (device_p->disabled != 0)) - return; - device_p->disabled = 1; - z90crypt.hdware_info->type_mask[device_p->dev_type].disabled_count++; - z90crypt.hdware_info->hdware_mask.disabled_count++; -} - -/** - * Bitlength limits for each card - * - * There are new MCLs which allow more bitlengths. See the table for details. - * The MCL must be applied and the newer bitlengths enabled for these to work. - * - * Card Type Old limit New limit - * PCICA ??-2048 same (the lower limit is less than 128 bit...) - * PCICC 512-1024 512-2048 - * PCIXCC_MCL2 512-2048 ----- (applying any GA LIC will make an MCL3 card) - * PCIXCC_MCL3 ----- 128-2048 - * CEX2C 512-2048 128-2048 - * CEX2A ??-2048 same (the lower limit is less than 128 bit...) - * - * ext_bitlens (extended bitlengths) is a global, since you should not apply an - * MCL to just one card in a machine. We assume, at first, that all cards have - * these capabilities. - */ -int ext_bitlens = 1; // This is global -#define PCIXCC_MIN_MOD_SIZE 16 // 128 bits -#define OLD_PCIXCC_MIN_MOD_SIZE 64 // 512 bits -#define PCICC_MIN_MOD_SIZE 64 // 512 bits -#define OLD_PCICC_MAX_MOD_SIZE 128 // 1024 bits -#define MAX_MOD_SIZE 256 // 2048 bits - -static inline int -select_device_type(int *dev_type_p, int bytelength) -{ - static int count = 0; - int PCICA_avail, PCIXCC_MCL3_avail, CEX2C_avail, CEX2A_avail, - index_to_use; - struct status *stat; - if ((*dev_type_p != PCICC) && (*dev_type_p != PCICA) && - (*dev_type_p != PCIXCC_MCL2) && (*dev_type_p != PCIXCC_MCL3) && - (*dev_type_p != CEX2C) && (*dev_type_p != CEX2A) && - (*dev_type_p != ANYDEV)) - return -1; - if (*dev_type_p != ANYDEV) { - stat = &z90crypt.hdware_info->type_mask[*dev_type_p]; - if (stat->st_count > - (stat->disabled_count + stat->user_disabled_count)) - return 0; - return -1; - } - - /** - * Assumption: PCICA, PCIXCC_MCL3, CEX2C, and CEX2A are all similar in - * speed. - * - * PCICA and CEX2A do NOT co-exist, so it would be either one or the - * other present. - */ - stat = &z90crypt.hdware_info->type_mask[PCICA]; - PCICA_avail = stat->st_count - - (stat->disabled_count + stat->user_disabled_count); - stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL3]; - PCIXCC_MCL3_avail = stat->st_count - - (stat->disabled_count + stat->user_disabled_count); - stat = &z90crypt.hdware_info->type_mask[CEX2C]; - CEX2C_avail = stat->st_count - - (stat->disabled_count + stat->user_disabled_count); - stat = &z90crypt.hdware_info->type_mask[CEX2A]; - CEX2A_avail = stat->st_count - - (stat->disabled_count + stat->user_disabled_count); - if (PCICA_avail || PCIXCC_MCL3_avail || CEX2C_avail || CEX2A_avail) { - /** - * bitlength is a factor, PCICA or CEX2A are the most capable, - * even with the new MCL for PCIXCC. - */ - if ((bytelength < PCIXCC_MIN_MOD_SIZE) || - (!ext_bitlens && (bytelength < OLD_PCIXCC_MIN_MOD_SIZE))) { - if (PCICA_avail) { - *dev_type_p = PCICA; - return 0; - } - if (CEX2A_avail) { - *dev_type_p = CEX2A; - return 0; - } - return -1; - } - - index_to_use = count % (PCICA_avail + PCIXCC_MCL3_avail + - CEX2C_avail + CEX2A_avail); - if (index_to_use < PCICA_avail) - *dev_type_p = PCICA; - else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail)) - *dev_type_p = PCIXCC_MCL3; - else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail + - CEX2C_avail)) - *dev_type_p = CEX2C; - else - *dev_type_p = CEX2A; - count++; - return 0; - } - - /* Less than OLD_PCIXCC_MIN_MOD_SIZE cannot go to a PCIXCC_MCL2 */ - if (bytelength < OLD_PCIXCC_MIN_MOD_SIZE) - return -1; - stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL2]; - if (stat->st_count > - (stat->disabled_count + stat->user_disabled_count)) { - *dev_type_p = PCIXCC_MCL2; - return 0; - } - - /** - * Less than PCICC_MIN_MOD_SIZE or more than OLD_PCICC_MAX_MOD_SIZE - * (if we don't have the MCL applied and the newer bitlengths enabled) - * cannot go to a PCICC - */ - if ((bytelength < PCICC_MIN_MOD_SIZE) || - (!ext_bitlens && (bytelength > OLD_PCICC_MAX_MOD_SIZE))) { - return -1; - } - stat = &z90crypt.hdware_info->type_mask[PCICC]; - if (stat->st_count > - (stat->disabled_count + stat->user_disabled_count)) { - *dev_type_p = PCICC; - return 0; - } - - return -1; -} - -/** - * Try the selected number, then the selected type (can be ANYDEV) - */ -static inline int -select_device(int *dev_type_p, int *device_nr_p, int bytelength) -{ - int i, indx, devTp, low_count, low_indx; - struct device_x *index_p; - struct device *dev_ptr; - - PDEBUG("device type = %d, index = %d\n", *dev_type_p, *device_nr_p); - if ((*device_nr_p >= 0) && (*device_nr_p < Z90CRYPT_NUM_DEVS)) { - PDEBUG("trying index = %d\n", *device_nr_p); - dev_ptr = z90crypt.device_p[*device_nr_p]; - - if (dev_ptr && - (dev_ptr->dev_stat != DEV_GONE) && - (dev_ptr->disabled == 0) && - (dev_ptr->user_disabled == 0)) { - PDEBUG("selected by number, index = %d\n", - *device_nr_p); - *dev_type_p = dev_ptr->dev_type; - return *device_nr_p; - } - } - *device_nr_p = -1; - PDEBUG("trying type = %d\n", *dev_type_p); - devTp = *dev_type_p; - if (select_device_type(&devTp, bytelength) == -1) { - PDEBUG("failed to select by type\n"); - return -1; - } - PDEBUG("selected type = %d\n", devTp); - index_p = &z90crypt.hdware_info->type_x_addr[devTp]; - low_count = 0x0000FFFF; - low_indx = -1; - for (i = 0; i < z90crypt.hdware_info->type_mask[devTp].st_count; i++) { - indx = index_p->device_index[i]; - dev_ptr = z90crypt.device_p[indx]; - if (dev_ptr && - (dev_ptr->dev_stat != DEV_GONE) && - (dev_ptr->disabled == 0) && - (dev_ptr->user_disabled == 0) && - (devTp == dev_ptr->dev_type) && - (low_count > dev_ptr->dev_caller_count)) { - low_count = dev_ptr->dev_caller_count; - low_indx = indx; - } - } - *device_nr_p = low_indx; - return low_indx; -} - -static inline int -send_to_crypto_device(struct work_element *we_p) -{ - struct caller *caller_p; - struct device *device_p; - int dev_nr; - int bytelen = ((struct ica_rsa_modexpo *)we_p->buffer)->inputdatalength; - - if (!we_p->requestptr) - return SEN_FATAL_ERROR; - caller_p = (struct caller *)we_p->requestptr; - dev_nr = we_p->devindex; - if (select_device(&we_p->devtype, &dev_nr, bytelen) == -1) { - if (z90crypt.hdware_info->hdware_mask.st_count != 0) - return SEN_RETRY; - else - return SEN_NOT_AVAIL; - } - we_p->devindex = dev_nr; - device_p = z90crypt.device_p[dev_nr]; - if (!device_p) - return SEN_NOT_AVAIL; - if (device_p->dev_type != we_p->devtype) - return SEN_RETRY; - if (device_p->dev_caller_count >= device_p->dev_q_depth) - return SEN_QUEUE_FULL; - PDEBUG("device number prior to send: %d\n", dev_nr); - switch (send_to_AP(dev_nr, z90crypt.cdx, - caller_p->caller_dev_dep_req_l, - caller_p->caller_dev_dep_req_p)) { - case DEV_SEN_EXCEPTION: - PRINTKC("Exception during send to device %d\n", dev_nr); - z90crypt.terminating = 1; - return SEN_FATAL_ERROR; - case DEV_GONE: - PRINTK("Device %d not available\n", dev_nr); - remove_device(device_p); - return SEN_NOT_AVAIL; - case DEV_EMPTY: - return SEN_NOT_AVAIL; - case DEV_NO_WORK: - return SEN_FATAL_ERROR; - case DEV_BAD_MESSAGE: - return SEN_USER_ERROR; - case DEV_QUEUE_FULL: - return SEN_QUEUE_FULL; - default: - case DEV_ONLINE: - break; - } - list_add_tail(&(caller_p->caller_liste), &(device_p->dev_caller_list)); - device_p->dev_caller_count++; - return 0; -} - -/** - * Send puts the user's work on one of two queues: - * the pending queue if the send was successful - * the request queue if the send failed because device full or busy - */ -static inline int -z90crypt_send(struct work_element *we_p, const char *buf) -{ - int rv; - - PDEBUG("PID %d\n", PID()); - - if (CHK_RDWRMASK(we_p->status[0]) != STAT_NOWORK) { - PDEBUG("PID %d tried to send more work but has outstanding " - "work.\n", PID()); - return -EWORKPEND; - } - we_p->devindex = -1; // Reset device number - spin_lock_irq(&queuespinlock); - rv = send_to_crypto_device(we_p); - switch (rv) { - case 0: - we_p->requestsent = jiffies; - we_p->audit[0] |= FP_SENT; - list_add_tail(&we_p->liste, &pending_list); - ++pendingq_count; - we_p->audit[0] |= FP_PENDING; - break; - case SEN_BUSY: - case SEN_QUEUE_FULL: - rv = 0; - we_p->devindex = -1; // any device will do - we_p->requestsent = jiffies; - list_add_tail(&we_p->liste, &request_list); - ++requestq_count; - we_p->audit[0] |= FP_REQUEST; - break; - case SEN_RETRY: - rv = -ERESTARTSYS; - break; - case SEN_NOT_AVAIL: - PRINTK("*** No devices available.\n"); - rv = we_p->retcode = -ENODEV; - we_p->status[0] |= STAT_FAILED; - break; - case REC_OPERAND_INV: - case REC_OPERAND_SIZE: - case REC_EVEN_MOD: - case REC_INVALID_PAD: - rv = we_p->retcode = -EINVAL; - we_p->status[0] |= STAT_FAILED; - break; - default: - we_p->retcode = rv; - we_p->status[0] |= STAT_FAILED; - break; - } - if (rv != -ERESTARTSYS) - SET_RDWRMASK(we_p->status[0], STAT_WRITTEN); - spin_unlock_irq(&queuespinlock); - if (rv == 0) - tasklet_schedule(&reader_tasklet); - return rv; -} - -/** - * process_results copies the user's work from kernel space. - */ -static inline int -z90crypt_process_results(struct work_element *we_p, char __user *buf) -{ - int rv; - - PDEBUG("we_p %p (PID %d)\n", we_p, PID()); - - LONG2DEVPTR(we_p->devindex)->dev_total_req_cnt++; - SET_RDWRMASK(we_p->status[0], STAT_READPEND); - - rv = 0; - if (!we_p->buffer) { - PRINTK("we_p %p PID %d in STAT_READPEND: buffer NULL.\n", - we_p, PID()); - rv = -ENOBUFF; - } - - if (!rv) - if ((rv = copy_to_user(buf, we_p->buffer, we_p->buff_size))) { - PDEBUG("copy_to_user failed: rv = %d\n", rv); - rv = -EFAULT; - } - - if (!rv) - rv = we_p->retcode; - if (!rv) - if (we_p->resp_buff_size - && copy_to_user(we_p->resp_addr, we_p->resp_buff, - we_p->resp_buff_size)) - rv = -EFAULT; - - SET_RDWRMASK(we_p->status[0], STAT_NOWORK); - return rv; -} - -static unsigned char NULL_psmid[8] = -{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -/** - * Used in device configuration functions - */ -#define MAX_RESET 90 - -/** - * This is used only for PCICC support - */ -static inline int -is_PKCS11_padded(unsigned char *buffer, int length) -{ - int i; - if ((buffer[0] != 0x00) || (buffer[1] != 0x01)) - return 0; - for (i = 2; i < length; i++) - if (buffer[i] != 0xFF) - break; - if ((i < 10) || (i == length)) - return 0; - if (buffer[i] != 0x00) - return 0; - return 1; -} - -/** - * This is used only for PCICC support - */ -static inline int -is_PKCS12_padded(unsigned char *buffer, int length) -{ - int i; - if ((buffer[0] != 0x00) || (buffer[1] != 0x02)) - return 0; - for (i = 2; i < length; i++) - if (buffer[i] == 0x00) - break; - if ((i < 10) || (i == length)) - return 0; - if (buffer[i] != 0x00) - return 0; - return 1; -} - -/** - * builds struct caller and converts message from generic format to - * device-dependent format - * func is ICARSAMODEXPO or ICARSACRT - * function is PCI_FUNC_KEY_ENCRYPT or PCI_FUNC_KEY_DECRYPT - */ -static inline int -build_caller(struct work_element *we_p, short function) -{ - int rv; - struct caller *caller_p = (struct caller *)we_p->requestptr; - - if ((we_p->devtype != PCICC) && (we_p->devtype != PCICA) && - (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) && - (we_p->devtype != CEX2C) && (we_p->devtype != CEX2A)) - return SEN_NOT_AVAIL; - - memcpy(caller_p->caller_id, we_p->caller_id, - sizeof(caller_p->caller_id)); - caller_p->caller_dev_dep_req_p = caller_p->caller_dev_dep_req; - caller_p->caller_dev_dep_req_l = MAX_RESPONSE_SIZE; - caller_p->caller_buf_p = we_p->buffer; - INIT_LIST_HEAD(&(caller_p->caller_liste)); - - rv = convert_request(we_p->buffer, we_p->funccode, function, - z90crypt.cdx, we_p->devtype, - &caller_p->caller_dev_dep_req_l, - caller_p->caller_dev_dep_req_p); - if (rv) { - if (rv == SEN_NOT_AVAIL) - PDEBUG("request can't be processed on hdwr avail\n"); - else - PRINTK("Error from convert_request: %d\n", rv); - } - else - memcpy(&(caller_p->caller_dev_dep_req_p[4]), we_p->caller_id,8); - return rv; -} - -static inline void -unbuild_caller(struct device *device_p, struct caller *caller_p) -{ - if (!caller_p) - return; - if (caller_p->caller_liste.next && caller_p->caller_liste.prev) - if (!list_empty(&caller_p->caller_liste)) { - list_del_init(&caller_p->caller_liste); - device_p->dev_caller_count--; - } - memset(caller_p->caller_id, 0, sizeof(caller_p->caller_id)); -} - -static inline int -get_crypto_request_buffer(struct work_element *we_p) -{ - struct ica_rsa_modexpo *mex_p; - struct ica_rsa_modexpo_crt *crt_p; - unsigned char *temp_buffer; - short function; - int rv; - - mex_p = (struct ica_rsa_modexpo *) we_p->buffer; - crt_p = (struct ica_rsa_modexpo_crt *) we_p->buffer; - - PDEBUG("device type input = %d\n", we_p->devtype); - - if (z90crypt.terminating) - return REC_NO_RESPONSE; - if (memcmp(we_p->caller_id, NULL_psmid, 8) == 0) { - PRINTK("psmid zeroes\n"); - return SEN_FATAL_ERROR; - } - if (!we_p->buffer) { - PRINTK("buffer pointer NULL\n"); - return SEN_USER_ERROR; - } - if (!we_p->requestptr) { - PRINTK("caller pointer NULL\n"); - return SEN_USER_ERROR; - } - - if ((we_p->devtype != PCICA) && (we_p->devtype != PCICC) && - (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) && - (we_p->devtype != CEX2C) && (we_p->devtype != CEX2A) && - (we_p->devtype != ANYDEV)) { - PRINTK("invalid device type\n"); - return SEN_USER_ERROR; - } - - if ((mex_p->inputdatalength < 1) || - (mex_p->inputdatalength > MAX_MOD_SIZE)) { - PRINTK("inputdatalength[%d] is not valid\n", - mex_p->inputdatalength); - return SEN_USER_ERROR; - } - - if (mex_p->outputdatalength < mex_p->inputdatalength) { - PRINTK("outputdatalength[%d] < inputdatalength[%d]\n", - mex_p->outputdatalength, mex_p->inputdatalength); - return SEN_USER_ERROR; - } - - if (!mex_p->inputdata || !mex_p->outputdata) { - PRINTK("inputdata[%p] or outputdata[%p] is NULL\n", - mex_p->outputdata, mex_p->inputdata); - return SEN_USER_ERROR; - } - - /** - * As long as outputdatalength is big enough, we can set the - * outputdatalength equal to the inputdatalength, since that is the - * number of bytes we will copy in any case - */ - mex_p->outputdatalength = mex_p->inputdatalength; - - rv = 0; - switch (we_p->funccode) { - case ICARSAMODEXPO: - if (!mex_p->b_key || !mex_p->n_modulus) - rv = SEN_USER_ERROR; - break; - case ICARSACRT: - if (!IS_EVEN(crt_p->inputdatalength)) { - PRINTK("inputdatalength[%d] is odd, CRT form\n", - crt_p->inputdatalength); - rv = SEN_USER_ERROR; - break; - } - if (!crt_p->bp_key || - !crt_p->bq_key || - !crt_p->np_prime || - !crt_p->nq_prime || - !crt_p->u_mult_inv) { - PRINTK("CRT form, bad data: %p/%p/%p/%p/%p\n", - crt_p->bp_key, crt_p->bq_key, - crt_p->np_prime, crt_p->nq_prime, - crt_p->u_mult_inv); - rv = SEN_USER_ERROR; - } - break; - default: - PRINTK("bad func = %d\n", we_p->funccode); - rv = SEN_USER_ERROR; - break; - } - if (rv != 0) - return rv; - - if (select_device_type(&we_p->devtype, mex_p->inputdatalength) < 0) - return SEN_NOT_AVAIL; - - temp_buffer = (unsigned char *)we_p + sizeof(struct work_element) + - sizeof(struct caller); - if (copy_from_user(temp_buffer, mex_p->inputdata, - mex_p->inputdatalength) != 0) - return SEN_RELEASED; - - function = PCI_FUNC_KEY_ENCRYPT; - switch (we_p->devtype) { - /* PCICA and CEX2A do everything with a simple RSA mod-expo operation */ - case PCICA: - case CEX2A: - function = PCI_FUNC_KEY_ENCRYPT; - break; - /** - * PCIXCC_MCL2 does all Mod-Expo form with a simple RSA mod-expo - * operation, and all CRT forms with a PKCS-1.2 format decrypt. - * PCIXCC_MCL3 and CEX2C do all Mod-Expo and CRT forms with a simple RSA - * mod-expo operation - */ - case PCIXCC_MCL2: - if (we_p->funccode == ICARSAMODEXPO) - function = PCI_FUNC_KEY_ENCRYPT; - else - function = PCI_FUNC_KEY_DECRYPT; - break; - case PCIXCC_MCL3: - case CEX2C: - if (we_p->funccode == ICARSAMODEXPO) - function = PCI_FUNC_KEY_ENCRYPT; - else - function = PCI_FUNC_KEY_DECRYPT; - break; - /** - * PCICC does everything as a PKCS-1.2 format request - */ - case PCICC: - /* PCICC cannot handle input that is is PKCS#1.1 padded */ - if (is_PKCS11_padded(temp_buffer, mex_p->inputdatalength)) { - return SEN_NOT_AVAIL; - } - if (we_p->funccode == ICARSAMODEXPO) { - if (is_PKCS12_padded(temp_buffer, - mex_p->inputdatalength)) - function = PCI_FUNC_KEY_ENCRYPT; - else - function = PCI_FUNC_KEY_DECRYPT; - } else - /* all CRT forms are decrypts */ - function = PCI_FUNC_KEY_DECRYPT; - break; - } - PDEBUG("function: %04x\n", function); - rv = build_caller(we_p, function); - PDEBUG("rv from build_caller = %d\n", rv); - return rv; -} - -static inline int -z90crypt_prepare(struct work_element *we_p, unsigned int funccode, - const char __user *buffer) -{ - int rv; - - we_p->devindex = -1; - if (funccode == ICARSAMODEXPO) - we_p->buff_size = sizeof(struct ica_rsa_modexpo); - else - we_p->buff_size = sizeof(struct ica_rsa_modexpo_crt); - - if (copy_from_user(we_p->buffer, buffer, we_p->buff_size)) - return -EFAULT; - - we_p->audit[0] |= FP_COPYFROM; - SET_RDWRMASK(we_p->status[0], STAT_WRITTEN); - we_p->funccode = funccode; - we_p->devtype = -1; - we_p->audit[0] |= FP_BUFFREQ; - rv = get_crypto_request_buffer(we_p); - switch (rv) { - case 0: - we_p->audit[0] |= FP_BUFFGOT; - break; - case SEN_USER_ERROR: - rv = -EINVAL; - break; - case SEN_QUEUE_FULL: - rv = 0; - break; - case SEN_RELEASED: - rv = -EFAULT; - break; - case REC_NO_RESPONSE: - rv = -ENODEV; - break; - case SEN_NOT_AVAIL: - case EGETBUFF: - rv = -EGETBUFF; - break; - default: - PRINTK("rv = %d\n", rv); - rv = -EGETBUFF; - break; - } - if (CHK_RDWRMASK(we_p->status[0]) == STAT_WRITTEN) - SET_RDWRMASK(we_p->status[0], STAT_DEFAULT); - return rv; -} - -static inline void -purge_work_element(struct work_element *we_p) -{ - struct list_head *lptr; - - spin_lock_irq(&queuespinlock); - list_for_each(lptr, &request_list) { - if (lptr == &we_p->liste) { - list_del_init(lptr); - requestq_count--; - break; - } - } - list_for_each(lptr, &pending_list) { - if (lptr == &we_p->liste) { - list_del_init(lptr); - pendingq_count--; - break; - } - } - spin_unlock_irq(&queuespinlock); -} - -/** - * Build the request and send it. - */ -static inline int -z90crypt_rsa(struct priv_data *private_data_p, pid_t pid, - unsigned int cmd, unsigned long arg) -{ - struct work_element *we_p; - int rv; - - if ((rv = allocate_work_element(&we_p, private_data_p, pid))) { - PDEBUG("PID %d: allocate_work_element returned ENOMEM\n", pid); - return rv; - } - if ((rv = z90crypt_prepare(we_p, cmd, (const char __user *)arg))) - PDEBUG("PID %d: rv = %d from z90crypt_prepare\n", pid, rv); - if (!rv) - if ((rv = z90crypt_send(we_p, (const char *)arg))) - PDEBUG("PID %d: rv %d from z90crypt_send.\n", pid, rv); - if (!rv) { - we_p->audit[0] |= FP_ASLEEP; - wait_event(we_p->waitq, atomic_read(&we_p->alarmrung)); - we_p->audit[0] |= FP_AWAKE; - rv = we_p->retcode; - } - if (!rv) - rv = z90crypt_process_results(we_p, (char __user *)arg); - - if ((we_p->status[0] & STAT_FAILED)) { - switch (rv) { - /** - * EINVAL *after* receive is almost always a padding error or - * length error issued by a coprocessor (not an accelerator). - * We convert this return value to -EGETBUFF which should - * trigger a fallback to software. - */ - case -EINVAL: - if ((we_p->devtype != PCICA) && - (we_p->devtype != CEX2A)) - rv = -EGETBUFF; - break; - case -ETIMEOUT: - if (z90crypt.mask.st_count > 0) - rv = -ERESTARTSYS; // retry with another - else - rv = -ENODEV; // no cards left - /* fall through to clean up request queue */ - case -ERESTARTSYS: - case -ERELEASED: - switch (CHK_RDWRMASK(we_p->status[0])) { - case STAT_WRITTEN: - purge_work_element(we_p); - break; - case STAT_READPEND: - case STAT_NOWORK: - default: - break; - } - break; - default: - we_p->status[0] ^= STAT_FAILED; - break; - } - } - free_page((long)we_p); - return rv; -} - -/** - * This function is a little long, but it's really just one large switch - * statement. - */ -static long -z90crypt_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct priv_data *private_data_p = filp->private_data; - unsigned char *status; - unsigned char *qdepth; - unsigned int *reqcnt; - struct ica_z90_status *pstat; - int ret, i, loopLim, tempstat; - static int deprecated_msg_count1 = 0; - static int deprecated_msg_count2 = 0; - - PDEBUG("filp %p (PID %d), cmd 0x%08X\n", filp, PID(), cmd); - PDEBUG("cmd 0x%08X: dir %s, size 0x%04X, type 0x%02X, nr 0x%02X\n", - cmd, - !_IOC_DIR(cmd) ? "NO" - : ((_IOC_DIR(cmd) == (_IOC_READ|_IOC_WRITE)) ? "RW" - : ((_IOC_DIR(cmd) == _IOC_READ) ? "RD" - : "WR")), - _IOC_SIZE(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd)); - - if (_IOC_TYPE(cmd) != Z90_IOCTL_MAGIC) { - PRINTK("cmd 0x%08X contains bad magic\n", cmd); - return -ENOTTY; - } - - ret = 0; - switch (cmd) { - case ICARSAMODEXPO: - case ICARSACRT: - if (quiesce_z90crypt) { - ret = -EQUIESCE; - break; - } - ret = -ENODEV; // Default if no devices - loopLim = z90crypt.hdware_info->hdware_mask.st_count - - (z90crypt.hdware_info->hdware_mask.disabled_count + - z90crypt.hdware_info->hdware_mask.user_disabled_count); - for (i = 0; i < loopLim; i++) { - ret = z90crypt_rsa(private_data_p, PID(), cmd, arg); - if (ret != -ERESTARTSYS) - break; - } - if (ret == -ERESTARTSYS) - ret = -ENODEV; - break; - - case Z90STAT_TOTALCOUNT: - tempstat = get_status_totalcount(); - if (copy_to_user((int __user *)arg, &tempstat,sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_PCICACOUNT: - tempstat = get_status_PCICAcount(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_PCICCCOUNT: - tempstat = get_status_PCICCcount(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_PCIXCCMCL2COUNT: - tempstat = get_status_PCIXCCMCL2count(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_PCIXCCMCL3COUNT: - tempstat = get_status_PCIXCCMCL3count(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_CEX2CCOUNT: - tempstat = get_status_CEX2Ccount(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_CEX2ACOUNT: - tempstat = get_status_CEX2Acount(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_REQUESTQ_COUNT: - tempstat = get_status_requestq_count(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_PENDINGQ_COUNT: - tempstat = get_status_pendingq_count(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_TOTALOPEN_COUNT: - tempstat = get_status_totalopen_count(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_DOMAIN_INDEX: - tempstat = get_status_domain_index(); - if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90STAT_STATUS_MASK: - status = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL); - if (!status) { - PRINTK("kmalloc for status failed!\n"); - ret = -ENOMEM; - break; - } - get_status_status_mask(status); - if (copy_to_user((char __user *) arg, status, Z90CRYPT_NUM_APS) - != 0) - ret = -EFAULT; - kfree(status); - break; - - case Z90STAT_QDEPTH_MASK: - qdepth = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL); - if (!qdepth) { - PRINTK("kmalloc for qdepth failed!\n"); - ret = -ENOMEM; - break; - } - get_status_qdepth_mask(qdepth); - if (copy_to_user((char __user *) arg, qdepth, Z90CRYPT_NUM_APS) != 0) - ret = -EFAULT; - kfree(qdepth); - break; - - case Z90STAT_PERDEV_REQCNT: - reqcnt = kmalloc(sizeof(int) * Z90CRYPT_NUM_APS, GFP_KERNEL); - if (!reqcnt) { - PRINTK("kmalloc for reqcnt failed!\n"); - ret = -ENOMEM; - break; - } - get_status_perdevice_reqcnt(reqcnt); - if (copy_to_user((char __user *) arg, reqcnt, - Z90CRYPT_NUM_APS * sizeof(int)) != 0) - ret = -EFAULT; - kfree(reqcnt); - break; - - /* THIS IS DEPRECATED. USE THE NEW STATUS CALLS */ - case ICAZ90STATUS: - if (deprecated_msg_count1 < 20) { - PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n"); - deprecated_msg_count1++; - if (deprecated_msg_count1 == 20) - PRINTK("No longer issuing messages related to " - "deprecated call to ICAZ90STATUS.\n"); - } - - pstat = kmalloc(sizeof(struct ica_z90_status), GFP_KERNEL); - if (!pstat) { - PRINTK("kmalloc for pstat failed!\n"); - ret = -ENOMEM; - break; - } - - pstat->totalcount = get_status_totalcount(); - pstat->leedslitecount = get_status_PCICAcount(); - pstat->leeds2count = get_status_PCICCcount(); - pstat->requestqWaitCount = get_status_requestq_count(); - pstat->pendingqWaitCount = get_status_pendingq_count(); - pstat->totalOpenCount = get_status_totalopen_count(); - pstat->cryptoDomain = get_status_domain_index(); - get_status_status_mask(pstat->status); - get_status_qdepth_mask(pstat->qdepth); - - if (copy_to_user((struct ica_z90_status __user *) arg, pstat, - sizeof(struct ica_z90_status)) != 0) - ret = -EFAULT; - kfree(pstat); - break; - - /* THIS IS DEPRECATED. USE THE NEW STATUS CALLS */ - case Z90STAT_PCIXCCCOUNT: - if (deprecated_msg_count2 < 20) { - PRINTK("deprecated ioctl (Z90STAT_PCIXCCCOUNT)!\n"); - deprecated_msg_count2++; - if (deprecated_msg_count2 == 20) - PRINTK("No longer issuing messages about depre" - "cated ioctl Z90STAT_PCIXCCCOUNT.\n"); - } - - tempstat = get_status_PCIXCCcount(); - if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0) - ret = -EFAULT; - break; - - case Z90QUIESCE: - if (current->euid != 0) { - PRINTK("QUIESCE fails: euid %d\n", - current->euid); - ret = -EACCES; - } else { - PRINTK("QUIESCE device from PID %d\n", PID()); - quiesce_z90crypt = 1; - } - break; - - default: - /* user passed an invalid IOCTL number */ - PDEBUG("cmd 0x%08X contains invalid ioctl code\n", cmd); - ret = -ENOTTY; - break; - } - - return ret; -} - -static inline int -sprintcl(unsigned char *outaddr, unsigned char *addr, unsigned int len) -{ - int hl, i; - - hl = 0; - for (i = 0; i < len; i++) - hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]); - hl += sprintf(outaddr+hl, " "); - - return hl; -} - -static inline int -sprintrw(unsigned char *outaddr, unsigned char *addr, unsigned int len) -{ - int hl, inl, c, cx; - - hl = sprintf(outaddr, " "); - inl = 0; - for (c = 0; c < (len / 16); c++) { - hl += sprintcl(outaddr+hl, addr+inl, 16); - inl += 16; - } - - cx = len%16; - if (cx) { - hl += sprintcl(outaddr+hl, addr+inl, cx); - inl += cx; - } - - hl += sprintf(outaddr+hl, "\n"); - - return hl; -} - -static inline int -sprinthx(unsigned char *title, unsigned char *outaddr, - unsigned char *addr, unsigned int len) -{ - int hl, inl, r, rx; - - hl = sprintf(outaddr, "\n%s\n", title); - inl = 0; - for (r = 0; r < (len / 64); r++) { - hl += sprintrw(outaddr+hl, addr+inl, 64); - inl += 64; - } - rx = len % 64; - if (rx) { - hl += sprintrw(outaddr+hl, addr+inl, rx); - inl += rx; - } - - hl += sprintf(outaddr+hl, "\n"); - - return hl; -} - -static inline int -sprinthx4(unsigned char *title, unsigned char *outaddr, - unsigned int *array, unsigned int len) -{ - int hl, r; - - hl = sprintf(outaddr, "\n%s\n", title); - - for (r = 0; r < len; r++) { - if ((r % 8) == 0) - hl += sprintf(outaddr+hl, " "); - hl += sprintf(outaddr+hl, "%08X ", array[r]); - if ((r % 8) == 7) - hl += sprintf(outaddr+hl, "\n"); - } - - hl += sprintf(outaddr+hl, "\n"); - - return hl; -} - -static int -z90crypt_status(char *resp_buff, char **start, off_t offset, - int count, int *eof, void *data) -{ - unsigned char *workarea; - int len; - - /* resp_buff is a page. Use the right half for a work area */ - workarea = resp_buff+2000; - len = 0; - len += sprintf(resp_buff+len, "\nz90crypt version: %d.%d.%d\n", - z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT); - len += sprintf(resp_buff+len, "Cryptographic domain: %d\n", - get_status_domain_index()); - len += sprintf(resp_buff+len, "Total device count: %d\n", - get_status_totalcount()); - len += sprintf(resp_buff+len, "PCICA count: %d\n", - get_status_PCICAcount()); - len += sprintf(resp_buff+len, "PCICC count: %d\n", - get_status_PCICCcount()); - len += sprintf(resp_buff+len, "PCIXCC MCL2 count: %d\n", - get_status_PCIXCCMCL2count()); - len += sprintf(resp_buff+len, "PCIXCC MCL3 count: %d\n", - get_status_PCIXCCMCL3count()); - len += sprintf(resp_buff+len, "CEX2C count: %d\n", - get_status_CEX2Ccount()); - len += sprintf(resp_buff+len, "CEX2A count: %d\n", - get_status_CEX2Acount()); - len += sprintf(resp_buff+len, "requestq count: %d\n", - get_status_requestq_count()); - len += sprintf(resp_buff+len, "pendingq count: %d\n", - get_status_pendingq_count()); - len += sprintf(resp_buff+len, "Total open handles: %d\n\n", - get_status_totalopen_count()); - len += sprinthx( - "Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) " - "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A", - resp_buff+len, - get_status_status_mask(workarea), - Z90CRYPT_NUM_APS); - len += sprinthx("Waiting work element counts", - resp_buff+len, - get_status_qdepth_mask(workarea), - Z90CRYPT_NUM_APS); - len += sprinthx4( - "Per-device successfully completed request counts", - resp_buff+len, - get_status_perdevice_reqcnt((unsigned int *)workarea), - Z90CRYPT_NUM_APS); - *eof = 1; - memset(workarea, 0, Z90CRYPT_NUM_APS * sizeof(unsigned int)); - return len; -} - -static inline void -disable_card(int card_index) -{ - struct device *devp; - - devp = LONG2DEVPTR(card_index); - if (!devp || devp->user_disabled) - return; - devp->user_disabled = 1; - z90crypt.hdware_info->hdware_mask.user_disabled_count++; - if (devp->dev_type == -1) - return; - z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count++; -} - -static inline void -enable_card(int card_index) -{ - struct device *devp; - - devp = LONG2DEVPTR(card_index); - if (!devp || !devp->user_disabled) - return; - devp->user_disabled = 0; - z90crypt.hdware_info->hdware_mask.user_disabled_count--; - if (devp->dev_type == -1) - return; - z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count--; -} - -static int -z90crypt_status_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - int j, eol; - unsigned char *lbuf, *ptr; - unsigned int local_count; - -#define LBUFSIZE 1200 - lbuf = kmalloc(LBUFSIZE, GFP_KERNEL); - if (!lbuf) { - PRINTK("kmalloc failed!\n"); - return 0; - } - - if (count <= 0) - return 0; - - local_count = UMIN((unsigned int)count, LBUFSIZE-1); - - if (copy_from_user(lbuf, buffer, local_count) != 0) { - kfree(lbuf); - return -EFAULT; - } - - lbuf[local_count] = '\0'; - - ptr = strstr(lbuf, "Online devices"); - if (ptr == 0) { - PRINTK("Unable to parse data (missing \"Online devices\")\n"); - kfree(lbuf); - return count; - } - - ptr = strstr(ptr, "\n"); - if (ptr == 0) { - PRINTK("Unable to parse data (missing newline after \"Online devices\")\n"); - kfree(lbuf); - return count; - } - ptr++; - - if (strstr(ptr, "Waiting work element counts") == NULL) { - PRINTK("Unable to parse data (missing \"Waiting work element counts\")\n"); - kfree(lbuf); - return count; - } - - j = 0; - eol = 0; - while ((j < 64) && (*ptr != '\0')) { - switch (*ptr) { - case '\t': - case ' ': - break; - case '\n': - default: - eol = 1; - break; - case '0': // no device - case '1': // PCICA - case '2': // PCICC - case '3': // PCIXCC_MCL2 - case '4': // PCIXCC_MCL3 - case '5': // CEX2C - case '6': // CEX2A - j++; - break; - case 'd': - case 'D': - disable_card(j); - j++; - break; - case 'e': - case 'E': - enable_card(j); - j++; - break; - } - if (eol) - break; - ptr++; - } - - kfree(lbuf); - return count; -} - -/** - * Functions that run under a timer, with no process id - * - * The task functions: - * z90crypt_reader_task - * helper_send_work - * helper_handle_work_element - * helper_receive_rc - * z90crypt_config_task - * z90crypt_cleanup_task - * - * Helper functions: - * z90crypt_schedule_reader_timer - * z90crypt_schedule_reader_task - * z90crypt_schedule_config_task - * z90crypt_schedule_cleanup_task - */ -static inline int -receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p, - unsigned char *buff, unsigned char __user **dest_p_p) -{ - int dv, rv; - struct device *dev_ptr; - struct caller *caller_p; - struct ica_rsa_modexpo *icaMsg_p; - struct list_head *ptr, *tptr; - - memcpy(psmid, NULL_psmid, sizeof(NULL_psmid)); - - if (z90crypt.terminating) - return REC_FATAL_ERROR; - - caller_p = 0; - dev_ptr = z90crypt.device_p[index]; - rv = 0; - do { - if (!dev_ptr || dev_ptr->disabled) { - rv = REC_NO_WORK; // a disabled device can't return work - break; - } - if (dev_ptr->dev_self_x != index) { - PRINTKC("Corrupt dev ptr\n"); - z90crypt.terminating = 1; - rv = REC_FATAL_ERROR; - break; - } - if (!dev_ptr->dev_resp_l || !dev_ptr->dev_resp_p) { - dv = DEV_REC_EXCEPTION; - PRINTK("dev_resp_l = %d, dev_resp_p = %p\n", - dev_ptr->dev_resp_l, dev_ptr->dev_resp_p); - } else { - PDEBUG("Dequeue called for device %d\n", index); - dv = receive_from_AP(index, z90crypt.cdx, - dev_ptr->dev_resp_l, - dev_ptr->dev_resp_p, psmid); - } - switch (dv) { - case DEV_REC_EXCEPTION: - rv = REC_FATAL_ERROR; - z90crypt.terminating = 1; - PRINTKC("Exception in receive from device %d\n", - index); - break; - case DEV_ONLINE: - rv = 0; - break; - case DEV_EMPTY: - rv = REC_EMPTY; - break; - case DEV_NO_WORK: - rv = REC_NO_WORK; - break; - case DEV_BAD_MESSAGE: - case DEV_GONE: - case REC_HARDWAR_ERR: - default: - rv = REC_NO_RESPONSE; - break; - } - if (rv) - break; - if (dev_ptr->dev_caller_count <= 0) { - rv = REC_USER_GONE; - break; - } - - list_for_each_safe(ptr, tptr, &dev_ptr->dev_caller_list) { - caller_p = list_entry(ptr, struct caller, caller_liste); - if (!memcmp(caller_p->caller_id, psmid, - sizeof(caller_p->caller_id))) { - if (!list_empty(&caller_p->caller_liste)) { - list_del_init(ptr); - dev_ptr->dev_caller_count--; - break; - } - } - caller_p = 0; - } - if (!caller_p) { - PRINTKW("Unable to locate PSMID %02X%02X%02X%02X%02X" - "%02X%02X%02X in device list\n", - psmid[0], psmid[1], psmid[2], psmid[3], - psmid[4], psmid[5], psmid[6], psmid[7]); - rv = REC_USER_GONE; - break; - } - - PDEBUG("caller_p after successful receive: %p\n", caller_p); - rv = convert_response(dev_ptr->dev_resp_p, - caller_p->caller_buf_p, buff_len_p, buff); - switch (rv) { - case REC_USE_PCICA: - break; - case REC_OPERAND_INV: - case REC_OPERAND_SIZE: - case REC_EVEN_MOD: - case REC_INVALID_PAD: - PDEBUG("device %d: 'user error' %d\n", index, rv); - break; - case WRONG_DEVICE_TYPE: - case REC_HARDWAR_ERR: - case REC_BAD_MESSAGE: - PRINTKW("device %d: hardware error %d\n", index, rv); - rv = REC_NO_RESPONSE; - break; - default: - PDEBUG("device %d: rv = %d\n", index, rv); - break; - } - } while (0); - - switch (rv) { - case 0: - PDEBUG("Successful receive from device %d\n", index); - icaMsg_p = (struct ica_rsa_modexpo *)caller_p->caller_buf_p; - *dest_p_p = icaMsg_p->outputdata; - if (*buff_len_p == 0) - PRINTK("Zero *buff_len_p\n"); - break; - case REC_NO_RESPONSE: - PRINTKW("Removing device %d from availability\n", index); - remove_device(dev_ptr); - break; - } - - if (caller_p) - unbuild_caller(dev_ptr, caller_p); - - return rv; -} - -static inline void -helper_send_work(int index) -{ - struct work_element *rq_p; - int rv; - - if (list_empty(&request_list)) - return; - requestq_count--; - rq_p = list_entry(request_list.next, struct work_element, liste); - list_del_init(&rq_p->liste); - rq_p->audit[1] |= FP_REMREQUEST; - if (rq_p->devtype == SHRT2DEVPTR(index)->dev_type) { - rq_p->devindex = SHRT2LONG(index); - rv = send_to_crypto_device(rq_p); - if (rv == 0) { - rq_p->requestsent = jiffies; - rq_p->audit[0] |= FP_SENT; - list_add_tail(&rq_p->liste, &pending_list); - ++pendingq_count; - rq_p->audit[0] |= FP_PENDING; - } else { - switch (rv) { - case REC_OPERAND_INV: - case REC_OPERAND_SIZE: - case REC_EVEN_MOD: - case REC_INVALID_PAD: - rq_p->retcode = -EINVAL; - break; - case SEN_NOT_AVAIL: - case SEN_RETRY: - case REC_NO_RESPONSE: - default: - if (z90crypt.mask.st_count > 1) - rq_p->retcode = - -ERESTARTSYS; - else - rq_p->retcode = -ENODEV; - break; - } - rq_p->status[0] |= STAT_FAILED; - rq_p->audit[1] |= FP_AWAKENING; - atomic_set(&rq_p->alarmrung, 1); - wake_up(&rq_p->waitq); - } - } else { - if (z90crypt.mask.st_count > 1) - rq_p->retcode = -ERESTARTSYS; - else - rq_p->retcode = -ENODEV; - rq_p->status[0] |= STAT_FAILED; - rq_p->audit[1] |= FP_AWAKENING; - atomic_set(&rq_p->alarmrung, 1); - wake_up(&rq_p->waitq); - } -} - -static inline void -helper_handle_work_element(int index, unsigned char psmid[8], int rc, - int buff_len, unsigned char *buff, - unsigned char __user *resp_addr) -{ - struct work_element *pq_p; - struct list_head *lptr, *tptr; - - pq_p = 0; - list_for_each_safe(lptr, tptr, &pending_list) { - pq_p = list_entry(lptr, struct work_element, liste); - if (!memcmp(pq_p->caller_id, psmid, sizeof(pq_p->caller_id))) { - list_del_init(lptr); - pendingq_count--; - pq_p->audit[1] |= FP_NOTPENDING; - break; - } - pq_p = 0; - } - - if (!pq_p) { - PRINTK("device %d has work but no caller exists on pending Q\n", - SHRT2LONG(index)); - return; - } - - switch (rc) { - case 0: - pq_p->resp_buff_size = buff_len; - pq_p->audit[1] |= FP_RESPSIZESET; - if (buff_len) { - pq_p->resp_addr = resp_addr; - pq_p->audit[1] |= FP_RESPADDRCOPIED; - memcpy(pq_p->resp_buff, buff, buff_len); - pq_p->audit[1] |= FP_RESPBUFFCOPIED; - } - break; - case REC_OPERAND_INV: - case REC_OPERAND_SIZE: - case REC_EVEN_MOD: - case REC_INVALID_PAD: - PDEBUG("-EINVAL after application error %d\n", rc); - pq_p->retcode = -EINVAL; - pq_p->status[0] |= STAT_FAILED; - break; - case REC_USE_PCICA: - pq_p->retcode = -ERESTARTSYS; - pq_p->status[0] |= STAT_FAILED; - break; - case REC_NO_RESPONSE: - default: - if (z90crypt.mask.st_count > 1) - pq_p->retcode = -ERESTARTSYS; - else - pq_p->retcode = -ENODEV; - pq_p->status[0] |= STAT_FAILED; - break; - } - if ((pq_p->status[0] != STAT_FAILED) || (pq_p->retcode != -ERELEASED)) { - pq_p->audit[1] |= FP_AWAKENING; - atomic_set(&pq_p->alarmrung, 1); - wake_up(&pq_p->waitq); - } -} - -/** - * return TRUE if the work element should be removed from the queue - */ -static inline int -helper_receive_rc(int index, int *rc_p) -{ - switch (*rc_p) { - case 0: - case REC_OPERAND_INV: - case REC_OPERAND_SIZE: - case REC_EVEN_MOD: - case REC_INVALID_PAD: - case REC_USE_PCICA: - break; - - case REC_BUSY: - case REC_NO_WORK: - case REC_EMPTY: - case REC_RETRY_DEV: - case REC_FATAL_ERROR: - return 0; - - case REC_NO_RESPONSE: - break; - - default: - PRINTK("rc %d, device %d converted to REC_NO_RESPONSE\n", - *rc_p, SHRT2LONG(index)); - *rc_p = REC_NO_RESPONSE; - break; - } - return 1; -} - -static inline void -z90crypt_schedule_reader_timer(void) -{ - if (timer_pending(&reader_timer)) - return; - if (mod_timer(&reader_timer, jiffies+(READERTIME*HZ/1000)) != 0) - PRINTK("Timer pending while modifying reader timer\n"); -} - -static void -z90crypt_reader_task(unsigned long ptr) -{ - int workavail, index, rc, buff_len; - unsigned char psmid[8]; - unsigned char __user *resp_addr; - static unsigned char buff[1024]; - - /** - * we use workavail = 2 to ensure 2 passes with nothing dequeued before - * exiting the loop. If (pendingq_count+requestq_count) == 0 after the - * loop, there is no work remaining on the queues. - */ - resp_addr = 0; - workavail = 2; - buff_len = 0; - while (workavail) { - workavail--; - rc = 0; - spin_lock_irq(&queuespinlock); - memset(buff, 0x00, sizeof(buff)); - - /* Dequeue once from each device in round robin. */ - for (index = 0; index < z90crypt.mask.st_count; index++) { - PDEBUG("About to receive.\n"); - rc = receive_from_crypto_device(SHRT2LONG(index), - psmid, - &buff_len, - buff, - &resp_addr); - PDEBUG("Dequeued: rc = %d.\n", rc); - - if (helper_receive_rc(index, &rc)) { - if (rc != REC_NO_RESPONSE) { - helper_send_work(index); - workavail = 2; - } - - helper_handle_work_element(index, psmid, rc, - buff_len, buff, - resp_addr); - } - - if (rc == REC_FATAL_ERROR) - PRINTKW("REC_FATAL_ERROR from device %d!\n", - SHRT2LONG(index)); - } - spin_unlock_irq(&queuespinlock); - } - - if (pendingq_count + requestq_count) - z90crypt_schedule_reader_timer(); -} - -static inline void -z90crypt_schedule_config_task(unsigned int expiration) -{ - if (timer_pending(&config_timer)) - return; - if (mod_timer(&config_timer, jiffies+(expiration*HZ)) != 0) - PRINTK("Timer pending while modifying config timer\n"); -} - -static void -z90crypt_config_task(unsigned long ptr) -{ - int rc; - - PDEBUG("jiffies %ld\n", jiffies); - - if ((rc = refresh_z90crypt(&z90crypt.cdx))) - PRINTK("Error %d detected in refresh_z90crypt.\n", rc); - /* If return was fatal, don't bother reconfiguring */ - if ((rc != TSQ_FATAL_ERROR) && (rc != RSQ_FATAL_ERROR)) - z90crypt_schedule_config_task(CONFIGTIME); -} - -static inline void -z90crypt_schedule_cleanup_task(void) -{ - if (timer_pending(&cleanup_timer)) - return; - if (mod_timer(&cleanup_timer, jiffies+(CLEANUPTIME*HZ)) != 0) - PRINTK("Timer pending while modifying cleanup timer\n"); -} - -static inline void -helper_drain_queues(void) -{ - struct work_element *pq_p; - struct list_head *lptr, *tptr; - - list_for_each_safe(lptr, tptr, &pending_list) { - pq_p = list_entry(lptr, struct work_element, liste); - pq_p->retcode = -ENODEV; - pq_p->status[0] |= STAT_FAILED; - unbuild_caller(LONG2DEVPTR(pq_p->devindex), - (struct caller *)pq_p->requestptr); - list_del_init(lptr); - pendingq_count--; - pq_p->audit[1] |= FP_NOTPENDING; - pq_p->audit[1] |= FP_AWAKENING; - atomic_set(&pq_p->alarmrung, 1); - wake_up(&pq_p->waitq); - } - - list_for_each_safe(lptr, tptr, &request_list) { - pq_p = list_entry(lptr, struct work_element, liste); - pq_p->retcode = -ENODEV; - pq_p->status[0] |= STAT_FAILED; - list_del_init(lptr); - requestq_count--; - pq_p->audit[1] |= FP_REMREQUEST; - pq_p->audit[1] |= FP_AWAKENING; - atomic_set(&pq_p->alarmrung, 1); - wake_up(&pq_p->waitq); - } -} - -static inline void -helper_timeout_requests(void) -{ - struct work_element *pq_p; - struct list_head *lptr, *tptr; - long timelimit; - - timelimit = jiffies - (CLEANUPTIME * HZ); - /* The list is in strict chronological order */ - list_for_each_safe(lptr, tptr, &pending_list) { - pq_p = list_entry(lptr, struct work_element, liste); - if (pq_p->requestsent >= timelimit) - break; - PRINTKW("Purging(PQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n", - ((struct caller *)pq_p->requestptr)->caller_id[0], - ((struct caller *)pq_p->requestptr)->caller_id[1], - ((struct caller *)pq_p->requestptr)->caller_id[2], - ((struct caller *)pq_p->requestptr)->caller_id[3], - ((struct caller *)pq_p->requestptr)->caller_id[4], - ((struct caller *)pq_p->requestptr)->caller_id[5], - ((struct caller *)pq_p->requestptr)->caller_id[6], - ((struct caller *)pq_p->requestptr)->caller_id[7]); - pq_p->retcode = -ETIMEOUT; - pq_p->status[0] |= STAT_FAILED; - /* get this off any caller queue it may be on */ - unbuild_caller(LONG2DEVPTR(pq_p->devindex), - (struct caller *) pq_p->requestptr); - list_del_init(lptr); - pendingq_count--; - pq_p->audit[1] |= FP_TIMEDOUT; - pq_p->audit[1] |= FP_NOTPENDING; - pq_p->audit[1] |= FP_AWAKENING; - atomic_set(&pq_p->alarmrung, 1); - wake_up(&pq_p->waitq); - } - - /** - * If pending count is zero, items left on the request queue may - * never be processed. - */ - if (pendingq_count <= 0) { - list_for_each_safe(lptr, tptr, &request_list) { - pq_p = list_entry(lptr, struct work_element, liste); - if (pq_p->requestsent >= timelimit) - break; - PRINTKW("Purging(RQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n", - ((struct caller *)pq_p->requestptr)->caller_id[0], - ((struct caller *)pq_p->requestptr)->caller_id[1], - ((struct caller *)pq_p->requestptr)->caller_id[2], - ((struct caller *)pq_p->requestptr)->caller_id[3], - ((struct caller *)pq_p->requestptr)->caller_id[4], - ((struct caller *)pq_p->requestptr)->caller_id[5], - ((struct caller *)pq_p->requestptr)->caller_id[6], - ((struct caller *)pq_p->requestptr)->caller_id[7]); - pq_p->retcode = -ETIMEOUT; - pq_p->status[0] |= STAT_FAILED; - list_del_init(lptr); - requestq_count--; - pq_p->audit[1] |= FP_TIMEDOUT; - pq_p->audit[1] |= FP_REMREQUEST; - pq_p->audit[1] |= FP_AWAKENING; - atomic_set(&pq_p->alarmrung, 1); - wake_up(&pq_p->waitq); - } - } -} - -static void -z90crypt_cleanup_task(unsigned long ptr) -{ - PDEBUG("jiffies %ld\n", jiffies); - spin_lock_irq(&queuespinlock); - if (z90crypt.mask.st_count <= 0) // no devices! - helper_drain_queues(); - else - helper_timeout_requests(); - spin_unlock_irq(&queuespinlock); - z90crypt_schedule_cleanup_task(); -} - -static void -z90crypt_schedule_reader_task(unsigned long ptr) -{ - tasklet_schedule(&reader_tasklet); -} - -/** - * Lowlevel Functions: - * - * create_z90crypt: creates and initializes basic data structures - * refresh_z90crypt: re-initializes basic data structures - * find_crypto_devices: returns a count and mask of hardware status - * create_crypto_device: builds the descriptor for a device - * destroy_crypto_device: unallocates the descriptor for a device - * destroy_z90crypt: drains all work, unallocates structs - */ - -/** - * build the z90crypt root structure using the given domain index - */ -static int -create_z90crypt(int *cdx_p) -{ - struct hdware_block *hdware_blk_p; - - memset(&z90crypt, 0x00, sizeof(struct z90crypt)); - z90crypt.domain_established = 0; - z90crypt.len = sizeof(struct z90crypt); - z90crypt.max_count = Z90CRYPT_NUM_DEVS; - z90crypt.cdx = *cdx_p; - - hdware_blk_p = kzalloc(sizeof(struct hdware_block), GFP_ATOMIC); - if (!hdware_blk_p) { - PDEBUG("kmalloc for hardware block failed\n"); - return ENOMEM; - } - z90crypt.hdware_info = hdware_blk_p; - - return 0; -} - -static inline int -helper_scan_devices(int cdx_array[16], int *cdx_p, int *correct_cdx_found) -{ - enum hdstat hd_stat; - int q_depth, dev_type; - int indx, chkdom, numdomains; - - q_depth = dev_type = numdomains = 0; - for (chkdom = 0; chkdom <= 15; cdx_array[chkdom++] = -1); - for (indx = 0; indx < z90crypt.max_count; indx++) { - hd_stat = HD_NOT_THERE; - numdomains = 0; - for (chkdom = 0; chkdom <= 15; chkdom++) { - hd_stat = query_online(indx, chkdom, MAX_RESET, - &q_depth, &dev_type); - if (hd_stat == HD_TSQ_EXCEPTION) { - z90crypt.terminating = 1; - PRINTKC("exception taken!\n"); - break; - } - if (hd_stat == HD_ONLINE) { - cdx_array[numdomains++] = chkdom; - if (*cdx_p == chkdom) { - *correct_cdx_found = 1; - break; - } - } - } - if ((*correct_cdx_found == 1) || (numdomains != 0)) - break; - if (z90crypt.terminating) - break; - } - return numdomains; -} - -static inline int -probe_crypto_domain(int *cdx_p) -{ - int cdx_array[16]; - char cdx_array_text[53], temp[5]; - int correct_cdx_found, numdomains; - - correct_cdx_found = 0; - numdomains = helper_scan_devices(cdx_array, cdx_p, &correct_cdx_found); - - if (z90crypt.terminating) - return TSQ_FATAL_ERROR; - - if (correct_cdx_found) - return 0; - - if (numdomains == 0) { - PRINTKW("Unable to find crypto domain: No devices found\n"); - return Z90C_NO_DEVICES; - } - - if (numdomains == 1) { - if (*cdx_p == -1) { - *cdx_p = cdx_array[0]; - return 0; - } - PRINTKW("incorrect domain: specified = %d, found = %d\n", - *cdx_p, cdx_array[0]); - return Z90C_INCORRECT_DOMAIN; - } - - numdomains--; - sprintf(cdx_array_text, "%d", cdx_array[numdomains]); - while (numdomains) { - numdomains--; - sprintf(temp, ", %d", cdx_array[numdomains]); - strcat(cdx_array_text, temp); - } - - PRINTKW("ambiguous domain detected: specified = %d, found array = %s\n", - *cdx_p, cdx_array_text); - return Z90C_AMBIGUOUS_DOMAIN; -} - -static int -refresh_z90crypt(int *cdx_p) -{ - int i, j, indx, rv; - static struct status local_mask; - struct device *devPtr; - unsigned char oldStat, newStat; - int return_unchanged; - - if (z90crypt.len != sizeof(z90crypt)) - return ENOTINIT; - if (z90crypt.terminating) - return TSQ_FATAL_ERROR; - rv = 0; - if (!z90crypt.hdware_info->hdware_mask.st_count && - !z90crypt.domain_established) { - rv = probe_crypto_domain(cdx_p); - if (z90crypt.terminating) - return TSQ_FATAL_ERROR; - if (rv == Z90C_NO_DEVICES) - return 0; // try later - if (rv) - return rv; - z90crypt.cdx = *cdx_p; - z90crypt.domain_established = 1; - } - rv = find_crypto_devices(&local_mask); - if (rv) { - PRINTK("find crypto devices returned %d\n", rv); - return rv; - } - if (!memcmp(&local_mask, &z90crypt.hdware_info->hdware_mask, - sizeof(struct status))) { - return_unchanged = 1; - for (i = 0; i < Z90CRYPT_NUM_TYPES; i++) { - /** - * Check for disabled cards. If any device is marked - * disabled, destroy it. - */ - for (j = 0; - j < z90crypt.hdware_info->type_mask[i].st_count; - j++) { - indx = z90crypt.hdware_info->type_x_addr[i]. - device_index[j]; - devPtr = z90crypt.device_p[indx]; - if (devPtr && devPtr->disabled) { - local_mask.st_mask[indx] = HD_NOT_THERE; - return_unchanged = 0; - } - } - } - if (return_unchanged == 1) - return 0; - } - - spin_lock_irq(&queuespinlock); - for (i = 0; i < z90crypt.max_count; i++) { - oldStat = z90crypt.hdware_info->hdware_mask.st_mask[i]; - newStat = local_mask.st_mask[i]; - if ((oldStat == HD_ONLINE) && (newStat != HD_ONLINE)) - destroy_crypto_device(i); - else if ((oldStat != HD_ONLINE) && (newStat == HD_ONLINE)) { - rv = create_crypto_device(i); - if (rv >= REC_FATAL_ERROR) - return rv; - if (rv != 0) { - local_mask.st_mask[i] = HD_NOT_THERE; - local_mask.st_count--; - } - } - } - memcpy(z90crypt.hdware_info->hdware_mask.st_mask, local_mask.st_mask, - sizeof(local_mask.st_mask)); - z90crypt.hdware_info->hdware_mask.st_count = local_mask.st_count; - z90crypt.hdware_info->hdware_mask.disabled_count = - local_mask.disabled_count; - refresh_index_array(&z90crypt.mask, &z90crypt.overall_device_x); - for (i = 0; i < Z90CRYPT_NUM_TYPES; i++) - refresh_index_array(&(z90crypt.hdware_info->type_mask[i]), - &(z90crypt.hdware_info->type_x_addr[i])); - spin_unlock_irq(&queuespinlock); - - return rv; -} - -static int -find_crypto_devices(struct status *deviceMask) -{ - int i, q_depth, dev_type; - enum hdstat hd_stat; - - deviceMask->st_count = 0; - deviceMask->disabled_count = 0; - deviceMask->user_disabled_count = 0; - - for (i = 0; i < z90crypt.max_count; i++) { - hd_stat = query_online(i, z90crypt.cdx, MAX_RESET, &q_depth, - &dev_type); - if (hd_stat == HD_TSQ_EXCEPTION) { - z90crypt.terminating = 1; - PRINTKC("Exception during probe for crypto devices\n"); - return TSQ_FATAL_ERROR; - } - deviceMask->st_mask[i] = hd_stat; - if (hd_stat == HD_ONLINE) { - PDEBUG("Got an online crypto!: %d\n", i); - PDEBUG("Got a queue depth of %d\n", q_depth); - PDEBUG("Got a device type of %d\n", dev_type); - if (q_depth <= 0) - return TSQ_FATAL_ERROR; - deviceMask->st_count++; - z90crypt.q_depth_array[i] = q_depth; - z90crypt.dev_type_array[i] = dev_type; - } - } - - return 0; -} - -static int -refresh_index_array(struct status *status_str, struct device_x *index_array) -{ - int i, count; - enum devstat stat; - - i = -1; - count = 0; - do { - stat = status_str->st_mask[++i]; - if (stat == DEV_ONLINE) - index_array->device_index[count++] = i; - } while ((i < Z90CRYPT_NUM_DEVS) && (count < status_str->st_count)); - - return count; -} - -static int -create_crypto_device(int index) -{ - int rv, devstat, total_size; - struct device *dev_ptr; - struct status *type_str_p; - int deviceType; - - dev_ptr = z90crypt.device_p[index]; - if (!dev_ptr) { - total_size = sizeof(struct device) + - z90crypt.q_depth_array[index] * sizeof(int); - - dev_ptr = kzalloc(total_size, GFP_ATOMIC); - if (!dev_ptr) { - PRINTK("kmalloc device %d failed\n", index); - return ENOMEM; - } - dev_ptr->dev_resp_p = kmalloc(MAX_RESPONSE_SIZE, GFP_ATOMIC); - if (!dev_ptr->dev_resp_p) { - kfree(dev_ptr); - PRINTK("kmalloc device %d rec buffer failed\n", index); - return ENOMEM; - } - dev_ptr->dev_resp_l = MAX_RESPONSE_SIZE; - INIT_LIST_HEAD(&(dev_ptr->dev_caller_list)); - } - - devstat = reset_device(index, z90crypt.cdx, MAX_RESET); - if (devstat == DEV_RSQ_EXCEPTION) { - PRINTK("exception during reset device %d\n", index); - kfree(dev_ptr->dev_resp_p); - kfree(dev_ptr); - return RSQ_FATAL_ERROR; - } - if (devstat == DEV_ONLINE) { - dev_ptr->dev_self_x = index; - dev_ptr->dev_type = z90crypt.dev_type_array[index]; - if (dev_ptr->dev_type == NILDEV) { - rv = probe_device_type(dev_ptr); - if (rv) { - PRINTK("rv = %d from probe_device_type %d\n", - rv, index); - kfree(dev_ptr->dev_resp_p); - kfree(dev_ptr); - return rv; - } - } - if (dev_ptr->dev_type == PCIXCC_UNK) { - rv = probe_PCIXCC_type(dev_ptr); - if (rv) { - PRINTK("rv = %d from probe_PCIXCC_type %d\n", - rv, index); - kfree(dev_ptr->dev_resp_p); - kfree(dev_ptr); - return rv; - } - } - deviceType = dev_ptr->dev_type; - z90crypt.dev_type_array[index] = deviceType; - if (deviceType == PCICA) - z90crypt.hdware_info->device_type_array[index] = 1; - else if (deviceType == PCICC) - z90crypt.hdware_info->device_type_array[index] = 2; - else if (deviceType == PCIXCC_MCL2) - z90crypt.hdware_info->device_type_array[index] = 3; - else if (deviceType == PCIXCC_MCL3) - z90crypt.hdware_info->device_type_array[index] = 4; - else if (deviceType == CEX2C) - z90crypt.hdware_info->device_type_array[index] = 5; - else if (deviceType == CEX2A) - z90crypt.hdware_info->device_type_array[index] = 6; - else // No idea how this would happen. - z90crypt.hdware_info->device_type_array[index] = -1; - } - - /** - * 'q_depth' returned by the hardware is one less than - * the actual depth - */ - dev_ptr->dev_q_depth = z90crypt.q_depth_array[index]; - dev_ptr->dev_type = z90crypt.dev_type_array[index]; - dev_ptr->dev_stat = devstat; - dev_ptr->disabled = 0; - z90crypt.device_p[index] = dev_ptr; - - if (devstat == DEV_ONLINE) { - if (z90crypt.mask.st_mask[index] != DEV_ONLINE) { - z90crypt.mask.st_mask[index] = DEV_ONLINE; - z90crypt.mask.st_count++; - } - deviceType = dev_ptr->dev_type; - type_str_p = &z90crypt.hdware_info->type_mask[deviceType]; - if (type_str_p->st_mask[index] != DEV_ONLINE) { - type_str_p->st_mask[index] = DEV_ONLINE; - type_str_p->st_count++; - } - } - - return 0; -} - -static int -destroy_crypto_device(int index) -{ - struct device *dev_ptr; - int t, disabledFlag; - - dev_ptr = z90crypt.device_p[index]; - - /* remember device type; get rid of device struct */ - if (dev_ptr) { - disabledFlag = dev_ptr->disabled; - t = dev_ptr->dev_type; - kfree(dev_ptr->dev_resp_p); - kfree(dev_ptr); - } else { - disabledFlag = 0; - t = -1; - } - z90crypt.device_p[index] = 0; - - /* if the type is valid, remove the device from the type_mask */ - if ((t != -1) && z90crypt.hdware_info->type_mask[t].st_mask[index]) { - z90crypt.hdware_info->type_mask[t].st_mask[index] = 0x00; - z90crypt.hdware_info->type_mask[t].st_count--; - if (disabledFlag == 1) - z90crypt.hdware_info->type_mask[t].disabled_count--; - } - if (z90crypt.mask.st_mask[index] != DEV_GONE) { - z90crypt.mask.st_mask[index] = DEV_GONE; - z90crypt.mask.st_count--; - } - z90crypt.hdware_info->device_type_array[index] = 0; - - return 0; -} - -static void -destroy_z90crypt(void) -{ - int i; - - for (i = 0; i < z90crypt.max_count; i++) - if (z90crypt.device_p[i]) - destroy_crypto_device(i); - kfree(z90crypt.hdware_info); - memset((void *)&z90crypt, 0, sizeof(z90crypt)); -} - -static unsigned char static_testmsg[384] = { -0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x06,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x43, -0x41,0x2d,0x41,0x50,0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,0x00,0x00,0x00,0x00, -0x50,0x4b,0x00,0x00,0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x32, -0x01,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0xb8,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x43,0x53,0x46, -0x20,0x20,0x20,0x20,0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,0x2d,0x31,0x2e,0x32, -0x37,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44, -0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, -0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66, -0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,0x88,0x1e,0x00,0x00, -0x57,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,0x03,0x02,0x00,0x00, -0x40,0x01,0x00,0x01,0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,0xf6,0xd2,0x7b,0x58, -0x4b,0xf9,0x28,0x68,0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,0x63,0x42,0xef,0xf8, -0xfd,0xa4,0xf8,0xb0,0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,0x53,0x8c,0x6f,0x4e, -0x72,0x8f,0x6c,0x04,0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,0xf7,0xdd,0xfd,0x4f, -0x11,0x36,0x95,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - -static int -probe_device_type(struct device *devPtr) -{ - int rv, dv, i, index, length; - unsigned char psmid[8]; - static unsigned char loc_testmsg[sizeof(static_testmsg)]; - - index = devPtr->dev_self_x; - rv = 0; - do { - memcpy(loc_testmsg, static_testmsg, sizeof(static_testmsg)); - length = sizeof(static_testmsg) - 24; - /* the -24 allows for the header */ - dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg); - if (dv) { - PDEBUG("dv returned by send during probe: %d\n", dv); - if (dv == DEV_SEN_EXCEPTION) { - rv = SEN_FATAL_ERROR; - PRINTKC("exception in send to AP %d\n", index); - break; - } - PDEBUG("return value from send_to_AP: %d\n", rv); - switch (dv) { - case DEV_GONE: - PDEBUG("dev %d not available\n", index); - rv = SEN_NOT_AVAIL; - break; - case DEV_ONLINE: - rv = 0; - break; - case DEV_EMPTY: - rv = SEN_NOT_AVAIL; - break; - case DEV_NO_WORK: - rv = SEN_FATAL_ERROR; - break; - case DEV_BAD_MESSAGE: - rv = SEN_USER_ERROR; - break; - case DEV_QUEUE_FULL: - rv = SEN_QUEUE_FULL; - break; - default: - PRINTK("unknown dv=%d for dev %d\n", dv, index); - rv = SEN_NOT_AVAIL; - break; - } - } - - if (rv) - break; - - for (i = 0; i < 6; i++) { - mdelay(300); - dv = receive_from_AP(index, z90crypt.cdx, - devPtr->dev_resp_l, - devPtr->dev_resp_p, psmid); - PDEBUG("dv returned by DQ = %d\n", dv); - if (dv == DEV_REC_EXCEPTION) { - rv = REC_FATAL_ERROR; - PRINTKC("exception in dequeue %d\n", - index); - break; - } - switch (dv) { - case DEV_ONLINE: - rv = 0; - break; - case DEV_EMPTY: - rv = REC_EMPTY; - break; - case DEV_NO_WORK: - rv = REC_NO_WORK; - break; - case DEV_BAD_MESSAGE: - case DEV_GONE: - default: - rv = REC_NO_RESPONSE; - break; - } - if ((rv != 0) && (rv != REC_NO_WORK)) - break; - if (rv == 0) - break; - } - if (rv) - break; - rv = (devPtr->dev_resp_p[0] == 0x00) && - (devPtr->dev_resp_p[1] == 0x86); - if (rv) - devPtr->dev_type = PCICC; - else - devPtr->dev_type = PCICA; - rv = 0; - } while (0); - /* In a general error case, the card is not marked online */ - return rv; -} - -static unsigned char MCL3_testmsg[] = { -0x00,0x00,0x00,0x00,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, -0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00,0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A,0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20, -0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D, -0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD, -0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22, -0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54, -0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00,0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00, -0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C, -0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF,0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9, -0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63,0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5, -0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A,0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01, -0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28,0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91, -0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5,0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C, -0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98,0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96, -0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19,0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47, -0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36,0xF1,0x3D,0x93,0x53 -}; - -static int -probe_PCIXCC_type(struct device *devPtr) -{ - int rv, dv, i, index, length; - unsigned char psmid[8]; - static unsigned char loc_testmsg[548]; - struct CPRBX *cprbx_p; - - index = devPtr->dev_self_x; - rv = 0; - do { - memcpy(loc_testmsg, MCL3_testmsg, sizeof(MCL3_testmsg)); - length = sizeof(MCL3_testmsg) - 0x0C; - dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg); - if (dv) { - PDEBUG("dv returned = %d\n", dv); - if (dv == DEV_SEN_EXCEPTION) { - rv = SEN_FATAL_ERROR; - PRINTKC("exception in send to AP %d\n", index); - break; - } - PDEBUG("return value from send_to_AP: %d\n", rv); - switch (dv) { - case DEV_GONE: - PDEBUG("dev %d not available\n", index); - rv = SEN_NOT_AVAIL; - break; - case DEV_ONLINE: - rv = 0; - break; - case DEV_EMPTY: - rv = SEN_NOT_AVAIL; - break; - case DEV_NO_WORK: - rv = SEN_FATAL_ERROR; - break; - case DEV_BAD_MESSAGE: - rv = SEN_USER_ERROR; - break; - case DEV_QUEUE_FULL: - rv = SEN_QUEUE_FULL; - break; - default: - PRINTK("unknown dv=%d for dev %d\n", dv, index); - rv = SEN_NOT_AVAIL; - break; - } - } - - if (rv) - break; - - for (i = 0; i < 6; i++) { - mdelay(300); - dv = receive_from_AP(index, z90crypt.cdx, - devPtr->dev_resp_l, - devPtr->dev_resp_p, psmid); - PDEBUG("dv returned by DQ = %d\n", dv); - if (dv == DEV_REC_EXCEPTION) { - rv = REC_FATAL_ERROR; - PRINTKC("exception in dequeue %d\n", - index); - break; - } - switch (dv) { - case DEV_ONLINE: - rv = 0; - break; - case DEV_EMPTY: - rv = REC_EMPTY; - break; - case DEV_NO_WORK: - rv = REC_NO_WORK; - break; - case DEV_BAD_MESSAGE: - case DEV_GONE: - default: - rv = REC_NO_RESPONSE; - break; - } - if ((rv != 0) && (rv != REC_NO_WORK)) - break; - if (rv == 0) - break; - } - if (rv) - break; - cprbx_p = (struct CPRBX *) (devPtr->dev_resp_p + 48); - if ((cprbx_p->ccp_rtcode == 8) && (cprbx_p->ccp_rscode == 33)) { - devPtr->dev_type = PCIXCC_MCL2; - PDEBUG("device %d is MCL2\n", index); - } else { - devPtr->dev_type = PCIXCC_MCL3; - PDEBUG("device %d is MCL3\n", index); - } - } while (0); - /* In a general error case, the card is not marked online */ - return rv; -} - -module_init(z90crypt_init_module); -module_exit(z90crypt_cleanup_module); diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c new file mode 100644 index 0000000..1edc10a --- /dev/null +++ b/drivers/s390/crypto/zcrypt_api.c @@ -0,0 +1,1091 @@ +/* + * linux/drivers/s390/crypto/zcrypt_api.c + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * Cornelia Huck <cornelia.huck@de.ibm.com> + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/compat.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> + +#include "zcrypt_api.h" + +/** + * Module description. + */ +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " + "Copyright 2001, 2006 IBM Corporation"); +MODULE_LICENSE("GPL"); + +static DEFINE_SPINLOCK(zcrypt_device_lock); +static LIST_HEAD(zcrypt_device_list); +static int zcrypt_device_count = 0; +static atomic_t zcrypt_open_count = ATOMIC_INIT(0); + +/** + * Device attributes common for all crypto devices. + */ +static ssize_t zcrypt_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zcrypt_device *zdev = to_ap_dev(dev)->private; + return snprintf(buf, PAGE_SIZE, "%s\n", zdev->type_string); +} + +static DEVICE_ATTR(type, 0444, zcrypt_type_show, NULL); + +static ssize_t zcrypt_online_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zcrypt_device *zdev = to_ap_dev(dev)->private; + return snprintf(buf, PAGE_SIZE, "%d\n", zdev->online); +} + +static ssize_t zcrypt_online_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct zcrypt_device *zdev = to_ap_dev(dev)->private; + int online; + + if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1) + return -EINVAL; + zdev->online = online; + if (!online) + ap_flush_queue(zdev->ap_dev); + return count; +} + +static DEVICE_ATTR(online, 0644, zcrypt_online_show, zcrypt_online_store); + +static struct attribute * zcrypt_device_attrs[] = { + &dev_attr_type.attr, + &dev_attr_online.attr, + NULL, +}; + +static struct attribute_group zcrypt_device_attr_group = { + .attrs = zcrypt_device_attrs, +}; + +/** + * Move the device towards the head of the device list. + * Need to be called while holding the zcrypt device list lock. + * Note: cards with speed_rating of 0 are kept at the end of the list. + */ +static void __zcrypt_increase_preference(struct zcrypt_device *zdev) +{ + struct zcrypt_device *tmp; + struct list_head *l; + + if (zdev->speed_rating == 0) + return; + for (l = zdev->list.prev; l != &zcrypt_device_list; l = l->prev) { + tmp = list_entry(l, struct zcrypt_device, list); + if ((tmp->request_count + 1) * tmp->speed_rating <= + (zdev->request_count + 1) * zdev->speed_rating && + tmp->speed_rating != 0) + break; + } + if (l == zdev->list.prev) + return; + /* Move zdev behind l */ + list_del(&zdev->list); + list_add(&zdev->list, l); +} + +/** + * Move the device towards the tail of the device list. + * Need to be called while holding the zcrypt device list lock. + * Note: cards with speed_rating of 0 are kept at the end of the list. + */ +static void __zcrypt_decrease_preference(struct zcrypt_device *zdev) +{ + struct zcrypt_device *tmp; + struct list_head *l; + + if (zdev->speed_rating == 0) + return; + for (l = zdev->list.next; l != &zcrypt_device_list; l = l->next) { + tmp = list_entry(l, struct zcrypt_device, list); + if ((tmp->request_count + 1) * tmp->speed_rating > + (zdev->request_count + 1) * zdev->speed_rating || + tmp->speed_rating == 0) + break; + } + if (l == zdev->list.next) + return; + /* Move zdev before l */ + list_del(&zdev->list); + list_add_tail(&zdev->list, l); +} + +static void zcrypt_device_release(struct kref *kref) +{ + struct zcrypt_device *zdev = + container_of(kref, struct zcrypt_device, refcount); + zcrypt_device_free(zdev); +} + +void zcrypt_device_get(struct zcrypt_device *zdev) +{ + kref_get(&zdev->refcount); +} +EXPORT_SYMBOL(zcrypt_device_get); + +int zcrypt_device_put(struct zcrypt_device *zdev) +{ + return kref_put(&zdev->refcount, zcrypt_device_release); +} +EXPORT_SYMBOL(zcrypt_device_put); + +struct zcrypt_device *zcrypt_device_alloc(size_t max_response_size) +{ + struct zcrypt_device *zdev; + + zdev = kzalloc(sizeof(struct zcrypt_device), GFP_KERNEL); + if (!zdev) + return NULL; + zdev->reply.message = kmalloc(max_response_size, GFP_KERNEL); + if (!zdev->reply.message) + goto out_free; + zdev->reply.length = max_response_size; + spin_lock_init(&zdev->lock); + INIT_LIST_HEAD(&zdev->list); + return zdev; + +out_free: + kfree(zdev); + return NULL; +} +EXPORT_SYMBOL(zcrypt_device_alloc); + +void zcrypt_device_free(struct zcrypt_device *zdev) +{ + kfree(zdev->reply.message); + kfree(zdev); +} +EXPORT_SYMBOL(zcrypt_device_free); + +/** + * Register a crypto device. + */ +int zcrypt_device_register(struct zcrypt_device *zdev) +{ + int rc; + + rc = sysfs_create_group(&zdev->ap_dev->device.kobj, + &zcrypt_device_attr_group); + if (rc) + goto out; + get_device(&zdev->ap_dev->device); + kref_init(&zdev->refcount); + spin_lock_bh(&zcrypt_device_lock); + zdev->online = 1; /* New devices are online by default. */ + list_add_tail(&zdev->list, &zcrypt_device_list); + __zcrypt_increase_preference(zdev); + zcrypt_device_count++; + spin_unlock_bh(&zcrypt_device_lock); +out: + return rc; +} +EXPORT_SYMBOL(zcrypt_device_register); + +/** + * Unregister a crypto device. + */ +void zcrypt_device_unregister(struct zcrypt_device *zdev) +{ + spin_lock_bh(&zcrypt_device_lock); + zcrypt_device_count--; + list_del_init(&zdev->list); + spin_unlock_bh(&zcrypt_device_lock); + sysfs_remove_group(&zdev->ap_dev->device.kobj, + &zcrypt_device_attr_group); + put_device(&zdev->ap_dev->device); + zcrypt_device_put(zdev); +} +EXPORT_SYMBOL(zcrypt_device_unregister); + +/** + * zcrypt_read is not be supported beyond zcrypt 1.3.1 + */ +static ssize_t zcrypt_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + return -EPERM; +} + +/** + * Write is is not allowed + */ +static ssize_t zcrypt_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + return -EPERM; +} + +/** + * Device open/close functions to count number of users. + */ +static int zcrypt_open(struct inode *inode, struct file *filp) +{ + atomic_inc(&zcrypt_open_count); + return 0; +} + +static int zcrypt_release(struct inode *inode, struct file *filp) +{ + atomic_dec(&zcrypt_open_count); + return 0; +} + +/** + * zcrypt ioctls. + */ +static long zcrypt_rsa_modexpo(struct ica_rsa_modexpo *mex) +{ + struct zcrypt_device *zdev; + int rc; + + if (mex->outputdatalength < mex->inputdatalength) + return -EINVAL; + /** + * As long as outputdatalength is big enough, we can set the + * outputdatalength equal to the inputdatalength, since that is the + * number of bytes we will copy in any case + */ + mex->outputdatalength = mex->inputdatalength; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + if (!zdev->online || + !zdev->ops->rsa_modexpo || + zdev->min_mod_size > mex->inputdatalength || + zdev->max_mod_size < mex->inputdatalength) + continue; + zcrypt_device_get(zdev); + get_device(&zdev->ap_dev->device); + zdev->request_count++; + __zcrypt_decrease_preference(zdev); + spin_unlock_bh(&zcrypt_device_lock); + if (try_module_get(zdev->ap_dev->drv->driver.owner)) { + rc = zdev->ops->rsa_modexpo(zdev, mex); + module_put(zdev->ap_dev->drv->driver.owner); + } + else + rc = -EAGAIN; + spin_lock_bh(&zcrypt_device_lock); + zdev->request_count--; + __zcrypt_increase_preference(zdev); + put_device(&zdev->ap_dev->device); + zcrypt_device_put(zdev); + spin_unlock_bh(&zcrypt_device_lock); + return rc; + } + spin_unlock_bh(&zcrypt_device_lock); + return -ENODEV; +} + +static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt) +{ + struct zcrypt_device *zdev; + unsigned long long z1, z2, z3; + int rc, copied; + + if (crt->outputdatalength < crt->inputdatalength || + (crt->inputdatalength & 1)) + return -EINVAL; + /** + * As long as outputdatalength is big enough, we can set the + * outputdatalength equal to the inputdatalength, since that is the + * number of bytes we will copy in any case + */ + crt->outputdatalength = crt->inputdatalength; + + copied = 0; + restart: + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + if (!zdev->online || + !zdev->ops->rsa_modexpo_crt || + zdev->min_mod_size > crt->inputdatalength || + zdev->max_mod_size < crt->inputdatalength) + continue; + if (zdev->short_crt && crt->inputdatalength > 240) { + /** + * Check inputdata for leading zeros for cards + * that can't handle np_prime, bp_key, or + * u_mult_inv > 128 bytes. + */ + if (copied == 0) { + int len; + spin_unlock_bh(&zcrypt_device_lock); + /* len is max 256 / 2 - 120 = 8 */ + len = crt->inputdatalength / 2 - 120; + z1 = z2 = z3 = 0; + if (copy_from_user(&z1, crt->np_prime, len) || + copy_from_user(&z2, crt->bp_key, len) || + copy_from_user(&z3, crt->u_mult_inv, len)) + return -EFAULT; + copied = 1; + /** + * We have to restart device lookup - + * the device list may have changed by now. + */ + goto restart; + } + if (z1 != 0ULL || z2 != 0ULL || z3 != 0ULL) + /* The device can't handle this request. */ + continue; + } + zcrypt_device_get(zdev); + get_device(&zdev->ap_dev->device); + zdev->request_count++; + __zcrypt_decrease_preference(zdev); + spin_unlock_bh(&zcrypt_device_lock); + if (try_module_get(zdev->ap_dev->drv->driver.owner)) { + rc = zdev->ops->rsa_modexpo_crt(zdev, crt); + module_put(zdev->ap_dev->drv->driver.owner); + } + else + rc = -EAGAIN; + spin_lock_bh(&zcrypt_device_lock); + zdev->request_count--; + __zcrypt_increase_preference(zdev); + put_device(&zdev->ap_dev->device); + zcrypt_device_put(zdev); + spin_unlock_bh(&zcrypt_device_lock); + return rc; + } + spin_unlock_bh(&zcrypt_device_lock); + return -ENODEV; +} + +static long zcrypt_send_cprb(struct ica_xcRB *xcRB) +{ + struct zcrypt_device *zdev; + int rc; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + if (!zdev->online || !zdev->ops->send_cprb || + (xcRB->user_defined != AUTOSELECT && + AP_QID_DEVICE(zdev->ap_dev->qid) != xcRB->user_defined) + ) + continue; + zcrypt_device_get(zdev); + get_device(&zdev->ap_dev->device); + zdev->request_count++; + __zcrypt_decrease_preference(zdev); + spin_unlock_bh(&zcrypt_device_lock); + if (try_module_get(zdev->ap_dev->drv->driver.owner)) { + rc = zdev->ops->send_cprb(zdev, xcRB); + module_put(zdev->ap_dev->drv->driver.owner); + } + else + rc = -EAGAIN; + spin_lock_bh(&zcrypt_device_lock); + zdev->request_count--; + __zcrypt_increase_preference(zdev); + put_device(&zdev->ap_dev->device); + zcrypt_device_put(zdev); + spin_unlock_bh(&zcrypt_device_lock); + return rc; + } + spin_unlock_bh(&zcrypt_device_lock); + return -ENODEV; +} + +static void zcrypt_status_mask(char status[AP_DEVICES]) +{ + struct zcrypt_device *zdev; + + memset(status, 0, sizeof(char) * AP_DEVICES); + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) + status[AP_QID_DEVICE(zdev->ap_dev->qid)] = + zdev->online ? zdev->user_space_type : 0x0d; + spin_unlock_bh(&zcrypt_device_lock); +} + +static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES]) +{ + struct zcrypt_device *zdev; + + memset(qdepth, 0, sizeof(char) * AP_DEVICES); + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + spin_lock(&zdev->ap_dev->lock); + qdepth[AP_QID_DEVICE(zdev->ap_dev->qid)] = + zdev->ap_dev->pendingq_count + + zdev->ap_dev->requestq_count; + spin_unlock(&zdev->ap_dev->lock); + } + spin_unlock_bh(&zcrypt_device_lock); +} + +static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES]) +{ + struct zcrypt_device *zdev; + + memset(reqcnt, 0, sizeof(int) * AP_DEVICES); + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + spin_lock(&zdev->ap_dev->lock); + reqcnt[AP_QID_DEVICE(zdev->ap_dev->qid)] = + zdev->ap_dev->total_request_count; + spin_unlock(&zdev->ap_dev->lock); + } + spin_unlock_bh(&zcrypt_device_lock); +} + +static int zcrypt_pendingq_count(void) +{ + struct zcrypt_device *zdev; + int pendingq_count = 0; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + spin_lock(&zdev->ap_dev->lock); + pendingq_count += zdev->ap_dev->pendingq_count; + spin_unlock(&zdev->ap_dev->lock); + } + spin_unlock_bh(&zcrypt_device_lock); + return pendingq_count; +} + +static int zcrypt_requestq_count(void) +{ + struct zcrypt_device *zdev; + int requestq_count = 0; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) { + spin_lock(&zdev->ap_dev->lock); + requestq_count += zdev->ap_dev->requestq_count; + spin_unlock(&zdev->ap_dev->lock); + } + spin_unlock_bh(&zcrypt_device_lock); + return requestq_count; +} + +static int zcrypt_count_type(int type) +{ + struct zcrypt_device *zdev; + int device_count = 0; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) + if (zdev->user_space_type == type) + device_count++; + spin_unlock_bh(&zcrypt_device_lock); + return device_count; +} + +/** + * Old, deprecated combi status call. + */ +static long zcrypt_ica_status(struct file *filp, unsigned long arg) +{ + struct ica_z90_status *pstat; + int ret; + + pstat = kzalloc(sizeof(*pstat), GFP_KERNEL); + if (!pstat) + return -ENOMEM; + pstat->totalcount = zcrypt_device_count; + pstat->leedslitecount = zcrypt_count_type(ZCRYPT_PCICA); + pstat->leeds2count = zcrypt_count_type(ZCRYPT_PCICC); + pstat->requestqWaitCount = zcrypt_requestq_count(); + pstat->pendingqWaitCount = zcrypt_pendingq_count(); + pstat->totalOpenCount = atomic_read(&zcrypt_open_count); + pstat->cryptoDomain = ap_domain_index; + zcrypt_status_mask(pstat->status); + zcrypt_qdepth_mask(pstat->qdepth); + ret = 0; + if (copy_to_user((void __user *) arg, pstat, sizeof(*pstat))) + ret = -EFAULT; + kfree(pstat); + return ret; +} + +static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int rc; + + switch (cmd) { + case ICARSAMODEXPO: { + struct ica_rsa_modexpo __user *umex = (void __user *) arg; + struct ica_rsa_modexpo mex; + if (copy_from_user(&mex, umex, sizeof(mex))) + return -EFAULT; + do { + rc = zcrypt_rsa_modexpo(&mex); + } while (rc == -EAGAIN); + if (rc) + return rc; + return put_user(mex.outputdatalength, &umex->outputdatalength); + } + case ICARSACRT: { + struct ica_rsa_modexpo_crt __user *ucrt = (void __user *) arg; + struct ica_rsa_modexpo_crt crt; + if (copy_from_user(&crt, ucrt, sizeof(crt))) + return -EFAULT; + do { + rc = zcrypt_rsa_crt(&crt); + } while (rc == -EAGAIN); + if (rc) + return rc; + return put_user(crt.outputdatalength, &ucrt->outputdatalength); + } + case ZSECSENDCPRB: { + struct ica_xcRB __user *uxcRB = (void __user *) arg; + struct ica_xcRB xcRB; + if (copy_from_user(&xcRB, uxcRB, sizeof(xcRB))) + return -EFAULT; + do { + rc = zcrypt_send_cprb(&xcRB); + } while (rc == -EAGAIN); + if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB))) + return -EFAULT; + return rc; + } + case Z90STAT_STATUS_MASK: { + char status[AP_DEVICES]; + zcrypt_status_mask(status); + if (copy_to_user((char __user *) arg, status, + sizeof(char) * AP_DEVICES)) + return -EFAULT; + return 0; + } + case Z90STAT_QDEPTH_MASK: { + char qdepth[AP_DEVICES]; + zcrypt_qdepth_mask(qdepth); + if (copy_to_user((char __user *) arg, qdepth, + sizeof(char) * AP_DEVICES)) + return -EFAULT; + return 0; + } + case Z90STAT_PERDEV_REQCNT: { + int reqcnt[AP_DEVICES]; + zcrypt_perdev_reqcnt(reqcnt); + if (copy_to_user((int __user *) arg, reqcnt, + sizeof(int) * AP_DEVICES)) + return -EFAULT; + return 0; + } + case Z90STAT_REQUESTQ_COUNT: + return put_user(zcrypt_requestq_count(), (int __user *) arg); + case Z90STAT_PENDINGQ_COUNT: + return put_user(zcrypt_pendingq_count(), (int __user *) arg); + case Z90STAT_TOTALOPEN_COUNT: + return put_user(atomic_read(&zcrypt_open_count), + (int __user *) arg); + case Z90STAT_DOMAIN_INDEX: + return put_user(ap_domain_index, (int __user *) arg); + /** + * Deprecated ioctls. Don't add another device count ioctl, + * you can count them yourself in the user space with the + * output of the Z90STAT_STATUS_MASK ioctl. + */ + case ICAZ90STATUS: + return zcrypt_ica_status(filp, arg); + case Z90STAT_TOTALCOUNT: + return put_user(zcrypt_device_count, (int __user *) arg); + case Z90STAT_PCICACOUNT: + return put_user(zcrypt_count_type(ZCRYPT_PCICA), + (int __user *) arg); + case Z90STAT_PCICCCOUNT: + return put_user(zcrypt_count_type(ZCRYPT_PCICC), + (int __user *) arg); + case Z90STAT_PCIXCCMCL2COUNT: + return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL2), + (int __user *) arg); + case Z90STAT_PCIXCCMCL3COUNT: + return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL3), + (int __user *) arg); + case Z90STAT_PCIXCCCOUNT: + return put_user(zcrypt_count_type(ZCRYPT_PCIXCC_MCL2) + + zcrypt_count_type(ZCRYPT_PCIXCC_MCL3), + (int __user *) arg); + case Z90STAT_CEX2CCOUNT: + return put_user(zcrypt_count_type(ZCRYPT_CEX2C), + (int __user *) arg); + case Z90STAT_CEX2ACOUNT: + return put_user(zcrypt_count_type(ZCRYPT_CEX2A), + (int __user *) arg); + default: + /* unknown ioctl number */ + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +/** + * ioctl32 conversion routines + */ +struct compat_ica_rsa_modexpo { + compat_uptr_t inputdata; + unsigned int inputdatalength; + compat_uptr_t outputdata; + unsigned int outputdatalength; + compat_uptr_t b_key; + compat_uptr_t n_modulus; +}; + +static long trans_modexpo32(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct compat_ica_rsa_modexpo __user *umex32 = compat_ptr(arg); + struct compat_ica_rsa_modexpo mex32; + struct ica_rsa_modexpo mex64; + long rc; + + if (copy_from_user(&mex32, umex32, sizeof(mex32))) + return -EFAULT; + mex64.inputdata = compat_ptr(mex32.inputdata); + mex64.inputdatalength = mex32.inputdatalength; + mex64.outputdata = compat_ptr(mex32.outputdata); + mex64.outputdatalength = mex32.outputdatalength; + mex64.b_key = compat_ptr(mex32.b_key); + mex64.n_modulus = compat_ptr(mex32.n_modulus); + do { + rc = zcrypt_rsa_modexpo(&mex64); + } while (rc == -EAGAIN); + if (!rc) + rc = put_user(mex64.outputdatalength, + &umex32->outputdatalength); + return rc; +} + +struct compat_ica_rsa_modexpo_crt { + compat_uptr_t inputdata; + unsigned int inputdatalength; + compat_uptr_t outputdata; + unsigned int outputdatalength; + compat_uptr_t bp_key; + compat_uptr_t bq_key; + compat_uptr_t np_prime; + compat_uptr_t nq_prime; + compat_uptr_t u_mult_inv; +}; + +static long trans_modexpo_crt32(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct compat_ica_rsa_modexpo_crt __user *ucrt32 = compat_ptr(arg); + struct compat_ica_rsa_modexpo_crt crt32; + struct ica_rsa_modexpo_crt crt64; + long rc; + + if (copy_from_user(&crt32, ucrt32, sizeof(crt32))) + return -EFAULT; + crt64.inputdata = compat_ptr(crt32.inputdata); + crt64.inputdatalength = crt32.inputdatalength; + crt64.outputdata= compat_ptr(crt32.outputdata); + crt64.outputdatalength = crt32.outputdatalength; + crt64.bp_key = compat_ptr(crt32.bp_key); + crt64.bq_key = compat_ptr(crt32.bq_key); + crt64.np_prime = compat_ptr(crt32.np_prime); + crt64.nq_prime = compat_ptr(crt32.nq_prime); + crt64.u_mult_inv = compat_ptr(crt32.u_mult_inv); + do { + rc = zcrypt_rsa_crt(&crt64); + } while (rc == -EAGAIN); + if (!rc) + rc = put_user(crt64.outputdatalength, + &ucrt32->outputdatalength); + return rc; +} + +struct compat_ica_xcRB { + unsigned short agent_ID; + unsigned int user_defined; + unsigned short request_ID; + unsigned int request_control_blk_length; + unsigned char padding1[16 - sizeof (compat_uptr_t)]; + compat_uptr_t request_control_blk_addr; + unsigned int request_data_length; + char padding2[16 - sizeof (compat_uptr_t)]; + compat_uptr_t request_data_address; + unsigned int reply_control_blk_length; + char padding3[16 - sizeof (compat_uptr_t)]; + compat_uptr_t reply_control_blk_addr; + unsigned int reply_data_length; + char padding4[16 - sizeof (compat_uptr_t)]; + compat_uptr_t reply_data_addr; + unsigned short priority_window; + unsigned int status; +} __attribute__((packed)); + +static long trans_xcRB32(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct compat_ica_xcRB __user *uxcRB32 = compat_ptr(arg); + struct compat_ica_xcRB xcRB32; + struct ica_xcRB xcRB64; + long rc; + + if (copy_from_user(&xcRB32, uxcRB32, sizeof(xcRB32))) + return -EFAULT; + xcRB64.agent_ID = xcRB32.agent_ID; + xcRB64.user_defined = xcRB32.user_defined; + xcRB64.request_ID = xcRB32.request_ID; + xcRB64.request_control_blk_length = + xcRB32.request_control_blk_length; + xcRB64.request_control_blk_addr = + compat_ptr(xcRB32.request_control_blk_addr); + xcRB64.request_data_length = + xcRB32.request_data_length; + xcRB64.request_data_address = + compat_ptr(xcRB32.request_data_address); + xcRB64.reply_control_blk_length = + xcRB32.reply_control_blk_length; + xcRB64.reply_control_blk_addr = + compat_ptr(xcRB32.reply_control_blk_addr); + xcRB64.reply_data_length = xcRB32.reply_data_length; + xcRB64.reply_data_addr = + compat_ptr(xcRB32.reply_data_addr); + xcRB64.priority_window = xcRB32.priority_window; + xcRB64.status = xcRB32.status; + do { + rc = zcrypt_send_cprb(&xcRB64); + } while (rc == -EAGAIN); + xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length; + xcRB32.reply_data_length = xcRB64.reply_data_length; + xcRB32.status = xcRB64.status; + if (copy_to_user(uxcRB32, &xcRB32, sizeof(xcRB32))) + return -EFAULT; + return rc; +} + +long zcrypt_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + if (cmd == ICARSAMODEXPO) + return trans_modexpo32(filp, cmd, arg); + if (cmd == ICARSACRT) + return trans_modexpo_crt32(filp, cmd, arg); + if (cmd == ZSECSENDCPRB) + return trans_xcRB32(filp, cmd, arg); + return zcrypt_unlocked_ioctl(filp, cmd, arg); +} +#endif + +/** + * Misc device file operations. + */ +static struct file_operations zcrypt_fops = { + .owner = THIS_MODULE, + .read = zcrypt_read, + .write = zcrypt_write, + .unlocked_ioctl = zcrypt_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = zcrypt_compat_ioctl, +#endif + .open = zcrypt_open, + .release = zcrypt_release +}; + +/** + * Misc device. + */ +static struct miscdevice zcrypt_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "z90crypt", + .fops = &zcrypt_fops, +}; + +/** + * Deprecated /proc entry support. + */ +static struct proc_dir_entry *zcrypt_entry; + +static inline int sprintcl(unsigned char *outaddr, unsigned char *addr, + unsigned int len) +{ + int hl, i; + + hl = 0; + for (i = 0; i < len; i++) + hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]); + hl += sprintf(outaddr+hl, " "); + return hl; +} + +static inline int sprintrw(unsigned char *outaddr, unsigned char *addr, + unsigned int len) +{ + int hl, inl, c, cx; + + hl = sprintf(outaddr, " "); + inl = 0; + for (c = 0; c < (len / 16); c++) { + hl += sprintcl(outaddr+hl, addr+inl, 16); + inl += 16; + } + cx = len%16; + if (cx) { + hl += sprintcl(outaddr+hl, addr+inl, cx); + inl += cx; + } + hl += sprintf(outaddr+hl, "\n"); + return hl; +} + +static inline int sprinthx(unsigned char *title, unsigned char *outaddr, + unsigned char *addr, unsigned int len) +{ + int hl, inl, r, rx; + + hl = sprintf(outaddr, "\n%s\n", title); + inl = 0; + for (r = 0; r < (len / 64); r++) { + hl += sprintrw(outaddr+hl, addr+inl, 64); + inl += 64; + } + rx = len % 64; + if (rx) { + hl += sprintrw(outaddr+hl, addr+inl, rx); + inl += rx; + } + hl += sprintf(outaddr+hl, "\n"); + return hl; +} + +static inline int sprinthx4(unsigned char *title, unsigned char *outaddr, + unsigned int *array, unsigned int len) +{ + int hl, r; + + hl = sprintf(outaddr, "\n%s\n", title); + for (r = 0; r < len; r++) { + if ((r % 8) == 0) + hl += sprintf(outaddr+hl, " "); + hl += sprintf(outaddr+hl, "%08X ", array[r]); + if ((r % 8) == 7) + hl += sprintf(outaddr+hl, "\n"); + } + hl += sprintf(outaddr+hl, "\n"); + return hl; +} + +static int zcrypt_status_read(char *resp_buff, char **start, off_t offset, + int count, int *eof, void *data) +{ + unsigned char *workarea; + int len; + + len = 0; + + /* resp_buff is a page. Use the right half for a work area */ + workarea = resp_buff + 2000; + len += sprintf(resp_buff + len, "\nzcrypt version: %d.%d.%d\n", + ZCRYPT_VERSION, ZCRYPT_RELEASE, ZCRYPT_VARIANT); + len += sprintf(resp_buff + len, "Cryptographic domain: %d\n", + ap_domain_index); + len += sprintf(resp_buff + len, "Total device count: %d\n", + zcrypt_device_count); + len += sprintf(resp_buff + len, "PCICA count: %d\n", + zcrypt_count_type(ZCRYPT_PCICA)); + len += sprintf(resp_buff + len, "PCICC count: %d\n", + zcrypt_count_type(ZCRYPT_PCICC)); + len += sprintf(resp_buff + len, "PCIXCC MCL2 count: %d\n", + zcrypt_count_type(ZCRYPT_PCIXCC_MCL2)); + len += sprintf(resp_buff + len, "PCIXCC MCL3 count: %d\n", + zcrypt_count_type(ZCRYPT_PCIXCC_MCL3)); + len += sprintf(resp_buff + len, "CEX2C count: %d\n", + zcrypt_count_type(ZCRYPT_CEX2C)); + len += sprintf(resp_buff + len, "CEX2A count: %d\n", + zcrypt_count_type(ZCRYPT_CEX2A)); + len += sprintf(resp_buff + len, "requestq count: %d\n", + zcrypt_requestq_count()); + len += sprintf(resp_buff + len, "pendingq count: %d\n", + zcrypt_pendingq_count()); + len += sprintf(resp_buff + len, "Total open handles: %d\n\n", + atomic_read(&zcrypt_open_count)); + zcrypt_status_mask(workarea); + len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) " + "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A", + resp_buff+len, workarea, AP_DEVICES); + zcrypt_qdepth_mask(workarea); + len += sprinthx("Waiting work element counts", + resp_buff+len, workarea, AP_DEVICES); + zcrypt_perdev_reqcnt((unsigned int *) workarea); + len += sprinthx4("Per-device successfully completed request counts", + resp_buff+len,(unsigned int *) workarea, AP_DEVICES); + *eof = 1; + memset((void *) workarea, 0x00, AP_DEVICES * sizeof(unsigned int)); + return len; +} + +static void zcrypt_disable_card(int index) +{ + struct zcrypt_device *zdev; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) + if (AP_QID_DEVICE(zdev->ap_dev->qid) == index) { + zdev->online = 0; + ap_flush_queue(zdev->ap_dev); + break; + } + spin_unlock_bh(&zcrypt_device_lock); +} + +static void zcrypt_enable_card(int index) +{ + struct zcrypt_device *zdev; + + spin_lock_bh(&zcrypt_device_lock); + list_for_each_entry(zdev, &zcrypt_device_list, list) + if (AP_QID_DEVICE(zdev->ap_dev->qid) == index) { + zdev->online = 1; + break; + } + spin_unlock_bh(&zcrypt_device_lock); +} + +static int zcrypt_status_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + unsigned char *lbuf, *ptr; + unsigned long local_count; + int j; + + if (count <= 0) + return 0; + +#define LBUFSIZE 1200UL + lbuf = kmalloc(LBUFSIZE, GFP_KERNEL); + if (!lbuf) { + PRINTK("kmalloc failed!\n"); + return 0; + } + + local_count = min(LBUFSIZE - 1, count); + if (copy_from_user(lbuf, buffer, local_count) != 0) { + kfree(lbuf); + return -EFAULT; + } + lbuf[local_count] = '\0'; + + ptr = strstr(lbuf, "Online devices"); + if (!ptr) { + PRINTK("Unable to parse data (missing \"Online devices\")\n"); + goto out; + } + ptr = strstr(ptr, "\n"); + if (!ptr) { + PRINTK("Unable to parse data (missing newline " + "after \"Online devices\")\n"); + goto out; + } + ptr++; + + if (strstr(ptr, "Waiting work element counts") == NULL) { + PRINTK("Unable to parse data (missing " + "\"Waiting work element counts\")\n"); + goto out; + } + + for (j = 0; j < 64 && *ptr; ptr++) { + /** + * '0' for no device, '1' for PCICA, '2' for PCICC, + * '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3, + * '5' for CEX2C and '6' for CEX2A' + */ + if (*ptr >= '0' && *ptr <= '6') + j++; + else if (*ptr == 'd' || *ptr == 'D') + zcrypt_disable_card(j++); + else if (*ptr == 'e' || *ptr == 'E') + zcrypt_enable_card(j++); + else if (*ptr != ' ' && *ptr != '\t') + break; + } +out: + kfree(lbuf); + return count; +} + +/** + * The module initialization code. + */ +int __init zcrypt_api_init(void) +{ + int rc; + + /* Register the request sprayer. */ + rc = misc_register(&zcrypt_misc_device); + if (rc < 0) { + PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n", + zcrypt_misc_device.minor, rc); + goto out; + } + + /* Set up the proc file system */ + zcrypt_entry = create_proc_entry("driver/z90crypt", 0644, NULL); + if (!zcrypt_entry) { + PRINTK("Couldn't create z90crypt proc entry\n"); + rc = -ENOMEM; + goto out_misc; + } + zcrypt_entry->nlink = 1; + zcrypt_entry->data = NULL; + zcrypt_entry->read_proc = zcrypt_status_read; + zcrypt_entry->write_proc = zcrypt_status_write; + + return 0; + +out_misc: + misc_deregister(&zcrypt_misc_device); +out: + return rc; +} + +/** + * The module termination code. + */ +void zcrypt_api_exit(void) +{ + remove_proc_entry("driver/z90crypt", NULL); + misc_deregister(&zcrypt_misc_device); +} + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +module_init(zcrypt_api_init); +module_exit(zcrypt_api_exit); +#endif diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h new file mode 100644 index 0000000..de4877e --- /dev/null +++ b/drivers/s390/crypto/zcrypt_api.h @@ -0,0 +1,141 @@ +/* + * linux/drivers/s390/crypto/zcrypt_api.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * Cornelia Huck <cornelia.huck@de.ibm.com> + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_API_H_ +#define _ZCRYPT_API_H_ + +/** + * Macro definitions + * + * PDEBUG debugs in the form "zcrypt: function_name -> message" + * + * PRINTK is like PDEBUG, except that it is always enabled + * PRINTKN is like PRINTK, except that it does not include the function name + * PRINTKW is like PRINTK, except that it uses KERN_WARNING + * PRINTKC is like PRINTK, except that it uses KERN_CRIT + */ +#define DEV_NAME "zcrypt" + +#define PRINTK(fmt, args...) \ + printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) +#define PRINTKN(fmt, args...) \ + printk(KERN_DEBUG DEV_NAME ": " fmt, ## args) +#define PRINTKW(fmt, args...) \ + printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) +#define PRINTKC(fmt, args...) \ + printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) + +#ifdef ZCRYPT_DEBUG +#define PDEBUG(fmt, args...) \ + printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args) +#else +#define PDEBUG(fmt, args...) do {} while (0) +#endif + +#include "ap_bus.h" +#include <asm/zcrypt.h> + +/* deprecated status calls */ +#define ICAZ90STATUS _IOR(ZCRYPT_IOCTL_MAGIC, 0x10, struct ica_z90_status) +#define Z90STAT_PCIXCCCOUNT _IOR(ZCRYPT_IOCTL_MAGIC, 0x43, int) + +/** + * This structure is deprecated and the corresponding ioctl() has been + * replaced with individual ioctl()s for each piece of data! + */ +struct ica_z90_status { + int totalcount; + int leedslitecount; // PCICA + int leeds2count; // PCICC + // int PCIXCCCount; is not in struct for backward compatibility + int requestqWaitCount; + int pendingqWaitCount; + int totalOpenCount; + int cryptoDomain; + // status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3, + // 5=CEX2C + unsigned char status[64]; + // qdepth: # work elements waiting for each device + unsigned char qdepth[64]; +}; + +/** + * device type for an actual device is either PCICA, PCICC, PCIXCC_MCL2, + * PCIXCC_MCL3, CEX2C, or CEX2A + * + * NOTE: PCIXCC_MCL3 refers to a PCIXCC with May 2004 version of Licensed + * Internal Code (LIC) (EC J12220 level 29). + * PCIXCC_MCL2 refers to any LIC before this level. + */ +#define ZCRYPT_PCICA 1 +#define ZCRYPT_PCICC 2 +#define ZCRYPT_PCIXCC_MCL2 3 +#define ZCRYPT_PCIXCC_MCL3 4 +#define ZCRYPT_CEX2C 5 +#define ZCRYPT_CEX2A 6 + +struct zcrypt_device; + +struct zcrypt_ops { + long (*rsa_modexpo)(struct zcrypt_device *, struct ica_rsa_modexpo *); + long (*rsa_modexpo_crt)(struct zcrypt_device *, + struct ica_rsa_modexpo_crt *); + long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *); +}; + +struct zcrypt_device { + struct list_head list; /* Device list. */ + spinlock_t lock; /* Per device lock. */ + struct kref refcount; /* device refcounting */ + struct ap_device *ap_dev; /* The "real" ap device. */ + struct zcrypt_ops *ops; /* Crypto operations. */ + int online; /* User online/offline */ + + int user_space_type; /* User space device id. */ + char *type_string; /* User space device name. */ + int min_mod_size; /* Min number of bits. */ + int max_mod_size; /* Max number of bits. */ + int short_crt; /* Card has crt length restriction. */ + int speed_rating; /* Speed of the crypto device. */ + + int request_count; /* # current requests. */ + + struct ap_message reply; /* Per-device reply structure. */ +}; + +struct zcrypt_device *zcrypt_device_alloc(size_t); +void zcrypt_device_free(struct zcrypt_device *); +void zcrypt_device_get(struct zcrypt_device *); +int zcrypt_device_put(struct zcrypt_device *); +int zcrypt_device_register(struct zcrypt_device *); +void zcrypt_device_unregister(struct zcrypt_device *); +int zcrypt_api_init(void); +void zcrypt_api_exit(void); + +#endif /* _ZCRYPT_API_H_ */ diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h new file mode 100644 index 0000000..8dbcf0e --- /dev/null +++ b/drivers/s390/crypto/zcrypt_cca_key.h @@ -0,0 +1,350 @@ +/* + * linux/drivers/s390/crypto/zcrypt_cca_key.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_CCA_KEY_H_ +#define _ZCRYPT_CCA_KEY_H_ + +struct T6_keyBlock_hdr { + unsigned short blen; + unsigned short ulen; + unsigned short flags; +}; + +/** + * mapping for the cca private ME key token. + * Three parts of interest here: the header, the private section and + * the public section. + * + * mapping for the cca key token header + */ +struct cca_token_hdr { + unsigned char token_identifier; + unsigned char version; + unsigned short token_length; + unsigned char reserved[4]; +} __attribute__((packed)); + +#define CCA_TKN_HDR_ID_EXT 0x1E + +/** + * mapping for the cca private ME section + */ +struct cca_private_ext_ME_sec { + unsigned char section_identifier; + unsigned char version; + unsigned short section_length; + unsigned char private_key_hash[20]; + unsigned char reserved1[4]; + unsigned char key_format; + unsigned char reserved2; + unsigned char key_name_hash[20]; + unsigned char key_use_flags[4]; + unsigned char reserved3[6]; + unsigned char reserved4[24]; + unsigned char confounder[24]; + unsigned char exponent[128]; + unsigned char modulus[128]; +} __attribute__((packed)); + +#define CCA_PVT_USAGE_ALL 0x80 + +/** + * mapping for the cca public section + * In a private key, the modulus doesn't appear in the public + * section. So, an arbitrary public exponent of 0x010001 will be + * used, for a section length of 0x0F always. + */ +struct cca_public_sec { + unsigned char section_identifier; + unsigned char version; + unsigned short section_length; + unsigned char reserved[2]; + unsigned short exponent_len; + unsigned short modulus_bit_len; + unsigned short modulus_byte_len; /* In a private key, this is 0 */ +} __attribute__((packed)); + +/** + * mapping for the cca private CRT key 'token' + * The first three parts (the only parts considered in this release) + * are: the header, the private section and the public section. + * The header and public section are the same as for the + * struct cca_private_ext_ME + * + * Following the structure are the quantities p, q, dp, dq, u, pad, + * and modulus, in that order, where pad_len is the modulo 8 + * complement of the residue modulo 8 of the sum of + * (p_len + q_len + dp_len + dq_len + u_len). + */ +struct cca_pvt_ext_CRT_sec { + unsigned char section_identifier; + unsigned char version; + unsigned short section_length; + unsigned char private_key_hash[20]; + unsigned char reserved1[4]; + unsigned char key_format; + unsigned char reserved2; + unsigned char key_name_hash[20]; + unsigned char key_use_flags[4]; + unsigned short p_len; + unsigned short q_len; + unsigned short dp_len; + unsigned short dq_len; + unsigned short u_len; + unsigned short mod_len; + unsigned char reserved3[4]; + unsigned short pad_len; + unsigned char reserved4[52]; + unsigned char confounder[8]; +} __attribute__((packed)); + +#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08 +#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40 + +/** + * Set up private key fields of a type6 MEX message. + * Note that all numerics in the key token are big-endian, + * while the entries in the key block header are little-endian. + * + * @mex: pointer to user input data + * @p: pointer to memory area for the key + * + * Returns the size of the key area or -EFAULT + */ +static inline int zcrypt_type6_mex_key_de(struct ica_rsa_modexpo *mex, + void *p, int big_endian) +{ + static struct cca_token_hdr static_pvt_me_hdr = { + .token_identifier = 0x1E, + .token_length = 0x0183, + }; + static struct cca_private_ext_ME_sec static_pvt_me_sec = { + .section_identifier = 0x02, + .section_length = 0x016C, + .key_use_flags = {0x80,0x00,0x00,0x00}, + }; + static struct cca_public_sec static_pub_me_sec = { + .section_identifier = 0x04, + .section_length = 0x000F, + .exponent_len = 0x0003, + }; + static char pk_exponent[3] = { 0x01, 0x00, 0x01 }; + struct { + struct T6_keyBlock_hdr t6_hdr; + struct cca_token_hdr pvtMeHdr; + struct cca_private_ext_ME_sec pvtMeSec; + struct cca_public_sec pubMeSec; + char exponent[3]; + } __attribute__((packed)) *key = p; + unsigned char *temp; + + memset(key, 0, sizeof(*key)); + + if (big_endian) { + key->t6_hdr.blen = cpu_to_be16(0x189); + key->t6_hdr.ulen = cpu_to_be16(0x189 - 2); + } else { + key->t6_hdr.blen = cpu_to_le16(0x189); + key->t6_hdr.ulen = cpu_to_le16(0x189 - 2); + } + key->pvtMeHdr = static_pvt_me_hdr; + key->pvtMeSec = static_pvt_me_sec; + key->pubMeSec = static_pub_me_sec; + /** + * In a private key, the modulus doesn't appear in the public + * section. So, an arbitrary public exponent of 0x010001 will be + * used. + */ + memcpy(key->exponent, pk_exponent, 3); + + /* key parameter block */ + temp = key->pvtMeSec.exponent + + sizeof(key->pvtMeSec.exponent) - mex->inputdatalength; + if (copy_from_user(temp, mex->b_key, mex->inputdatalength)) + return -EFAULT; + + /* modulus */ + temp = key->pvtMeSec.modulus + + sizeof(key->pvtMeSec.modulus) - mex->inputdatalength; + if (copy_from_user(temp, mex->n_modulus, mex->inputdatalength)) + return -EFAULT; + key->pubMeSec.modulus_bit_len = 8 * mex->inputdatalength; + return sizeof(*key); +} + +/** + * Set up private key fields of a type6 MEX message. The _pad variant + * strips leading zeroes from the b_key. + * Note that all numerics in the key token are big-endian, + * while the entries in the key block header are little-endian. + * + * @mex: pointer to user input data + * @p: pointer to memory area for the key + * + * Returns the size of the key area or -EFAULT + */ +static inline int zcrypt_type6_mex_key_en(struct ica_rsa_modexpo *mex, + void *p, int big_endian) +{ + static struct cca_token_hdr static_pub_hdr = { + .token_identifier = 0x1E, + }; + static struct cca_public_sec static_pub_sec = { + .section_identifier = 0x04, + }; + struct { + struct T6_keyBlock_hdr t6_hdr; + struct cca_token_hdr pubHdr; + struct cca_public_sec pubSec; + char exponent[0]; + } __attribute__((packed)) *key = p; + unsigned char *temp; + int i; + + memset(key, 0, sizeof(*key)); + + key->pubHdr = static_pub_hdr; + key->pubSec = static_pub_sec; + + /* key parameter block */ + temp = key->exponent; + if (copy_from_user(temp, mex->b_key, mex->inputdatalength)) + return -EFAULT; + /* Strip leading zeroes from b_key. */ + for (i = 0; i < mex->inputdatalength; i++) + if (temp[i]) + break; + if (i >= mex->inputdatalength) + return -EINVAL; + memmove(temp, temp + i, mex->inputdatalength - i); + temp += mex->inputdatalength - i; + /* modulus */ + if (copy_from_user(temp, mex->n_modulus, mex->inputdatalength)) + return -EFAULT; + + key->pubSec.modulus_bit_len = 8 * mex->inputdatalength; + key->pubSec.modulus_byte_len = mex->inputdatalength; + key->pubSec.exponent_len = mex->inputdatalength - i; + key->pubSec.section_length = sizeof(key->pubSec) + + 2*mex->inputdatalength - i; + key->pubHdr.token_length = + key->pubSec.section_length + sizeof(key->pubHdr); + if (big_endian) { + key->t6_hdr.ulen = cpu_to_be16(key->pubHdr.token_length + 4); + key->t6_hdr.blen = cpu_to_be16(key->pubHdr.token_length + 6); + } else { + key->t6_hdr.ulen = cpu_to_le16(key->pubHdr.token_length + 4); + key->t6_hdr.blen = cpu_to_le16(key->pubHdr.token_length + 6); + } + return sizeof(*key) + 2*mex->inputdatalength - i; +} + +/** + * Set up private key fields of a type6 CRT message. + * Note that all numerics in the key token are big-endian, + * while the entries in the key block header are little-endian. + * + * @mex: pointer to user input data + * @p: pointer to memory area for the key + * + * Returns the size of the key area or -EFAULT + */ +static inline int zcrypt_type6_crt_key(struct ica_rsa_modexpo_crt *crt, + void *p, int big_endian) +{ + static struct cca_public_sec static_cca_pub_sec = { + .section_identifier = 4, + .section_length = 0x000f, + .exponent_len = 0x0003, + }; + static char pk_exponent[3] = { 0x01, 0x00, 0x01 }; + struct { + struct T6_keyBlock_hdr t6_hdr; + struct cca_token_hdr token; + struct cca_pvt_ext_CRT_sec pvt; + char key_parts[0]; + } __attribute__((packed)) *key = p; + struct cca_public_sec *pub; + int short_len, long_len, pad_len, key_len, size; + + memset(key, 0, sizeof(*key)); + + short_len = crt->inputdatalength / 2; + long_len = short_len + 8; + pad_len = -(3*long_len + 2*short_len) & 7; + key_len = 3*long_len + 2*short_len + pad_len + crt->inputdatalength; + size = sizeof(*key) + key_len + sizeof(*pub) + 3; + + /* parameter block.key block */ + if (big_endian) { + key->t6_hdr.blen = cpu_to_be16(size); + key->t6_hdr.ulen = cpu_to_be16(size - 2); + } else { + key->t6_hdr.blen = cpu_to_le16(size); + key->t6_hdr.ulen = cpu_to_le16(size - 2); + } + + /* key token header */ + key->token.token_identifier = CCA_TKN_HDR_ID_EXT; + key->token.token_length = size - 6; + + /* private section */ + key->pvt.section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT; + key->pvt.section_length = sizeof(key->pvt) + key_len; + key->pvt.key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL; + key->pvt.key_use_flags[0] = CCA_PVT_USAGE_ALL; + key->pvt.p_len = key->pvt.dp_len = key->pvt.u_len = long_len; + key->pvt.q_len = key->pvt.dq_len = short_len; + key->pvt.mod_len = crt->inputdatalength; + key->pvt.pad_len = pad_len; + + /* key parts */ + if (copy_from_user(key->key_parts, crt->np_prime, long_len) || + copy_from_user(key->key_parts + long_len, + crt->nq_prime, short_len) || + copy_from_user(key->key_parts + long_len + short_len, + crt->bp_key, long_len) || + copy_from_user(key->key_parts + 2*long_len + short_len, + crt->bq_key, short_len) || + copy_from_user(key->key_parts + 2*long_len + 2*short_len, + crt->u_mult_inv, long_len)) + return -EFAULT; + memset(key->key_parts + 3*long_len + 2*short_len + pad_len, + 0xff, crt->inputdatalength); + pub = (struct cca_public_sec *)(key->key_parts + key_len); + *pub = static_cca_pub_sec; + pub->modulus_bit_len = 8 * crt->inputdatalength; + /** + * In a private key, the modulus doesn't appear in the public + * section. So, an arbitrary public exponent of 0x010001 will be + * used. + */ + memcpy((char *) (pub + 1), pk_exponent, 3); + return size; +} + +#endif /* _ZCRYPT_CCA_KEY_H_ */ diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c new file mode 100644 index 0000000..a62b000 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -0,0 +1,435 @@ +/* + * linux/drivers/s390/crypto/zcrypt_cex2a.c + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> + +#include "ap_bus.h" +#include "zcrypt_api.h" +#include "zcrypt_error.h" +#include "zcrypt_cex2a.h" + +#define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */ +#define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */ + +#define CEX2A_SPEED_RATING 970 + +#define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */ +#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ + +#define CEX2A_CLEANUP_TIME (15*HZ) + +static struct ap_device_id zcrypt_cex2a_ids[] = { + { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) }, + { /* end of list */ }, +}; + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, " + "Copyright 2001, 2006 IBM Corporation"); +MODULE_LICENSE("GPL"); +#endif + +static int zcrypt_cex2a_probe(struct ap_device *ap_dev); +static void zcrypt_cex2a_remove(struct ap_device *ap_dev); +static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *, + struct ap_message *); + +static struct ap_driver zcrypt_cex2a_driver = { + .probe = zcrypt_cex2a_probe, + .remove = zcrypt_cex2a_remove, + .receive = zcrypt_cex2a_receive, + .ids = zcrypt_cex2a_ids, +}; + +/** + * Convert a ICAMEX message to a type50 MEX message. + * + * @zdev: crypto device pointer + * @zreq: crypto request pointer + * @mex: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo *mex) +{ + unsigned char *mod, *exp, *inp; + int mod_len; + + mod_len = mex->inputdatalength; + + if (mod_len <= 128) { + struct type50_meb1_msg *meb1 = ap_msg->message; + memset(meb1, 0, sizeof(*meb1)); + ap_msg->length = sizeof(*meb1); + meb1->header.msg_type_code = TYPE50_TYPE_CODE; + meb1->header.msg_len = sizeof(*meb1); + meb1->keyblock_type = TYPE50_MEB1_FMT; + mod = meb1->modulus + sizeof(meb1->modulus) - mod_len; + exp = meb1->exponent + sizeof(meb1->exponent) - mod_len; + inp = meb1->message + sizeof(meb1->message) - mod_len; + } else { + struct type50_meb2_msg *meb2 = ap_msg->message; + memset(meb2, 0, sizeof(*meb2)); + ap_msg->length = sizeof(*meb2); + meb2->header.msg_type_code = TYPE50_TYPE_CODE; + meb2->header.msg_len = sizeof(*meb2); + meb2->keyblock_type = TYPE50_MEB2_FMT; + mod = meb2->modulus + sizeof(meb2->modulus) - mod_len; + exp = meb2->exponent + sizeof(meb2->exponent) - mod_len; + inp = meb2->message + sizeof(meb2->message) - mod_len; + } + + if (copy_from_user(mod, mex->n_modulus, mod_len) || + copy_from_user(exp, mex->b_key, mod_len) || + copy_from_user(inp, mex->inputdata, mod_len)) + return -EFAULT; + return 0; +} + +/** + * Convert a ICACRT message to a type50 CRT message. + * + * @zdev: crypto device pointer + * @zreq: crypto request pointer + * @crt: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo_crt *crt) +{ + int mod_len, short_len, long_len, long_offset; + unsigned char *p, *q, *dp, *dq, *u, *inp; + + mod_len = crt->inputdatalength; + short_len = mod_len / 2; + long_len = mod_len / 2 + 8; + + /* + * CEX2A cannot handle p, dp, or U > 128 bytes. + * If we have one of these, we need to do extra checking. + */ + if (long_len > 128) { + /* + * zcrypt_rsa_crt already checked for the leading + * zeroes of np_prime, bp_key and u_mult_inc. + */ + long_offset = long_len - 128; + long_len = 128; + } else + long_offset = 0; + + /* + * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use + * the larger message structure. + */ + if (long_len <= 64) { + struct type50_crb1_msg *crb1 = ap_msg->message; + memset(crb1, 0, sizeof(*crb1)); + ap_msg->length = sizeof(*crb1); + crb1->header.msg_type_code = TYPE50_TYPE_CODE; + crb1->header.msg_len = sizeof(*crb1); + crb1->keyblock_type = TYPE50_CRB1_FMT; + p = crb1->p + sizeof(crb1->p) - long_len; + q = crb1->q + sizeof(crb1->q) - short_len; + dp = crb1->dp + sizeof(crb1->dp) - long_len; + dq = crb1->dq + sizeof(crb1->dq) - short_len; + u = crb1->u + sizeof(crb1->u) - long_len; + inp = crb1->message + sizeof(crb1->message) - mod_len; + } else { + struct type50_crb2_msg *crb2 = ap_msg->message; + memset(crb2, 0, sizeof(*crb2)); + ap_msg->length = sizeof(*crb2); + crb2->header.msg_type_code = TYPE50_TYPE_CODE; + crb2->header.msg_len = sizeof(*crb2); + crb2->keyblock_type = TYPE50_CRB2_FMT; + p = crb2->p + sizeof(crb2->p) - long_len; + q = crb2->q + sizeof(crb2->q) - short_len; + dp = crb2->dp + sizeof(crb2->dp) - long_len; + dq = crb2->dq + sizeof(crb2->dq) - short_len; + u = crb2->u + sizeof(crb2->u) - long_len; + inp = crb2->message + sizeof(crb2->message) - mod_len; + } + + if (copy_from_user(p, crt->np_prime + long_offset, long_len) || + copy_from_user(q, crt->nq_prime, short_len) || + copy_from_user(dp, crt->bp_key + long_offset, long_len) || + copy_from_user(dq, crt->bq_key, short_len) || + copy_from_user(u, crt->u_mult_inv + long_offset, long_len) || + copy_from_user(inp, crt->inputdata, mod_len)) + return -EFAULT; + + + return 0; +} + +/** + * Copy results from a type 80 reply message back to user space. + * + * @zdev: crypto device pointer + * @reply: reply AP message. + * @data: pointer to user output data + * @length: size of user output data + * + * Returns 0 on success or -EFAULT. + */ +static int convert_type80(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + struct type80_hdr *t80h = reply->message; + unsigned char *data; + + if (t80h->len < sizeof(*t80h) + outputdatalength) { + /* The result is too short, the CEX2A card may not do that.. */ + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } + BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE); + data = reply->message + t80h->len - outputdatalength; + if (copy_to_user(outputdata, data, outputdatalength)) + return -EFAULT; + return 0; +} + +static int convert_response(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + /* Response type byte is the second byte in the response. */ + switch (((unsigned char *) reply->message)[1]) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + return convert_error(zdev, reply); + case TYPE80_RSP_CODE: + return convert_type80(zdev, reply, + outputdata, outputdatalength); + default: /* Unknown response type, this should NEVER EVER happen */ + PRINTK("Unrecognized Message Header: %08x%08x\n", + *(unsigned int *) reply->message, + *(unsigned int *) (reply->message+4)); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } +} + +/** + * This function is called from the AP bus code after a crypto request + * "msg" has finished with the reply message "reply". + * It is called from tasklet context. + * @ap_dev: pointer to the AP device + * @msg: pointer to the AP message + * @reply: pointer to the AP reply message + */ +static void zcrypt_cex2a_receive(struct ap_device *ap_dev, + struct ap_message *msg, + struct ap_message *reply) +{ + static struct error_hdr error_reply = { + .type = TYPE82_RSP_CODE, + .reply_code = REP82_ERROR_MACHINE_FAILURE, + }; + struct type80_hdr *t80h = reply->message; + int length; + + /* Copy the reply message to the request message buffer. */ + if (IS_ERR(reply)) + memcpy(msg->message, &error_reply, sizeof(error_reply)); + else if (t80h->type == TYPE80_RSP_CODE) { + length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len); + memcpy(msg->message, reply->message, length); + } else + memcpy(msg->message, reply->message, sizeof error_reply); + complete((struct completion *) msg->private); +} + +static atomic_t zcrypt_step = ATOMIC_INIT(0); + +/** + * The request distributor calls this function if it picked the CEX2A + * device to handle a modexpo request. + * @zdev: pointer to zcrypt_device structure that identifies the + * CEX2A device to the request distributor + * @mex: pointer to the modexpo request buffer + */ +static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, + struct ica_rsa_modexpo *mex) +{ + struct ap_message ap_msg; + struct completion work; + int rc; + + ap_msg.message = (void *) kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &work; + rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex); + if (rc) + goto out_free; + init_completion(&work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &work, CEX2A_CLEANUP_TIME); + if (rc > 0) + rc = convert_response(zdev, &ap_msg, mex->outputdata, + mex->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + kfree(ap_msg.message); + return rc; +} + +/** + * The request distributor calls this function if it picked the CEX2A + * device to handle a modexpo_crt request. + * @zdev: pointer to zcrypt_device structure that identifies the + * CEX2A device to the request distributor + * @crt: pointer to the modexpoc_crt request buffer + */ +static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, + struct ica_rsa_modexpo_crt *crt) +{ + struct ap_message ap_msg; + struct completion work; + int rc; + + ap_msg.message = (void *) kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &work; + rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt); + if (rc) + goto out_free; + init_completion(&work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &work, CEX2A_CLEANUP_TIME); + if (rc > 0) + rc = convert_response(zdev, &ap_msg, crt->outputdata, + crt->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + kfree(ap_msg.message); + return rc; +} + +/** + * The crypto operations for a CEX2A card. + */ +static struct zcrypt_ops zcrypt_cex2a_ops = { + .rsa_modexpo = zcrypt_cex2a_modexpo, + .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt, +}; + +/** + * Probe function for CEX2A cards. It always accepts the AP device + * since the bus_match already checked the hardware type. + * @ap_dev: pointer to the AP device. + */ +static int zcrypt_cex2a_probe(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev; + int rc; + + zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->ap_dev = ap_dev; + zdev->ops = &zcrypt_cex2a_ops; + zdev->online = 1; + zdev->user_space_type = ZCRYPT_CEX2A; + zdev->type_string = "CEX2A"; + zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; + zdev->short_crt = 1; + zdev->speed_rating = CEX2A_SPEED_RATING; + ap_dev->reply = &zdev->reply; + ap_dev->private = zdev; + rc = zcrypt_device_register(zdev); + if (rc) + goto out_free; + return 0; + +out_free: + ap_dev->private = NULL; + zcrypt_device_free(zdev); + return rc; +} + +/** + * This is called to remove the extended CEX2A driver information + * if an AP device is removed. + */ +static void zcrypt_cex2a_remove(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev = ap_dev->private; + + zcrypt_device_unregister(zdev); +} + +int __init zcrypt_cex2a_init(void) +{ + return ap_driver_register(&zcrypt_cex2a_driver, THIS_MODULE, "cex2a"); +} + +void __exit zcrypt_cex2a_exit(void) +{ + ap_driver_unregister(&zcrypt_cex2a_driver); +} + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +module_init(zcrypt_cex2a_init); +module_exit(zcrypt_cex2a_exit); +#endif diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h new file mode 100644 index 0000000..8f69d1d --- /dev/null +++ b/drivers/s390/crypto/zcrypt_cex2a.h @@ -0,0 +1,126 @@ +/* + * linux/drivers/s390/crypto/zcrypt_cex2a.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_CEX2A_H_ +#define _ZCRYPT_CEX2A_H_ + +/** + * The type 50 message family is associated with a CEX2A card. + * + * The four members of the family are described below. + * + * Note that all unsigned char arrays are right-justified and left-padded + * with zeroes. + * + * Note that all reserved fields must be zeroes. + */ +struct type50_hdr { + unsigned char reserved1; + unsigned char msg_type_code; /* 0x50 */ + unsigned short msg_len; + unsigned char reserved2; + unsigned char ignored; + unsigned short reserved3; +} __attribute__((packed)); + +#define TYPE50_TYPE_CODE 0x50 + +#define TYPE50_MEB1_FMT 0x0001 +#define TYPE50_MEB2_FMT 0x0002 +#define TYPE50_CRB1_FMT 0x0011 +#define TYPE50_CRB2_FMT 0x0012 + +/* Mod-Exp, with a small modulus */ +struct type50_meb1_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0001 */ + unsigned char reserved[6]; + unsigned char exponent[128]; + unsigned char modulus[128]; + unsigned char message[128]; +} __attribute__((packed)); + +/* Mod-Exp, with a large modulus */ +struct type50_meb2_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0002 */ + unsigned char reserved[6]; + unsigned char exponent[256]; + unsigned char modulus[256]; + unsigned char message[256]; +} __attribute__((packed)); + +/* CRT, with a small modulus */ +struct type50_crb1_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0011 */ + unsigned char reserved[6]; + unsigned char p[64]; + unsigned char q[64]; + unsigned char dp[64]; + unsigned char dq[64]; + unsigned char u[64]; + unsigned char message[128]; +} __attribute__((packed)); + +/* CRT, with a large modulus */ +struct type50_crb2_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0012 */ + unsigned char reserved[6]; + unsigned char p[128]; + unsigned char q[128]; + unsigned char dp[128]; + unsigned char dq[128]; + unsigned char u[128]; + unsigned char message[256]; +} __attribute__((packed)); + +/** + * The type 80 response family is associated with a CEX2A card. + * + * Note that all unsigned char arrays are right-justified and left-padded + * with zeroes. + * + * Note that all reserved fields must be zeroes. + */ + +#define TYPE80_RSP_CODE 0x80 + +struct type80_hdr { + unsigned char reserved1; + unsigned char type; /* 0x80 */ + unsigned short len; + unsigned char code; /* 0x00 */ + unsigned char reserved2[3]; + unsigned char reserved3[8]; +} __attribute__((packed)); + +int zcrypt_cex2a_init(void); +void zcrypt_cex2a_exit(void); + +#endif /* _ZCRYPT_CEX2A_H_ */ diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h new file mode 100644 index 0000000..2cb616b --- /dev/null +++ b/drivers/s390/crypto/zcrypt_error.h @@ -0,0 +1,133 @@ +/* + * linux/drivers/s390/crypto/zcrypt_error.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_ERROR_H_ +#define _ZCRYPT_ERROR_H_ + +#include "zcrypt_api.h" + +/** + * Reply Messages + * + * Error reply messages are of two types: + * 82: Error (see below) + * 88: Error (see below) + * Both type 82 and type 88 have the same structure in the header. + * + * Request reply messages are of three known types: + * 80: Reply from a Type 50 Request (see CEX2A-RELATED STRUCTS) + * 84: Reply from a Type 4 Request (see PCICA-RELATED STRUCTS) + * 86: Reply from a Type 6 Request (see PCICC/PCIXCC/CEX2C-RELATED STRUCTS) + * + */ +struct error_hdr { + unsigned char reserved1; /* 0x00 */ + unsigned char type; /* 0x82 or 0x88 */ + unsigned char reserved2[2]; /* 0x0000 */ + unsigned char reply_code; /* reply code */ + unsigned char reserved3[3]; /* 0x000000 */ +}; + +#define TYPE82_RSP_CODE 0x82 +#define TYPE88_RSP_CODE 0x88 + +#define REP82_ERROR_MACHINE_FAILURE 0x10 +#define REP82_ERROR_PREEMPT_FAILURE 0x12 +#define REP82_ERROR_CHECKPT_FAILURE 0x14 +#define REP82_ERROR_MESSAGE_TYPE 0x20 +#define REP82_ERROR_INVALID_COMM_CD 0x21 /* Type 84 */ +#define REP82_ERROR_INVALID_MSG_LEN 0x23 +#define REP82_ERROR_RESERVD_FIELD 0x24 /* was 0x50 */ +#define REP82_ERROR_FORMAT_FIELD 0x29 +#define REP82_ERROR_INVALID_COMMAND 0x30 +#define REP82_ERROR_MALFORMED_MSG 0x40 +#define REP82_ERROR_RESERVED_FIELDO 0x50 /* old value */ +#define REP82_ERROR_WORD_ALIGNMENT 0x60 +#define REP82_ERROR_MESSAGE_LENGTH 0x80 +#define REP82_ERROR_OPERAND_INVALID 0x82 +#define REP82_ERROR_OPERAND_SIZE 0x84 +#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85 +#define REP82_ERROR_RESERVED_FIELD 0x88 +#define REP82_ERROR_TRANSPORT_FAIL 0x90 +#define REP82_ERROR_PACKET_TRUNCATED 0xA0 +#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0 + +#define REP88_ERROR_MODULE_FAILURE 0x10 + +#define REP88_ERROR_MESSAGE_TYPE 0x20 +#define REP88_ERROR_MESSAGE_MALFORMD 0x22 +#define REP88_ERROR_MESSAGE_LENGTH 0x23 +#define REP88_ERROR_RESERVED_FIELD 0x24 +#define REP88_ERROR_KEY_TYPE 0x34 +#define REP88_ERROR_INVALID_KEY 0x82 /* CEX2A */ +#define REP88_ERROR_OPERAND 0x84 /* CEX2A */ +#define REP88_ERROR_OPERAND_EVEN_MOD 0x85 /* CEX2A */ + +static inline int convert_error(struct zcrypt_device *zdev, + struct ap_message *reply) +{ + struct error_hdr *ehdr = reply->message; + + PRINTK("Hardware error : Type %02x Message Header: %08x%08x\n", + ehdr->type, *(unsigned int *) reply->message, + *(unsigned int *) (reply->message + 4)); + + switch (ehdr->reply_code) { + case REP82_ERROR_OPERAND_INVALID: + case REP82_ERROR_OPERAND_SIZE: + case REP82_ERROR_EVEN_MOD_IN_OPND: + case REP88_ERROR_MESSAGE_MALFORMD: + // REP88_ERROR_INVALID_KEY // '82' CEX2A + // REP88_ERROR_OPERAND // '84' CEX2A + // REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A + /* Invalid input data. */ + return -EINVAL; + case REP82_ERROR_MESSAGE_TYPE: + // REP88_ERROR_MESSAGE_TYPE // '20' CEX2A + /** + * To sent a message of the wrong type is a bug in the + * device driver. Warn about it, disable the device + * and then repeat the request. + */ + WARN_ON(1); + zdev->online = 0; + return -EAGAIN; + case REP82_ERROR_TRANSPORT_FAIL: + case REP82_ERROR_MACHINE_FAILURE: + // REP88_ERROR_MODULE_FAILURE // '10' CEX2A + /* If a card fails disable it and repeat the request. */ + zdev->online = 0; + return -EAGAIN; + default: + PRINTKW("unknown type %02x reply code = %d\n", + ehdr->type, ehdr->reply_code); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } +} + +#endif /* _ZCRYPT_ERROR_H_ */ diff --git a/drivers/s390/crypto/zcrypt_mono.c b/drivers/s390/crypto/zcrypt_mono.c new file mode 100644 index 0000000..2a9349a --- /dev/null +++ b/drivers/s390/crypto/zcrypt_mono.c @@ -0,0 +1,100 @@ +/* + * linux/drivers/s390/crypto/zcrypt_mono.c + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/compat.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> + +#include "ap_bus.h" +#include "zcrypt_api.h" +#include "zcrypt_pcica.h" +#include "zcrypt_pcicc.h" +#include "zcrypt_pcixcc.h" +#include "zcrypt_cex2a.h" + +/** + * The module initialization code. + */ +int __init zcrypt_init(void) +{ + int rc; + + rc = ap_module_init(); + if (rc) + goto out; + rc = zcrypt_api_init(); + if (rc) + goto out_ap; + rc = zcrypt_pcica_init(); + if (rc) + goto out_api; + rc = zcrypt_pcicc_init(); + if (rc) + goto out_pcica; + rc = zcrypt_pcixcc_init(); + if (rc) + goto out_pcicc; + rc = zcrypt_cex2a_init(); + if (rc) + goto out_pcixcc; + return 0; + +out_pcixcc: + zcrypt_pcixcc_exit(); +out_pcicc: + zcrypt_pcicc_exit(); +out_pcica: + zcrypt_pcica_exit(); +out_api: + zcrypt_api_exit(); +out_ap: + ap_module_exit(); +out: + return rc; +} + +/** + * The module termination code. + */ +void __exit zcrypt_exit(void) +{ + zcrypt_cex2a_exit(); + zcrypt_pcixcc_exit(); + zcrypt_pcicc_exit(); + zcrypt_pcica_exit(); + zcrypt_api_exit(); + ap_module_exit(); +} + +module_init(zcrypt_init); +module_exit(zcrypt_exit); diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c new file mode 100644 index 0000000..b6a4ecd --- /dev/null +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -0,0 +1,418 @@ +/* + * linux/drivers/s390/crypto/zcrypt_pcica.c + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> + +#include "ap_bus.h" +#include "zcrypt_api.h" +#include "zcrypt_error.h" +#include "zcrypt_pcica.h" + +#define PCICA_MIN_MOD_SIZE 1 /* 8 bits */ +#define PCICA_MAX_MOD_SIZE 256 /* 2048 bits */ + +#define PCICA_SPEED_RATING 2800 + +#define PCICA_MAX_MESSAGE_SIZE 0x3a0 /* sizeof(struct type4_lcr) */ +#define PCICA_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ + +#define PCICA_CLEANUP_TIME (15*HZ) + +static struct ap_device_id zcrypt_pcica_ids[] = { + { AP_DEVICE(AP_DEVICE_TYPE_PCICA) }, + { /* end of list */ }, +}; + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +MODULE_DEVICE_TABLE(ap, zcrypt_pcica_ids); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("PCICA Cryptographic Coprocessor device driver, " + "Copyright 2001, 2006 IBM Corporation"); +MODULE_LICENSE("GPL"); +#endif + +static int zcrypt_pcica_probe(struct ap_device *ap_dev); +static void zcrypt_pcica_remove(struct ap_device *ap_dev); +static void zcrypt_pcica_receive(struct ap_device *, struct ap_message *, + struct ap_message *); + +static struct ap_driver zcrypt_pcica_driver = { + .probe = zcrypt_pcica_probe, + .remove = zcrypt_pcica_remove, + .receive = zcrypt_pcica_receive, + .ids = zcrypt_pcica_ids, +}; + +/** + * Convert a ICAMEX message to a type4 MEX message. + * + * @zdev: crypto device pointer + * @zreq: crypto request pointer + * @mex: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICAMEX_msg_to_type4MEX_msg(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo *mex) +{ + unsigned char *modulus, *exponent, *message; + int mod_len; + + mod_len = mex->inputdatalength; + + if (mod_len <= 128) { + struct type4_sme *sme = ap_msg->message; + memset(sme, 0, sizeof(*sme)); + ap_msg->length = sizeof(*sme); + sme->header.msg_fmt = TYPE4_SME_FMT; + sme->header.msg_len = sizeof(*sme); + sme->header.msg_type_code = TYPE4_TYPE_CODE; + sme->header.request_code = TYPE4_REQU_CODE; + modulus = sme->modulus + sizeof(sme->modulus) - mod_len; + exponent = sme->exponent + sizeof(sme->exponent) - mod_len; + message = sme->message + sizeof(sme->message) - mod_len; + } else { + struct type4_lme *lme = ap_msg->message; + memset(lme, 0, sizeof(*lme)); + ap_msg->length = sizeof(*lme); + lme->header.msg_fmt = TYPE4_LME_FMT; + lme->header.msg_len = sizeof(*lme); + lme->header.msg_type_code = TYPE4_TYPE_CODE; + lme->header.request_code = TYPE4_REQU_CODE; + modulus = lme->modulus + sizeof(lme->modulus) - mod_len; + exponent = lme->exponent + sizeof(lme->exponent) - mod_len; + message = lme->message + sizeof(lme->message) - mod_len; + } + + if (copy_from_user(modulus, mex->n_modulus, mod_len) || + copy_from_user(exponent, mex->b_key, mod_len) || + copy_from_user(message, mex->inputdata, mod_len)) + return -EFAULT; + return 0; +} + +/** + * Convert a ICACRT message to a type4 CRT message. + * + * @zdev: crypto device pointer + * @zreq: crypto request pointer + * @crt: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICACRT_msg_to_type4CRT_msg(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo_crt *crt) +{ + unsigned char *p, *q, *dp, *dq, *u, *inp; + int mod_len, short_len, long_len; + + mod_len = crt->inputdatalength; + short_len = mod_len / 2; + long_len = mod_len / 2 + 8; + + if (mod_len <= 128) { + struct type4_scr *scr = ap_msg->message; + memset(scr, 0, sizeof(*scr)); + ap_msg->length = sizeof(*scr); + scr->header.msg_type_code = TYPE4_TYPE_CODE; + scr->header.request_code = TYPE4_REQU_CODE; + scr->header.msg_fmt = TYPE4_SCR_FMT; + scr->header.msg_len = sizeof(*scr); + p = scr->p + sizeof(scr->p) - long_len; + q = scr->q + sizeof(scr->q) - short_len; + dp = scr->dp + sizeof(scr->dp) - long_len; + dq = scr->dq + sizeof(scr->dq) - short_len; + u = scr->u + sizeof(scr->u) - long_len; + inp = scr->message + sizeof(scr->message) - mod_len; + } else { + struct type4_lcr *lcr = ap_msg->message; + memset(lcr, 0, sizeof(*lcr)); + ap_msg->length = sizeof(*lcr); + lcr->header.msg_type_code = TYPE4_TYPE_CODE; + lcr->header.request_code = TYPE4_REQU_CODE; + lcr->header.msg_fmt = TYPE4_LCR_FMT; + lcr->header.msg_len = sizeof(*lcr); + p = lcr->p + sizeof(lcr->p) - long_len; + q = lcr->q + sizeof(lcr->q) - short_len; + dp = lcr->dp + sizeof(lcr->dp) - long_len; + dq = lcr->dq + sizeof(lcr->dq) - short_len; + u = lcr->u + sizeof(lcr->u) - long_len; + inp = lcr->message + sizeof(lcr->message) - mod_len; + } + + if (copy_from_user(p, crt->np_prime, long_len) || + copy_from_user(q, crt->nq_prime, short_len) || + copy_from_user(dp, crt->bp_key, long_len) || + copy_from_user(dq, crt->bq_key, short_len) || + copy_from_user(u, crt->u_mult_inv, long_len) || + copy_from_user(inp, crt->inputdata, mod_len)) + return -EFAULT; + return 0; +} + +/** + * Copy results from a type 84 reply message back to user space. + * + * @zdev: crypto device pointer + * @reply: reply AP message. + * @data: pointer to user output data + * @length: size of user output data + * + * Returns 0 on success or -EFAULT. + */ +static inline int convert_type84(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + struct type84_hdr *t84h = reply->message; + char *data; + + if (t84h->len < sizeof(*t84h) + outputdatalength) { + /* The result is too short, the PCICA card may not do that.. */ + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } + BUG_ON(t84h->len > PCICA_MAX_RESPONSE_SIZE); + data = reply->message + t84h->len - outputdatalength; + if (copy_to_user(outputdata, data, outputdatalength)) + return -EFAULT; + return 0; +} + +static int convert_response(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + /* Response type byte is the second byte in the response. */ + switch (((unsigned char *) reply->message)[1]) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + return convert_error(zdev, reply); + case TYPE84_RSP_CODE: + return convert_type84(zdev, reply, + outputdata, outputdatalength); + default: /* Unknown response type, this should NEVER EVER happen */ + PRINTK("Unrecognized Message Header: %08x%08x\n", + *(unsigned int *) reply->message, + *(unsigned int *) (reply->message+4)); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } +} + +/** + * This function is called from the AP bus code after a crypto request + * "msg" has finished with the reply message "reply". + * It is called from tasklet context. + * @ap_dev: pointer to the AP device + * @msg: pointer to the AP message + * @reply: pointer to the AP reply message + */ +static void zcrypt_pcica_receive(struct ap_device *ap_dev, + struct ap_message *msg, + struct ap_message *reply) +{ + static struct error_hdr error_reply = { + .type = TYPE82_RSP_CODE, + .reply_code = REP82_ERROR_MACHINE_FAILURE, + }; + struct type84_hdr *t84h = reply->message; + int length; + + /* Copy the reply message to the request message buffer. */ + if (IS_ERR(reply)) + memcpy(msg->message, &error_reply, sizeof(error_reply)); + else if (t84h->code == TYPE84_RSP_CODE) { + length = min(PCICA_MAX_RESPONSE_SIZE, (int) t84h->len); + memcpy(msg->message, reply->message, length); + } else + memcpy(msg->message, reply->message, sizeof error_reply); + complete((struct completion *) msg->private); +} + +static atomic_t zcrypt_step = ATOMIC_INIT(0); + +/** + * The request distributor calls this function if it picked the PCICA + * device to handle a modexpo request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCICA device to the request distributor + * @mex: pointer to the modexpo request buffer + */ +static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev, + struct ica_rsa_modexpo *mex) +{ + struct ap_message ap_msg; + struct completion work; + int rc; + + ap_msg.message = (void *) kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &work; + rc = ICAMEX_msg_to_type4MEX_msg(zdev, &ap_msg, mex); + if (rc) + goto out_free; + init_completion(&work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &work, PCICA_CLEANUP_TIME); + if (rc > 0) + rc = convert_response(zdev, &ap_msg, mex->outputdata, + mex->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + kfree(ap_msg.message); + return rc; +} + +/** + * The request distributor calls this function if it picked the PCICA + * device to handle a modexpo_crt request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCICA device to the request distributor + * @crt: pointer to the modexpoc_crt request buffer + */ +static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev, + struct ica_rsa_modexpo_crt *crt) +{ + struct ap_message ap_msg; + struct completion work; + int rc; + + ap_msg.message = (void *) kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &work; + rc = ICACRT_msg_to_type4CRT_msg(zdev, &ap_msg, crt); + if (rc) + goto out_free; + init_completion(&work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &work, PCICA_CLEANUP_TIME); + if (rc > 0) + rc = convert_response(zdev, &ap_msg, crt->outputdata, + crt->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + kfree(ap_msg.message); + return rc; +} + +/** + * The crypto operations for a PCICA card. + */ +static struct zcrypt_ops zcrypt_pcica_ops = { + .rsa_modexpo = zcrypt_pcica_modexpo, + .rsa_modexpo_crt = zcrypt_pcica_modexpo_crt, +}; + +/** + * Probe function for PCICA cards. It always accepts the AP device + * since the bus_match already checked the hardware type. + * @ap_dev: pointer to the AP device. + */ +static int zcrypt_pcica_probe(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev; + int rc; + + zdev = zcrypt_device_alloc(PCICA_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->ap_dev = ap_dev; + zdev->ops = &zcrypt_pcica_ops; + zdev->online = 1; + zdev->user_space_type = ZCRYPT_PCICA; + zdev->type_string = "PCICA"; + zdev->min_mod_size = PCICA_MIN_MOD_SIZE; + zdev->max_mod_size = PCICA_MAX_MOD_SIZE; + zdev->speed_rating = PCICA_SPEED_RATING; + ap_dev->reply = &zdev->reply; + ap_dev->private = zdev; + rc = zcrypt_device_register(zdev); + if (rc) + goto out_free; + return 0; + +out_free: + ap_dev->private = NULL; + zcrypt_device_free(zdev); + return rc; +} + +/** + * This is called to remove the extended PCICA driver information + * if an AP device is removed. + */ +static void zcrypt_pcica_remove(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev = ap_dev->private; + + zcrypt_device_unregister(zdev); +} + +int __init zcrypt_pcica_init(void) +{ + return ap_driver_register(&zcrypt_pcica_driver, THIS_MODULE, "pcica"); +} + +void zcrypt_pcica_exit(void) +{ + ap_driver_unregister(&zcrypt_pcica_driver); +} + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +module_init(zcrypt_pcica_init); +module_exit(zcrypt_pcica_exit); +#endif diff --git a/drivers/s390/crypto/zcrypt_pcica.h b/drivers/s390/crypto/zcrypt_pcica.h new file mode 100644 index 0000000..3be1118 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_pcica.h @@ -0,0 +1,117 @@ +/* + * linux/drivers/s390/crypto/zcrypt_pcica.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_PCICA_H_ +#define _ZCRYPT_PCICA_H_ + +/** + * The type 4 message family is associated with a PCICA card. + * + * The four members of the family are described below. + * + * Note that all unsigned char arrays are right-justified and left-padded + * with zeroes. + * + * Note that all reserved fields must be zeroes. + */ +struct type4_hdr { + unsigned char reserved1; + unsigned char msg_type_code; /* 0x04 */ + unsigned short msg_len; + unsigned char request_code; /* 0x40 */ + unsigned char msg_fmt; + unsigned short reserved2; +} __attribute__((packed)); + +#define TYPE4_TYPE_CODE 0x04 +#define TYPE4_REQU_CODE 0x40 + +#define TYPE4_SME_FMT 0x00 +#define TYPE4_LME_FMT 0x10 +#define TYPE4_SCR_FMT 0x40 +#define TYPE4_LCR_FMT 0x50 + +/* Mod-Exp, with a small modulus */ +struct type4_sme { + struct type4_hdr header; + unsigned char message[128]; + unsigned char exponent[128]; + unsigned char modulus[128]; +} __attribute__((packed)); + +/* Mod-Exp, with a large modulus */ +struct type4_lme { + struct type4_hdr header; + unsigned char message[256]; + unsigned char exponent[256]; + unsigned char modulus[256]; +} __attribute__((packed)); + +/* CRT, with a small modulus */ +struct type4_scr { + struct type4_hdr header; + unsigned char message[128]; + unsigned char dp[72]; + unsigned char dq[64]; + unsigned char p[72]; + unsigned char q[64]; + unsigned char u[72]; +} __attribute__((packed)); + +/* CRT, with a large modulus */ +struct type4_lcr { + struct type4_hdr header; + unsigned char message[256]; + unsigned char dp[136]; + unsigned char dq[128]; + unsigned char p[136]; + unsigned char q[128]; + unsigned char u[136]; +} __attribute__((packed)); + +/** + * The type 84 response family is associated with a PCICA card. + * + * Note that all unsigned char arrays are right-justified and left-padded + * with zeroes. + * + * Note that all reserved fields must be zeroes. + */ + +struct type84_hdr { + unsigned char reserved1; + unsigned char code; + unsigned short len; + unsigned char reserved2[4]; +} __attribute__((packed)); + +#define TYPE84_RSP_CODE 0x84 + +int zcrypt_pcica_init(void); +void zcrypt_pcica_exit(void); + +#endif /* _ZCRYPT_PCICA_H_ */ diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c new file mode 100644 index 0000000..f295a40 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -0,0 +1,630 @@ +/* + * linux/drivers/s390/crypto/zcrypt_pcicc.c + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> + +#include "ap_bus.h" +#include "zcrypt_api.h" +#include "zcrypt_error.h" +#include "zcrypt_pcicc.h" +#include "zcrypt_cca_key.h" + +#define PCICC_MIN_MOD_SIZE 64 /* 512 bits */ +#define PCICC_MAX_MOD_SIZE_OLD 128 /* 1024 bits */ +#define PCICC_MAX_MOD_SIZE 256 /* 2048 bits */ + +/** + * PCICC cards need a speed rating of 0. This keeps them at the end of + * the zcrypt device list (see zcrypt_api.c). PCICC cards are only + * used if no other cards are present because they are slow and can only + * cope with PKCS12 padded requests. The logic is queer. PKCS11 padded + * requests are rejected. The modexpo function encrypts PKCS12 padded data + * and decrypts any non-PKCS12 padded data (except PKCS11) in the assumption + * that it's encrypted PKCS12 data. The modexpo_crt function always decrypts + * the data in the assumption that its PKCS12 encrypted data. + */ +#define PCICC_SPEED_RATING 0 + +#define PCICC_MAX_MESSAGE_SIZE 0x710 /* max size type6 v1 crt message */ +#define PCICC_MAX_RESPONSE_SIZE 0x710 /* max size type86 v1 reply */ + +#define PCICC_CLEANUP_TIME (15*HZ) + +static struct ap_device_id zcrypt_pcicc_ids[] = { + { AP_DEVICE(AP_DEVICE_TYPE_PCICC) }, + { /* end of list */ }, +}; + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +MODULE_DEVICE_TABLE(ap, zcrypt_pcicc_ids); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("PCICC Cryptographic Coprocessor device driver, " + "Copyright 2001, 2006 IBM Corporation"); +MODULE_LICENSE("GPL"); +#endif + +static int zcrypt_pcicc_probe(struct ap_device *ap_dev); +static void zcrypt_pcicc_remove(struct ap_device *ap_dev); +static void zcrypt_pcicc_receive(struct ap_device *, struct ap_message *, + struct ap_message *); + +static struct ap_driver zcrypt_pcicc_driver = { + .probe = zcrypt_pcicc_probe, + .remove = zcrypt_pcicc_remove, + .receive = zcrypt_pcicc_receive, + .ids = zcrypt_pcicc_ids, +}; + +/** + * The following is used to initialize the CPRB passed to the PCICC card + * in a type6 message. The 3 fields that must be filled in at execution + * time are req_parml, rpl_parml and usage_domain. Note that all three + * fields are *little*-endian. Actually, everything about this interface + * is ascii/little-endian, since the device has 'Intel inside'. + * + * The CPRB is followed immediately by the parm block. + * The parm block contains: + * - function code ('PD' 0x5044 or 'PK' 0x504B) + * - rule block (0x0A00 'PKCS-1.2' or 0x0A00 'ZERO-PAD') + * - VUD block + */ +static struct CPRB static_cprb = { + .cprb_len = __constant_cpu_to_le16(0x0070), + .cprb_ver_id = 0x41, + .func_id = {0x54,0x32}, + .checkpoint_flag= 0x01, + .svr_namel = __constant_cpu_to_le16(0x0008), + .svr_name = {'I','C','S','F',' ',' ',' ',' '} +}; + +/** + * Check the message for PKCS11 padding. + */ +static inline int is_PKCS11_padded(unsigned char *buffer, int length) +{ + int i; + if ((buffer[0] != 0x00) || (buffer[1] != 0x01)) + return 0; + for (i = 2; i < length; i++) + if (buffer[i] != 0xFF) + break; + if (i < 10 || i == length) + return 0; + if (buffer[i] != 0x00) + return 0; + return 1; +} + +/** + * Check the message for PKCS12 padding. + */ +static inline int is_PKCS12_padded(unsigned char *buffer, int length) +{ + int i; + if ((buffer[0] != 0x00) || (buffer[1] != 0x02)) + return 0; + for (i = 2; i < length; i++) + if (buffer[i] == 0x00) + break; + if ((i < 10) || (i == length)) + return 0; + if (buffer[i] != 0x00) + return 0; + return 1; +} + +/** + * Convert a ICAMEX message to a type6 MEX message. + * + * @zdev: crypto device pointer + * @zreq: crypto request pointer + * @mex: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICAMEX_msg_to_type6MEX_msg(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo *mex) +{ + static struct type6_hdr static_type6_hdr = { + .type = 0x06, + .offset1 = 0x00000058, + .agent_id = {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50, + 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01}, + .function_code = {'P','K'}, + }; + static struct function_and_rules_block static_pke_function_and_rules ={ + .function_code = {'P','K'}, + .ulen = __constant_cpu_to_le16(10), + .only_rule = {'P','K','C','S','-','1','.','2'} + }; + struct { + struct type6_hdr hdr; + struct CPRB cprb; + struct function_and_rules_block fr; + unsigned short length; + char text[0]; + } __attribute__((packed)) *msg = ap_msg->message; + int vud_len, pad_len, size; + + /* VUD.ciphertext */ + if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength)) + return -EFAULT; + + if (is_PKCS11_padded(msg->text, mex->inputdatalength)) + return -EINVAL; + + /* static message header and f&r */ + msg->hdr = static_type6_hdr; + msg->fr = static_pke_function_and_rules; + + if (is_PKCS12_padded(msg->text, mex->inputdatalength)) { + /* strip the padding and adjust the data length */ + pad_len = strnlen(msg->text + 2, mex->inputdatalength - 2) + 3; + if (pad_len <= 9 || pad_len >= mex->inputdatalength) + return -ENODEV; + vud_len = mex->inputdatalength - pad_len; + memmove(msg->text, msg->text + pad_len, vud_len); + msg->length = cpu_to_le16(vud_len + 2); + + /* Set up key after the variable length text. */ + size = zcrypt_type6_mex_key_en(mex, msg->text + vud_len, 0); + if (size < 0) + return size; + size += sizeof(*msg) + vud_len; /* total size of msg */ + } else { + vud_len = mex->inputdatalength; + msg->length = cpu_to_le16(2 + vud_len); + + msg->hdr.function_code[1] = 'D'; + msg->fr.function_code[1] = 'D'; + + /* Set up key after the variable length text. */ + size = zcrypt_type6_mex_key_de(mex, msg->text + vud_len, 0); + if (size < 0) + return size; + size += sizeof(*msg) + vud_len; /* total size of msg */ + } + + /* message header, cprb and f&r */ + msg->hdr.ToCardLen1 = (size - sizeof(msg->hdr) + 3) & -4; + msg->hdr.FromCardLen1 = PCICC_MAX_RESPONSE_SIZE - sizeof(msg->hdr); + + msg->cprb = static_cprb; + msg->cprb.usage_domain[0]= AP_QID_QUEUE(zdev->ap_dev->qid); + msg->cprb.req_parml = cpu_to_le16(size - sizeof(msg->hdr) - + sizeof(msg->cprb)); + msg->cprb.rpl_parml = cpu_to_le16(msg->hdr.FromCardLen1); + + ap_msg->length = (size + 3) & -4; + return 0; +} + +/** + * Convert a ICACRT message to a type6 CRT message. + * + * @zdev: crypto device pointer + * @zreq: crypto request pointer + * @crt: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICACRT_msg_to_type6CRT_msg(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo_crt *crt) +{ + static struct type6_hdr static_type6_hdr = { + .type = 0x06, + .offset1 = 0x00000058, + .agent_id = {0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50, + 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01}, + .function_code = {'P','D'}, + }; + static struct function_and_rules_block static_pkd_function_and_rules ={ + .function_code = {'P','D'}, + .ulen = __constant_cpu_to_le16(10), + .only_rule = {'P','K','C','S','-','1','.','2'} + }; + struct { + struct type6_hdr hdr; + struct CPRB cprb; + struct function_and_rules_block fr; + unsigned short length; + char text[0]; + } __attribute__((packed)) *msg = ap_msg->message; + int size; + + /* VUD.ciphertext */ + msg->length = cpu_to_le16(2 + crt->inputdatalength); + if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength)) + return -EFAULT; + + if (is_PKCS11_padded(msg->text, crt->inputdatalength)) + return -EINVAL; + + /* Set up key after the variable length text. */ + size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 0); + if (size < 0) + return size; + size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */ + + /* message header, cprb and f&r */ + msg->hdr = static_type6_hdr; + msg->hdr.ToCardLen1 = (size - sizeof(msg->hdr) + 3) & -4; + msg->hdr.FromCardLen1 = PCICC_MAX_RESPONSE_SIZE - sizeof(msg->hdr); + + msg->cprb = static_cprb; + msg->cprb.usage_domain[0] = AP_QID_QUEUE(zdev->ap_dev->qid); + msg->cprb.req_parml = msg->cprb.rpl_parml = + cpu_to_le16(size - sizeof(msg->hdr) - sizeof(msg->cprb)); + + msg->fr = static_pkd_function_and_rules; + + ap_msg->length = (size + 3) & -4; + return 0; +} + +/** + * Copy results from a type 86 reply message back to user space. + * + * @zdev: crypto device pointer + * @reply: reply AP message. + * @data: pointer to user output data + * @length: size of user output data + * + * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error. + */ +struct type86_reply { + struct type86_hdr hdr; + struct type86_fmt2_ext fmt2; + struct CPRB cprb; + unsigned char pad[4]; /* 4 byte function code/rules block ? */ + unsigned short length; + char text[0]; +} __attribute__((packed)); + +static int convert_type86(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + static unsigned char static_pad[] = { + 0x00,0x02, + 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD, + 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57, + 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B, + 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39, + 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5, + 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D, + 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB, + 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F, + 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9, + 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45, + 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9, + 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F, + 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD, + 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D, + 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD, + 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9, + 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B, + 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B, + 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B, + 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD, + 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7, + 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1, + 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3, + 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23, + 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55, + 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43, + 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F, + 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F, + 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5, + 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD, + 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41, + 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09 + }; + struct type86_reply *msg = reply->message; + unsigned short service_rc, service_rs; + unsigned int reply_len, pad_len; + char *data; + + service_rc = le16_to_cpu(msg->cprb.ccp_rtcode); + if (unlikely(service_rc != 0)) { + service_rs = le16_to_cpu(msg->cprb.ccp_rscode); + if (service_rc == 8 && service_rs == 66) { + PDEBUG("Bad block format on PCICC\n"); + return -EINVAL; + } + if (service_rc == 8 && service_rs == 65) { + PDEBUG("Probably an even modulus on PCICC\n"); + return -EINVAL; + } + if (service_rc == 8 && service_rs == 770) { + PDEBUG("Invalid key length on PCICC\n"); + zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD; + return -EAGAIN; + } + if (service_rc == 8 && service_rs == 783) { + PDEBUG("Extended bitlengths not enabled on PCICC\n"); + zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD; + return -EAGAIN; + } + PRINTK("Unknown service rc/rs (PCICC): %d/%d\n", + service_rc, service_rs); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } + data = msg->text; + reply_len = le16_to_cpu(msg->length) - 2; + if (reply_len > outputdatalength) + return -EINVAL; + /** + * For all encipher requests, the length of the ciphertext (reply_len) + * will always equal the modulus length. For MEX decipher requests + * the output needs to get padded. Minimum pad size is 10. + * + * Currently, the cases where padding will be added is for: + * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support + * ZERO-PAD and CRT is only supported for PKD requests) + * - PCICC, always + */ + pad_len = outputdatalength - reply_len; + if (pad_len > 0) { + if (pad_len < 10) + return -EINVAL; + /* 'restore' padding left in the PCICC/PCIXCC card. */ + if (copy_to_user(outputdata, static_pad, pad_len - 1)) + return -EFAULT; + if (put_user(0, outputdata + pad_len - 1)) + return -EFAULT; + } + /* Copy the crypto response to user space. */ + if (copy_to_user(outputdata + pad_len, data, reply_len)) + return -EFAULT; + return 0; +} + +static int convert_response(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + struct type86_reply *msg = reply->message; + + /* Response type byte is the second byte in the response. */ + switch (msg->hdr.type) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + return convert_error(zdev, reply); + case TYPE86_RSP_CODE: + if (msg->hdr.reply_code) + return convert_error(zdev, reply); + if (msg->cprb.cprb_ver_id == 0x01) + return convert_type86(zdev, reply, + outputdata, outputdatalength); + /* no break, incorrect cprb version is an unknown response */ + default: /* Unknown response type, this should NEVER EVER happen */ + PRINTK("Unrecognized Message Header: %08x%08x\n", + *(unsigned int *) reply->message, + *(unsigned int *) (reply->message+4)); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } +} + +/** + * This function is called from the AP bus code after a crypto request + * "msg" has finished with the reply message "reply". + * It is called from tasklet context. + * @ap_dev: pointer to the AP device + * @msg: pointer to the AP message + * @reply: pointer to the AP reply message + */ +static void zcrypt_pcicc_receive(struct ap_device *ap_dev, + struct ap_message *msg, + struct ap_message *reply) +{ + static struct error_hdr error_reply = { + .type = TYPE82_RSP_CODE, + .reply_code = REP82_ERROR_MACHINE_FAILURE, + }; + struct type86_reply *t86r = reply->message; + int length; + + /* Copy the reply message to the request message buffer. */ + if (IS_ERR(reply)) + memcpy(msg->message, &error_reply, sizeof(error_reply)); + else if (t86r->hdr.type == TYPE86_RSP_CODE && + t86r->cprb.cprb_ver_id == 0x01) { + length = sizeof(struct type86_reply) + t86r->length - 2; + length = min(PCICC_MAX_RESPONSE_SIZE, length); + memcpy(msg->message, reply->message, length); + } else + memcpy(msg->message, reply->message, sizeof error_reply); + complete((struct completion *) msg->private); +} + +static atomic_t zcrypt_step = ATOMIC_INIT(0); + +/** + * The request distributor calls this function if it picked the PCICC + * device to handle a modexpo request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCICC device to the request distributor + * @mex: pointer to the modexpo request buffer + */ +static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev, + struct ica_rsa_modexpo *mex) +{ + struct ap_message ap_msg; + struct completion work; + int rc; + + ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.length = PAGE_SIZE; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &work; + rc = ICAMEX_msg_to_type6MEX_msg(zdev, &ap_msg, mex); + if (rc) + goto out_free; + init_completion(&work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &work, PCICC_CLEANUP_TIME); + if (rc > 0) + rc = convert_response(zdev, &ap_msg, mex->outputdata, + mex->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + free_page((unsigned long) ap_msg.message); + return rc; +} + +/** + * The request distributor calls this function if it picked the PCICC + * device to handle a modexpo_crt request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCICC device to the request distributor + * @crt: pointer to the modexpoc_crt request buffer + */ +static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev, + struct ica_rsa_modexpo_crt *crt) +{ + struct ap_message ap_msg; + struct completion work; + int rc; + + ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.length = PAGE_SIZE; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &work; + rc = ICACRT_msg_to_type6CRT_msg(zdev, &ap_msg, crt); + if (rc) + goto out_free; + init_completion(&work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &work, PCICC_CLEANUP_TIME); + if (rc > 0) + rc = convert_response(zdev, &ap_msg, crt->outputdata, + crt->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + free_page((unsigned long) ap_msg.message); + return rc; +} + +/** + * The crypto operations for a PCICC card. + */ +static struct zcrypt_ops zcrypt_pcicc_ops = { + .rsa_modexpo = zcrypt_pcicc_modexpo, + .rsa_modexpo_crt = zcrypt_pcicc_modexpo_crt, +}; + +/** + * Probe function for PCICC cards. It always accepts the AP device + * since the bus_match already checked the hardware type. + * @ap_dev: pointer to the AP device. + */ +static int zcrypt_pcicc_probe(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev; + int rc; + + zdev = zcrypt_device_alloc(PCICC_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->ap_dev = ap_dev; + zdev->ops = &zcrypt_pcicc_ops; + zdev->online = 1; + zdev->user_space_type = ZCRYPT_PCICC; + zdev->type_string = "PCICC"; + zdev->min_mod_size = PCICC_MIN_MOD_SIZE; + zdev->max_mod_size = PCICC_MAX_MOD_SIZE; + zdev->speed_rating = PCICC_SPEED_RATING; + ap_dev->reply = &zdev->reply; + ap_dev->private = zdev; + rc = zcrypt_device_register(zdev); + if (rc) + goto out_free; + return 0; + + out_free: + ap_dev->private = NULL; + zcrypt_device_free(zdev); + return rc; +} + +/** + * This is called to remove the extended PCICC driver information + * if an AP device is removed. + */ +static void zcrypt_pcicc_remove(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev = ap_dev->private; + + zcrypt_device_unregister(zdev); +} + +int __init zcrypt_pcicc_init(void) +{ + return ap_driver_register(&zcrypt_pcicc_driver, THIS_MODULE, "pcicc"); +} + +void zcrypt_pcicc_exit(void) +{ + ap_driver_unregister(&zcrypt_pcicc_driver); +} + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +module_init(zcrypt_pcicc_init); +module_exit(zcrypt_pcicc_exit); +#endif diff --git a/drivers/s390/crypto/zcrypt_pcicc.h b/drivers/s390/crypto/zcrypt_pcicc.h new file mode 100644 index 0000000..6d44548 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_pcicc.h @@ -0,0 +1,176 @@ +/* + * linux/drivers/s390/crypto/zcrypt_pcicc.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_PCICC_H_ +#define _ZCRYPT_PCICC_H_ + +/** + * The type 6 message family is associated with PCICC or PCIXCC cards. + * + * It contains a message header followed by a CPRB, both of which + * are described below. + * + * Note that all reserved fields must be zeroes. + */ +struct type6_hdr { + unsigned char reserved1; /* 0x00 */ + unsigned char type; /* 0x06 */ + unsigned char reserved2[2]; /* 0x0000 */ + unsigned char right[4]; /* 0x00000000 */ + unsigned char reserved3[2]; /* 0x0000 */ + unsigned char reserved4[2]; /* 0x0000 */ + unsigned char apfs[4]; /* 0x00000000 */ + unsigned int offset1; /* 0x00000058 (offset to CPRB) */ + unsigned int offset2; /* 0x00000000 */ + unsigned int offset3; /* 0x00000000 */ + unsigned int offset4; /* 0x00000000 */ + unsigned char agent_id[16]; /* PCICC: */ + /* 0x0100 */ + /* 0x4343412d4150504c202020 */ + /* 0x010101 */ + /* PCIXCC: */ + /* 0x4341000000000000 */ + /* 0x0000000000000000 */ + unsigned char rqid[2]; /* rqid. internal to 603 */ + unsigned char reserved5[2]; /* 0x0000 */ + unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */ + unsigned char reserved6[2]; /* 0x0000 */ + unsigned int ToCardLen1; /* (request CPRB len + 3) & -4 */ + unsigned int ToCardLen2; /* db len 0x00000000 for PKD */ + unsigned int ToCardLen3; /* 0x00000000 */ + unsigned int ToCardLen4; /* 0x00000000 */ + unsigned int FromCardLen1; /* response buffer length */ + unsigned int FromCardLen2; /* db len 0x00000000 for PKD */ + unsigned int FromCardLen3; /* 0x00000000 */ + unsigned int FromCardLen4; /* 0x00000000 */ +} __attribute__((packed)); + +/** + * CPRB + * Note that all shorts, ints and longs are little-endian. + * All pointer fields are 32-bits long, and mean nothing + * + * A request CPRB is followed by a request_parameter_block. + * + * The request (or reply) parameter block is organized thus: + * function code + * VUD block + * key block + */ +struct CPRB { + unsigned short cprb_len; /* CPRB length */ + unsigned char cprb_ver_id; /* CPRB version id. */ + unsigned char pad_000; /* Alignment pad byte. */ + unsigned char srpi_rtcode[4]; /* SRPI return code LELONG */ + unsigned char srpi_verb; /* SRPI verb type */ + unsigned char flags; /* flags */ + unsigned char func_id[2]; /* function id */ + unsigned char checkpoint_flag; /* */ + unsigned char resv2; /* reserved */ + unsigned short req_parml; /* request parameter buffer */ + /* length 16-bit little endian */ + unsigned char req_parmp[4]; /* request parameter buffer * + * pointer (means nothing: the * + * parameter buffer follows * + * the CPRB). */ + unsigned char req_datal[4]; /* request data buffer */ + /* length ULELONG */ + unsigned char req_datap[4]; /* request data buffer */ + /* pointer */ + unsigned short rpl_parml; /* reply parameter buffer */ + /* length 16-bit little endian */ + unsigned char pad_001[2]; /* Alignment pad bytes. ULESHORT */ + unsigned char rpl_parmp[4]; /* reply parameter buffer * + * pointer (means nothing: the * + * parameter buffer follows * + * the CPRB). */ + unsigned char rpl_datal[4]; /* reply data buffer len ULELONG */ + unsigned char rpl_datap[4]; /* reply data buffer */ + /* pointer */ + unsigned short ccp_rscode; /* server reason code ULESHORT */ + unsigned short ccp_rtcode; /* server return code ULESHORT */ + unsigned char repd_parml[2]; /* replied parameter len ULESHORT*/ + unsigned char mac_data_len[2]; /* Mac Data Length ULESHORT */ + unsigned char repd_datal[4]; /* replied data length ULELONG */ + unsigned char req_pc[2]; /* PC identifier */ + unsigned char res_origin[8]; /* resource origin */ + unsigned char mac_value[8]; /* Mac Value */ + unsigned char logon_id[8]; /* Logon Identifier */ + unsigned char usage_domain[2]; /* cdx */ + unsigned char resv3[18]; /* reserved for requestor */ + unsigned short svr_namel; /* server name length ULESHORT */ + unsigned char svr_name[8]; /* server name */ +} __attribute__((packed)); + +/** + * The type 86 message family is associated with PCICC and PCIXCC cards. + * + * It contains a message header followed by a CPRB. The CPRB is + * the same as the request CPRB, which is described above. + * + * If format is 1, an error condition exists and no data beyond + * the 8-byte message header is of interest. + * + * The non-error message is shown below. + * + * Note that all reserved fields must be zeroes. + */ +struct type86_hdr { + unsigned char reserved1; /* 0x00 */ + unsigned char type; /* 0x86 */ + unsigned char format; /* 0x01 (error) or 0x02 (ok) */ + unsigned char reserved2; /* 0x00 */ + unsigned char reply_code; /* reply code (see above) */ + unsigned char reserved3[3]; /* 0x000000 */ +} __attribute__((packed)); + +#define TYPE86_RSP_CODE 0x86 +#define TYPE86_FMT2 0x02 + +struct type86_fmt2_ext { + unsigned char reserved[4]; /* 0x00000000 */ + unsigned char apfs[4]; /* final status */ + unsigned int count1; /* length of CPRB + parameters */ + unsigned int offset1; /* offset to CPRB */ + unsigned int count2; /* 0x00000000 */ + unsigned int offset2; /* db offset 0x00000000 for PKD */ + unsigned int count3; /* 0x00000000 */ + unsigned int offset3; /* 0x00000000 */ + unsigned int count4; /* 0x00000000 */ + unsigned int offset4; /* 0x00000000 */ +} __attribute__((packed)); + +struct function_and_rules_block { + unsigned char function_code[2]; + unsigned short ulen; + unsigned char only_rule[8]; +} __attribute__((packed)); + +int zcrypt_pcicc_init(void); +void zcrypt_pcicc_exit(void); + +#endif /* _ZCRYPT_PCICC_H_ */ diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c new file mode 100644 index 0000000..2da8b93 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -0,0 +1,951 @@ +/* + * linux/drivers/s390/crypto/zcrypt_pcixcc.c + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * Ralph Wuerthner <rwuerthn@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> + +#include "ap_bus.h" +#include "zcrypt_api.h" +#include "zcrypt_error.h" +#include "zcrypt_pcicc.h" +#include "zcrypt_pcixcc.h" +#include "zcrypt_cca_key.h" + +#define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */ +#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */ +#define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */ + +#define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */ +#define PCIXCC_MCL3_SPEED_RATING 7870 +#define CEX2C_SPEED_RATING 8540 + +#define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ +#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ + +#define PCIXCC_MAX_XCRB_MESSAGE_SIZE (12*1024) +#define PCIXCC_MAX_XCRB_RESPONSE_SIZE PCIXCC_MAX_XCRB_MESSAGE_SIZE +#define PCIXCC_MAX_XCRB_DATA_SIZE (11*1024) +#define PCIXCC_MAX_XCRB_REPLY_SIZE (5*1024) + +#define PCIXCC_MAX_RESPONSE_SIZE PCIXCC_MAX_XCRB_RESPONSE_SIZE + +#define PCIXCC_CLEANUP_TIME (15*HZ) + +#define CEIL4(x) ((((x)+3)/4)*4) + +struct response_type { + struct completion work; + int type; +}; +#define PCIXCC_RESPONSE_TYPE_ICA 0 +#define PCIXCC_RESPONSE_TYPE_XCRB 1 + +static struct ap_device_id zcrypt_pcixcc_ids[] = { + { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) }, + { /* end of list */ }, +}; + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, " + "Copyright 2001, 2006 IBM Corporation"); +MODULE_LICENSE("GPL"); +#endif + +static int zcrypt_pcixcc_probe(struct ap_device *ap_dev); +static void zcrypt_pcixcc_remove(struct ap_device *ap_dev); +static void zcrypt_pcixcc_receive(struct ap_device *, struct ap_message *, + struct ap_message *); + +static struct ap_driver zcrypt_pcixcc_driver = { + .probe = zcrypt_pcixcc_probe, + .remove = zcrypt_pcixcc_remove, + .receive = zcrypt_pcixcc_receive, + .ids = zcrypt_pcixcc_ids, +}; + +/** + * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C + * card in a type6 message. The 3 fields that must be filled in at execution + * time are req_parml, rpl_parml and usage_domain. + * Everything about this interface is ascii/big-endian, since the + * device does *not* have 'Intel inside'. + * + * The CPRBX is followed immediately by the parm block. + * The parm block contains: + * - function code ('PD' 0x5044 or 'PK' 0x504B) + * - rule block (one of:) + * + 0x000A 'PKCS-1.2' (MCL2 'PD') + * + 0x000A 'ZERO-PAD' (MCL2 'PK') + * + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD') + * + 0x000A 'MRP ' (MCL3 'PK' or CEX2C 'PK') + * - VUD block + */ +static struct CPRBX static_cprbx = { + .cprb_len = 0x00DC, + .cprb_ver_id = 0x02, + .func_id = {0x54,0x32}, +}; + +/** + * Convert a ICAMEX message to a type6 MEX message. + * + * @zdev: crypto device pointer + * @ap_msg: pointer to AP message + * @mex: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo *mex) +{ + static struct type6_hdr static_type6_hdrX = { + .type = 0x06, + .offset1 = 0x00000058, + .agent_id = {'C','A',}, + .function_code = {'P','K'}, + }; + static struct function_and_rules_block static_pke_fnr = { + .function_code = {'P','K'}, + .ulen = 10, + .only_rule = {'M','R','P',' ',' ',' ',' ',' '} + }; + static struct function_and_rules_block static_pke_fnr_MCL2 = { + .function_code = {'P','K'}, + .ulen = 10, + .only_rule = {'Z','E','R','O','-','P','A','D'} + }; + struct { + struct type6_hdr hdr; + struct CPRBX cprbx; + struct function_and_rules_block fr; + unsigned short length; + char text[0]; + } __attribute__((packed)) *msg = ap_msg->message; + int size; + + /* VUD.ciphertext */ + msg->length = mex->inputdatalength + 2; + if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength)) + return -EFAULT; + + /* Set up key which is located after the variable length text. */ + size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1); + if (size < 0) + return size; + size += sizeof(*msg) + mex->inputdatalength; + + /* message header, cprbx and f&r */ + msg->hdr = static_type6_hdrX; + msg->hdr.ToCardLen1 = size - sizeof(msg->hdr); + msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr); + + msg->cprbx = static_cprbx; + msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid); + msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1; + + msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ? + static_pke_fnr_MCL2 : static_pke_fnr; + + msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx); + + ap_msg->length = size; + return 0; +} + +/** + * Convert a ICACRT message to a type6 CRT message. + * + * @zdev: crypto device pointer + * @ap_msg: pointer to AP message + * @crt: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_rsa_modexpo_crt *crt) +{ + static struct type6_hdr static_type6_hdrX = { + .type = 0x06, + .offset1 = 0x00000058, + .agent_id = {'C','A',}, + .function_code = {'P','D'}, + }; + static struct function_and_rules_block static_pkd_fnr = { + .function_code = {'P','D'}, + .ulen = 10, + .only_rule = {'Z','E','R','O','-','P','A','D'} + }; + + static struct function_and_rules_block static_pkd_fnr_MCL2 = { + .function_code = {'P','D'}, + .ulen = 10, + .only_rule = {'P','K','C','S','-','1','.','2'} + }; + struct { + struct type6_hdr hdr; + struct CPRBX cprbx; + struct function_and_rules_block fr; + unsigned short length; + char text[0]; + } __attribute__((packed)) *msg = ap_msg->message; + int size; + + /* VUD.ciphertext */ + msg->length = crt->inputdatalength + 2; + if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength)) + return -EFAULT; + + /* Set up key which is located after the variable length text. */ + size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1); + if (size < 0) + return size; + size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */ + + /* message header, cprbx and f&r */ + msg->hdr = static_type6_hdrX; + msg->hdr.ToCardLen1 = size - sizeof(msg->hdr); + msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr); + + msg->cprbx = static_cprbx; + msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid); + msg->cprbx.req_parml = msg->cprbx.rpl_msgbl = + size - sizeof(msg->hdr) - sizeof(msg->cprbx); + + msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ? + static_pkd_fnr_MCL2 : static_pkd_fnr; + + ap_msg->length = size; + return 0; +} + +/** + * Convert a XCRB message to a type6 CPRB message. + * + * @zdev: crypto device pointer + * @ap_msg: pointer to AP message + * @xcRB: pointer to user input data + * + * Returns 0 on success or -EFAULT. + */ +struct type86_fmt2_msg { + struct type86_hdr hdr; + struct type86_fmt2_ext fmt2; +} __attribute__((packed)); + +static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, + struct ap_message *ap_msg, + struct ica_xcRB *xcRB) +{ + static struct type6_hdr static_type6_hdrX = { + .type = 0x06, + .offset1 = 0x00000058, + }; + struct { + struct type6_hdr hdr; + struct ica_CPRBX cprbx; + } __attribute__((packed)) *msg = ap_msg->message; + + int rcblen = CEIL4(xcRB->request_control_blk_length); + int replylen; + char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen; + char *function_code; + + /* length checks */ + ap_msg->length = sizeof(struct type6_hdr) + + CEIL4(xcRB->request_control_blk_length) + + xcRB->request_data_length; + if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) { + PRINTK("Combined message is too large (%ld/%d/%d).\n", + sizeof(struct type6_hdr), + xcRB->request_control_blk_length, + xcRB->request_data_length); + return -EFAULT; + } + if (CEIL4(xcRB->reply_control_blk_length) > + PCIXCC_MAX_XCRB_REPLY_SIZE) { + PDEBUG("Reply CPRB length is too large (%d).\n", + xcRB->request_control_blk_length); + return -EFAULT; + } + if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) { + PDEBUG("Reply data block length is too large (%d).\n", + xcRB->reply_data_length); + return -EFAULT; + } + replylen = CEIL4(xcRB->reply_control_blk_length) + + CEIL4(xcRB->reply_data_length) + + sizeof(struct type86_fmt2_msg); + if (replylen > PCIXCC_MAX_XCRB_RESPONSE_SIZE) { + PDEBUG("Reply CPRB + data block > PCIXCC_MAX_XCRB_RESPONSE_SIZE" + " (%d/%d/%d).\n", + sizeof(struct type86_fmt2_msg), + xcRB->reply_control_blk_length, + xcRB->reply_data_length); + xcRB->reply_control_blk_length = PCIXCC_MAX_XCRB_RESPONSE_SIZE - + (sizeof(struct type86_fmt2_msg) + + CEIL4(xcRB->reply_data_length)); + PDEBUG("Capping Reply CPRB length at %d\n", + xcRB->reply_control_blk_length); + } + + /* prepare type6 header */ + msg->hdr = static_type6_hdrX; + memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID)); + msg->hdr.ToCardLen1 = xcRB->request_control_blk_length; + if (xcRB->request_data_length) { + msg->hdr.offset2 = msg->hdr.offset1 + rcblen; + msg->hdr.ToCardLen2 = xcRB->request_data_length; + } + msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length; + msg->hdr.FromCardLen2 = xcRB->reply_data_length; + + /* prepare CPRB */ + if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr, + xcRB->request_control_blk_length)) + return -EFAULT; + if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) > + xcRB->request_control_blk_length) { + PDEBUG("cprb_len too large (%d/%d)\n", msg->cprbx.cprb_len, + xcRB->request_control_blk_length); + return -EFAULT; + } + function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; + memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); + + /* copy data block */ + if (xcRB->request_data_length && + copy_from_user(req_data, xcRB->request_data_address, + xcRB->request_data_length)) + return -EFAULT; + return 0; +} + +/** + * Copy results from a type 86 ICA reply message back to user space. + * + * @zdev: crypto device pointer + * @reply: reply AP message. + * @data: pointer to user output data + * @length: size of user output data + * + * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error. + */ +struct type86x_reply { + struct type86_hdr hdr; + struct type86_fmt2_ext fmt2; + struct CPRBX cprbx; + unsigned char pad[4]; /* 4 byte function code/rules block ? */ + unsigned short length; + char text[0]; +} __attribute__((packed)); + +static int convert_type86_ica(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + static unsigned char static_pad[] = { + 0x00,0x02, + 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD, + 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57, + 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B, + 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39, + 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5, + 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D, + 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB, + 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F, + 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9, + 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45, + 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9, + 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F, + 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD, + 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D, + 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD, + 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9, + 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B, + 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B, + 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B, + 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD, + 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7, + 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1, + 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3, + 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23, + 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55, + 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43, + 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F, + 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F, + 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5, + 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD, + 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41, + 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09 + }; + struct type86x_reply *msg = reply->message; + unsigned short service_rc, service_rs; + unsigned int reply_len, pad_len; + char *data; + + service_rc = msg->cprbx.ccp_rtcode; + if (unlikely(service_rc != 0)) { + service_rs = msg->cprbx.ccp_rscode; + if (service_rc == 8 && service_rs == 66) { + PDEBUG("Bad block format on PCIXCC/CEX2C\n"); + return -EINVAL; + } + if (service_rc == 8 && service_rs == 65) { + PDEBUG("Probably an even modulus on PCIXCC/CEX2C\n"); + return -EINVAL; + } + if (service_rc == 8 && service_rs == 770) { + PDEBUG("Invalid key length on PCIXCC/CEX2C\n"); + zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; + return -EAGAIN; + } + if (service_rc == 8 && service_rs == 783) { + PDEBUG("Extended bitlengths not enabled on PCIXCC/CEX2C\n"); + zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; + return -EAGAIN; + } + PRINTK("Unknown service rc/rs (PCIXCC/CEX2C): %d/%d\n", + service_rc, service_rs); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } + data = msg->text; + reply_len = msg->length - 2; + if (reply_len > outputdatalength) + return -EINVAL; + /** + * For all encipher requests, the length of the ciphertext (reply_len) + * will always equal the modulus length. For MEX decipher requests + * the output needs to get padded. Minimum pad size is 10. + * + * Currently, the cases where padding will be added is for: + * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support + * ZERO-PAD and CRT is only supported for PKD requests) + * - PCICC, always + */ + pad_len = outputdatalength - reply_len; + if (pad_len > 0) { + if (pad_len < 10) + return -EINVAL; + /* 'restore' padding left in the PCICC/PCIXCC card. */ + if (copy_to_user(outputdata, static_pad, pad_len - 1)) + return -EFAULT; + if (put_user(0, outputdata + pad_len - 1)) + return -EFAULT; + } + /* Copy the crypto response to user space. */ + if (copy_to_user(outputdata + pad_len, data, reply_len)) + return -EFAULT; + return 0; +} + +/** + * Copy results from a type 86 XCRB reply message back to user space. + * + * @zdev: crypto device pointer + * @reply: reply AP message. + * @xcRB: pointer to XCRB + * + * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error. + */ +static int convert_type86_xcrb(struct zcrypt_device *zdev, + struct ap_message *reply, + struct ica_xcRB *xcRB) +{ + struct type86_fmt2_msg *msg = reply->message; + char *data = reply->message; + + /* Copy CPRB to user */ + if (copy_to_user(xcRB->reply_control_blk_addr, + data + msg->fmt2.offset1, msg->fmt2.count1)) + return -EFAULT; + xcRB->reply_control_blk_length = msg->fmt2.count1; + + /* Copy data buffer to user */ + if (msg->fmt2.count2) + if (copy_to_user(xcRB->reply_data_addr, + data + msg->fmt2.offset2, msg->fmt2.count2)) + return -EFAULT; + xcRB->reply_data_length = msg->fmt2.count2; + return 0; +} + +static int convert_response_ica(struct zcrypt_device *zdev, + struct ap_message *reply, + char __user *outputdata, + unsigned int outputdatalength) +{ + struct type86x_reply *msg = reply->message; + + /* Response type byte is the second byte in the response. */ + switch (((unsigned char *) reply->message)[1]) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + return convert_error(zdev, reply); + case TYPE86_RSP_CODE: + if (msg->hdr.reply_code) + return convert_error(zdev, reply); + if (msg->cprbx.cprb_ver_id == 0x02) + return convert_type86_ica(zdev, reply, + outputdata, outputdatalength); + /* no break, incorrect cprb version is an unknown response */ + default: /* Unknown response type, this should NEVER EVER happen */ + PRINTK("Unrecognized Message Header: %08x%08x\n", + *(unsigned int *) reply->message, + *(unsigned int *) (reply->message+4)); + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } +} + +static int convert_response_xcrb(struct zcrypt_device *zdev, + struct ap_message *reply, + struct ica_xcRB *xcRB) +{ + struct type86x_reply *msg = reply->message; + + /* Response type byte is the second byte in the response. */ + switch (((unsigned char *) reply->message)[1]) { + case TYPE82_RSP_CODE: + case TYPE88_RSP_CODE: + xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ + return convert_error(zdev, reply); + case TYPE86_RSP_CODE: + if (msg->hdr.reply_code) { + memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32)); + return convert_error(zdev, reply); + } + if (msg->cprbx.cprb_ver_id == 0x02) + return convert_type86_xcrb(zdev, reply, xcRB); + /* no break, incorrect cprb version is an unknown response */ + default: /* Unknown response type, this should NEVER EVER happen */ + PRINTK("Unrecognized Message Header: %08x%08x\n", + *(unsigned int *) reply->message, + *(unsigned int *) (reply->message+4)); + xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ + zdev->online = 0; + return -EAGAIN; /* repeat the request on a different device. */ + } +} + +/** + * This function is called from the AP bus code after a crypto request + * "msg" has finished with the reply message "reply". + * It is called from tasklet context. + * @ap_dev: pointer to the AP device + * @msg: pointer to the AP message + * @reply: pointer to the AP reply message + */ +static void zcrypt_pcixcc_receive(struct ap_device *ap_dev, + struct ap_message *msg, + struct ap_message *reply) +{ + static struct error_hdr error_reply = { + .type = TYPE82_RSP_CODE, + .reply_code = REP82_ERROR_MACHINE_FAILURE, + }; + struct response_type *resp_type = + (struct response_type *) msg->private; + struct type86x_reply *t86r = reply->message; + int length; + + /* Copy the reply message to the request message buffer. */ + if (IS_ERR(reply)) + memcpy(msg->message, &error_reply, sizeof(error_reply)); + else if (t86r->hdr.type == TYPE86_RSP_CODE && + t86r->cprbx.cprb_ver_id == 0x02) { + switch (resp_type->type) { + case PCIXCC_RESPONSE_TYPE_ICA: + length = sizeof(struct type86x_reply) + + t86r->length - 2; + length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length); + memcpy(msg->message, reply->message, length); + break; + case PCIXCC_RESPONSE_TYPE_XCRB: + length = t86r->fmt2.offset2 + t86r->fmt2.count2; + length = min(PCIXCC_MAX_XCRB_RESPONSE_SIZE, length); + memcpy(msg->message, reply->message, length); + break; + default: + PRINTK("Invalid internal response type: %i\n", + resp_type->type); + memcpy(msg->message, &error_reply, + sizeof error_reply); + } + } else + memcpy(msg->message, reply->message, sizeof error_reply); + complete(&(resp_type->work)); +} + +static atomic_t zcrypt_step = ATOMIC_INIT(0); + +/** + * The request distributor calls this function if it picked the PCIXCC/CEX2C + * device to handle a modexpo request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCIXCC/CEX2C device to the request distributor + * @mex: pointer to the modexpo request buffer + */ +static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, + struct ica_rsa_modexpo *mex) +{ + struct ap_message ap_msg; + struct response_type resp_type = { + .type = PCIXCC_RESPONSE_TYPE_ICA, + }; + int rc; + + ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &resp_type; + rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex); + if (rc) + goto out_free; + init_completion(&resp_type.work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &resp_type.work, PCIXCC_CLEANUP_TIME); + if (rc > 0) + rc = convert_response_ica(zdev, &ap_msg, mex->outputdata, + mex->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + free_page((unsigned long) ap_msg.message); + return rc; +} + +/** + * The request distributor calls this function if it picked the PCIXCC/CEX2C + * device to handle a modexpo_crt request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCIXCC/CEX2C device to the request distributor + * @crt: pointer to the modexpoc_crt request buffer + */ +static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, + struct ica_rsa_modexpo_crt *crt) +{ + struct ap_message ap_msg; + struct response_type resp_type = { + .type = PCIXCC_RESPONSE_TYPE_ICA, + }; + int rc; + + ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &resp_type; + rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt); + if (rc) + goto out_free; + init_completion(&resp_type.work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &resp_type.work, PCIXCC_CLEANUP_TIME); + if (rc > 0) + rc = convert_response_ica(zdev, &ap_msg, crt->outputdata, + crt->outputdatalength); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + free_page((unsigned long) ap_msg.message); + return rc; +} + +/** + * The request distributor calls this function if it picked the PCIXCC/CEX2C + * device to handle a send_cprb request. + * @zdev: pointer to zcrypt_device structure that identifies the + * PCIXCC/CEX2C device to the request distributor + * @xcRB: pointer to the send_cprb request buffer + */ +long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, struct ica_xcRB *xcRB) +{ + struct ap_message ap_msg; + struct response_type resp_type = { + .type = PCIXCC_RESPONSE_TYPE_XCRB, + }; + int rc; + + ap_msg.message = (void *) kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL); + if (!ap_msg.message) + return -ENOMEM; + ap_msg.psmid = (((unsigned long long) current->pid) << 32) + + atomic_inc_return(&zcrypt_step); + ap_msg.private = &resp_type; + rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB); + if (rc) + goto out_free; + init_completion(&resp_type.work); + ap_queue_message(zdev->ap_dev, &ap_msg); + rc = wait_for_completion_interruptible_timeout( + &resp_type.work, PCIXCC_CLEANUP_TIME); + if (rc > 0) + rc = convert_response_xcrb(zdev, &ap_msg, xcRB); + else { + /* Signal pending or message timed out. */ + ap_cancel_message(zdev->ap_dev, &ap_msg); + if (rc == 0) + /* Message timed out. */ + rc = -ETIME; + } +out_free: + memset(ap_msg.message, 0x0, ap_msg.length); + kfree(ap_msg.message); + return rc; +} + +/** + * The crypto operations for a PCIXCC/CEX2C card. + */ +static struct zcrypt_ops zcrypt_pcixcc_ops = { + .rsa_modexpo = zcrypt_pcixcc_modexpo, + .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt, + .send_cprb = zcrypt_pcixcc_send_cprb, +}; + +/** + * Micro-code detection function. Its sends a message to a pcixcc card + * to find out the microcode level. + * @ap_dev: pointer to the AP device. + */ +static int zcrypt_pcixcc_mcl(struct ap_device *ap_dev) +{ + static unsigned char msg[] = { + 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00, + 0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8, + 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A, + 0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20, + 0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05, + 0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D, + 0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55, + 0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD, + 0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA, + 0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22, + 0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB, + 0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54, + 0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00, + 0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40, + 0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C, + 0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF, + 0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9, + 0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63, + 0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5, + 0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A, + 0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01, + 0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28, + 0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91, + 0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5, + 0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C, + 0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98, + 0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96, + 0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19, + 0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47, + 0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36, + 0xF1,0x3D,0x93,0x53 + }; + unsigned long long psmid; + struct CPRBX *cprbx; + char *reply; + int rc, i; + + reply = (void *) get_zeroed_page(GFP_KERNEL); + if (!reply) + return -ENOMEM; + + rc = ap_send(ap_dev->qid, 0x0102030405060708ULL, msg, sizeof(msg)); + if (rc) + goto out_free; + + /* Wait for the test message to complete. */ + for (i = 0; i < 6; i++) { + mdelay(300); + rc = ap_recv(ap_dev->qid, &psmid, reply, 4096); + if (rc == 0 && psmid == 0x0102030405060708ULL) + break; + } + + if (i >= 6) { + /* Got no answer. */ + rc = -ENODEV; + goto out_free; + } + + cprbx = (struct CPRBX *) (reply + 48); + if (cprbx->ccp_rtcode == 8 && cprbx->ccp_rscode == 33) + rc = ZCRYPT_PCIXCC_MCL2; + else + rc = ZCRYPT_PCIXCC_MCL3; +out_free: + free_page((unsigned long) reply); + return rc; +} + +/** + * Probe function for PCIXCC/CEX2C cards. It always accepts the AP device + * since the bus_match already checked the hardware type. The PCIXCC + * cards come in two flavours: micro code level 2 and micro code level 3. + * This is checked by sending a test message to the device. + * @ap_dev: pointer to the AP device. + */ +static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev; + int rc; + + zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->ap_dev = ap_dev; + zdev->ops = &zcrypt_pcixcc_ops; + zdev->online = 1; + if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) { + rc = zcrypt_pcixcc_mcl(ap_dev); + if (rc < 0) { + zcrypt_device_free(zdev); + return rc; + } + zdev->user_space_type = rc; + if (rc == ZCRYPT_PCIXCC_MCL2) { + zdev->type_string = "PCIXCC_MCL2"; + zdev->speed_rating = PCIXCC_MCL2_SPEED_RATING; + zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; + zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + } else { + zdev->type_string = "PCIXCC_MCL3"; + zdev->speed_rating = PCIXCC_MCL3_SPEED_RATING; + zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; + zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + } + } else { + zdev->user_space_type = ZCRYPT_CEX2C; + zdev->type_string = "CEX2C"; + zdev->speed_rating = CEX2C_SPEED_RATING; + zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; + zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + } + ap_dev->reply = &zdev->reply; + ap_dev->private = zdev; + rc = zcrypt_device_register(zdev); + if (rc) + goto out_free; + return 0; + + out_free: + ap_dev->private = NULL; + zcrypt_device_free(zdev); + return rc; +} + +/** + * This is called to remove the extended PCIXCC/CEX2C driver information + * if an AP device is removed. + */ +static void zcrypt_pcixcc_remove(struct ap_device *ap_dev) +{ + struct zcrypt_device *zdev = ap_dev->private; + + zcrypt_device_unregister(zdev); +} + +int __init zcrypt_pcixcc_init(void) +{ + return ap_driver_register(&zcrypt_pcixcc_driver, THIS_MODULE, "pcixcc"); +} + +void zcrypt_pcixcc_exit(void) +{ + ap_driver_unregister(&zcrypt_pcixcc_driver); +} + +#ifndef CONFIG_ZCRYPT_MONOLITHIC +module_init(zcrypt_pcixcc_init); +module_exit(zcrypt_pcixcc_exit); +#endif diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_pcixcc.h new file mode 100644 index 0000000..a78ff30 --- /dev/null +++ b/drivers/s390/crypto/zcrypt_pcixcc.h @@ -0,0 +1,79 @@ +/* + * linux/drivers/s390/crypto/zcrypt_pcixcc.h + * + * zcrypt 2.1.0 + * + * Copyright (C) 2001, 2006 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZCRYPT_PCIXCC_H_ +#define _ZCRYPT_PCIXCC_H_ + +/** + * CPRBX + * Note that all shorts and ints are big-endian. + * All pointer fields are 16 bytes long, and mean nothing. + * + * A request CPRB is followed by a request_parameter_block. + * + * The request (or reply) parameter block is organized thus: + * function code + * VUD block + * key block + */ +struct CPRBX { + unsigned short cprb_len; /* CPRB length 220 */ + unsigned char cprb_ver_id; /* CPRB version id. 0x02 */ + unsigned char pad_000[3]; /* Alignment pad bytes */ + unsigned char func_id[2]; /* function id 0x5432 */ + unsigned char cprb_flags[4]; /* Flags */ + unsigned int req_parml; /* request parameter buffer len */ + unsigned int req_datal; /* request data buffer */ + unsigned int rpl_msgbl; /* reply message block length */ + unsigned int rpld_parml; /* replied parameter block len */ + unsigned int rpl_datal; /* reply data block len */ + unsigned int rpld_datal; /* replied data block len */ + unsigned int req_extbl; /* request extension block len */ + unsigned char pad_001[4]; /* reserved */ + unsigned int rpld_extbl; /* replied extension block len */ + unsigned char req_parmb[16]; /* request parm block 'address' */ + unsigned char req_datab[16]; /* request data block 'address' */ + unsigned char rpl_parmb[16]; /* reply parm block 'address' */ + unsigned char rpl_datab[16]; /* reply data block 'address' */ + unsigned char req_extb[16]; /* request extension block 'addr'*/ + unsigned char rpl_extb[16]; /* reply extension block 'addres'*/ + unsigned short ccp_rtcode; /* server return code */ + unsigned short ccp_rscode; /* server reason code */ + unsigned int mac_data_len; /* Mac Data Length */ + unsigned char logon_id[8]; /* Logon Identifier */ + unsigned char mac_value[8]; /* Mac Value */ + unsigned char mac_content_flgs;/* Mac content flag byte */ + unsigned char pad_002; /* Alignment */ + unsigned short domain; /* Domain */ + unsigned char pad_003[12]; /* Domain masks */ + unsigned char pad_004[36]; /* reserved */ +} __attribute__((packed)); + +int zcrypt_pcixcc_init(void); +void zcrypt_pcixcc_exit(void); + +#endif /* _ZCRYPT_PCIXCC_H_ */ diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 5399c5d..a914129 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -19,9 +19,6 @@ #include "s390mach.h" -#define DBG printk -// #define DBG(args,...) do {} while (0); - static struct semaphore m_sem; extern int css_process_crw(int, int); @@ -83,11 +80,11 @@ repeat: ccode = stcrw(&crw[chain]); if (ccode != 0) break; - DBG(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " - "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", - crw[chain].slct, crw[chain].oflw, crw[chain].chn, - crw[chain].rsc, crw[chain].anc, crw[chain].erc, - crw[chain].rsid); + printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", + crw[chain].slct, crw[chain].oflw, crw[chain].chn, + crw[chain].rsc, crw[chain].anc, crw[chain].erc, + crw[chain].rsid); /* Check for overflows. */ if (crw[chain].oflw) { pr_debug("%s: crw overflow detected!\n", __FUNCTION__); @@ -117,8 +114,8 @@ repeat: * reported to the common I/O layer. */ if (crw[chain].slct) { - DBG(KERN_INFO"solicited machine check for " - "channel path %02X\n", crw[0].rsid); + pr_debug("solicited machine check for " + "channel path %02X\n", crw[0].rsid); break; } switch (crw[0].erc) { diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 94d1b74d..7c84b3d 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -543,7 +543,7 @@ do { \ } while (0) #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL -# define ZFCP_LOG_NORMAL(fmt, args...) +# define ZFCP_LOG_NORMAL(fmt, args...) do { } while (0) #else # define ZFCP_LOG_NORMAL(fmt, args...) \ do { \ @@ -553,7 +553,7 @@ do { \ #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO -# define ZFCP_LOG_INFO(fmt, args...) +# define ZFCP_LOG_INFO(fmt, args...) do { } while (0) #else # define ZFCP_LOG_INFO(fmt, args...) \ do { \ @@ -563,14 +563,14 @@ do { \ #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG -# define ZFCP_LOG_DEBUG(fmt, args...) +# define ZFCP_LOG_DEBUG(fmt, args...) do { } while (0) #else # define ZFCP_LOG_DEBUG(fmt, args...) \ ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args) #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE -# define ZFCP_LOG_TRACE(fmt, args...) +# define ZFCP_LOG_TRACE(fmt, args...) do { } while (0) #else # define ZFCP_LOG_TRACE(fmt, args...) \ ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args) diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c index d1c1e75..1e788e8 100644 --- a/drivers/s390/sysinfo.c +++ b/drivers/s390/sysinfo.c @@ -11,19 +11,18 @@ #include <linux/init.h> #include <asm/ebcdic.h> -struct sysinfo_1_1_1 -{ +struct sysinfo_1_1_1 { char reserved_0[32]; char manufacturer[16]; char type[4]; char reserved_1[12]; - char model[16]; + char model_capacity[16]; char sequence[16]; char plant[4]; + char model[16]; }; -struct sysinfo_1_2_1 -{ +struct sysinfo_1_2_1 { char reserved_0[80]; char sequence[16]; char plant[4]; @@ -31,9 +30,12 @@ struct sysinfo_1_2_1 unsigned short cpu_address; }; -struct sysinfo_1_2_2 -{ - char reserved_0[32]; +struct sysinfo_1_2_2 { + char format; + char reserved_0[1]; + unsigned short acc_offset; + char reserved_1[24]; + unsigned int secondary_capability; unsigned int capability; unsigned short cpus_total; unsigned short cpus_configured; @@ -42,8 +44,12 @@ struct sysinfo_1_2_2 unsigned short adjustment[0]; }; -struct sysinfo_2_2_1 -{ +struct sysinfo_1_2_2_extension { + unsigned int alt_capability; + unsigned short alt_adjustment[0]; +}; + +struct sysinfo_2_2_1 { char reserved_0[80]; char sequence[16]; char plant[4]; @@ -51,15 +57,11 @@ struct sysinfo_2_2_1 unsigned short cpu_address; }; -struct sysinfo_2_2_2 -{ +struct sysinfo_2_2_2 { char reserved_0[32]; unsigned short lpar_number; char reserved_1; unsigned char characteristics; - #define LPAR_CHAR_DEDICATED (1 << 7) - #define LPAR_CHAR_SHARED (1 << 6) - #define LPAR_CHAR_LIMITED (1 << 5) unsigned short cpus_total; unsigned short cpus_configured; unsigned short cpus_standby; @@ -71,12 +73,14 @@ struct sysinfo_2_2_2 unsigned short cpus_shared; }; -struct sysinfo_3_2_2 -{ +#define LPAR_CHAR_DEDICATED (1 << 7) +#define LPAR_CHAR_SHARED (1 << 6) +#define LPAR_CHAR_LIMITED (1 << 5) + +struct sysinfo_3_2_2 { char reserved_0[31]; unsigned char count; - struct - { + struct { char reserved_0[4]; unsigned short cpus_total; unsigned short cpus_configured; @@ -90,136 +94,223 @@ struct sysinfo_3_2_2 } vm[8]; }; -union s390_sysinfo +static inline int stsi(void *sysinfo, int fc, int sel1, int sel2) { - struct sysinfo_1_1_1 sysinfo_1_1_1; - struct sysinfo_1_2_1 sysinfo_1_2_1; - struct sysinfo_1_2_2 sysinfo_1_2_2; - struct sysinfo_2_2_1 sysinfo_2_2_1; - struct sysinfo_2_2_2 sysinfo_2_2_2; - struct sysinfo_3_2_2 sysinfo_3_2_2; -}; - -static inline int stsi (void *sysinfo, - int fc, int sel1, int sel2) -{ - int cc, retv; - -#ifndef CONFIG_64BIT - __asm__ __volatile__ ( "lr\t0,%2\n" - "\tlr\t1,%3\n" - "\tstsi\t0(%4)\n" - "0:\tipm\t%0\n" - "\tsrl\t%0,28\n" - "1:lr\t%1,0\n" - ".section .fixup,\"ax\"\n" - "2:\tlhi\t%0,3\n" - "\tbras\t1,3f\n" - "\t.long 1b\n" - "3:\tl\t1,0(1)\n" - "\tbr\t1\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - "\t.align 4\n" - "\t.long 0b,2b\n" - ".previous\n" - : "=d" (cc), "=d" (retv) - : "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo) - : "cc", "memory", "0", "1" ); -#else - __asm__ __volatile__ ( "lr\t0,%2\n" - "lr\t1,%3\n" - "\tstsi\t0(%4)\n" - "0:\tipm\t%0\n" - "\tsrl\t%0,28\n" - "1:lr\t%1,0\n" - ".section .fixup,\"ax\"\n" - "2:\tlhi\t%0,3\n" - "\tjg\t1b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - "\t.align 8\n" - "\t.quad 0b,2b\n" - ".previous\n" - : "=d" (cc), "=d" (retv) - : "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo) - : "cc", "memory", "0", "1" ); -#endif - - return cc? -1 : retv; + register int r0 asm("0") = (fc << 28) | sel1; + register int r1 asm("1") = sel2; + + asm volatile( + " stsi 0(%2)\n" + "0: jz 2f\n" + "1: lhi %0,%3\n" + "2:\n" + EX_TABLE(0b,1b) + : "+d" (r0) : "d" (r1), "a" (sysinfo), "K" (-ENOSYS) + : "cc", "memory" ); + return r0; } -static inline int stsi_0 (void) +static inline int stsi_0(void) { int rc = stsi (NULL, 0, 0, 0); - return rc == -1 ? rc : (((unsigned int)rc) >> 28); + return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28); } -static inline int stsi_1_1_1 (struct sysinfo_1_1_1 *info) +static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len) { - int rc = stsi (info, 1, 1, 1); - if (rc != -1) - { - EBCASC (info->manufacturer, sizeof(info->manufacturer)); - EBCASC (info->type, sizeof(info->type)); - EBCASC (info->model, sizeof(info->model)); - EBCASC (info->sequence, sizeof(info->sequence)); - EBCASC (info->plant, sizeof(info->plant)); - } - return rc == -1 ? rc : 0; + if (stsi(info, 1, 1, 1) == -ENOSYS) + return len; + + EBCASC(info->manufacturer, sizeof(info->manufacturer)); + EBCASC(info->type, sizeof(info->type)); + EBCASC(info->model, sizeof(info->model)); + EBCASC(info->sequence, sizeof(info->sequence)); + EBCASC(info->plant, sizeof(info->plant)); + EBCASC(info->model_capacity, sizeof(info->model_capacity)); + len += sprintf(page + len, "Manufacturer: %-16.16s\n", + info->manufacturer); + len += sprintf(page + len, "Type: %-4.4s\n", + info->type); + if (info->model[0] != '\0') + /* + * Sigh: the model field has been renamed with System z9 + * to model_capacity and a new model field has been added + * after the plant field. To avoid confusing older programs + * the "Model:" prints "model_capacity model" or just + * "model_capacity" if the model string is empty . + */ + len += sprintf(page + len, + "Model: %-16.16s %-16.16s\n", + info->model_capacity, info->model); + else + len += sprintf(page + len, "Model: %-16.16s\n", + info->model_capacity); + len += sprintf(page + len, "Sequence Code: %-16.16s\n", + info->sequence); + len += sprintf(page + len, "Plant: %-4.4s\n", + info->plant); + len += sprintf(page + len, "Model Capacity: %-16.16s\n", + info->model_capacity); + return len; } -static inline int stsi_1_2_1 (struct sysinfo_1_2_1 *info) +#if 0 /* Currently unused */ +static int stsi_1_2_1(struct sysinfo_1_2_1 *info, char *page, int len) { - int rc = stsi (info, 1, 2, 1); - if (rc != -1) - { - EBCASC (info->sequence, sizeof(info->sequence)); - EBCASC (info->plant, sizeof(info->plant)); - } - return rc == -1 ? rc : 0; + if (stsi(info, 1, 2, 1) == -ENOSYS) + return len; + + len += sprintf(page + len, "\n"); + EBCASC(info->sequence, sizeof(info->sequence)); + EBCASC(info->plant, sizeof(info->plant)); + len += sprintf(page + len, "Sequence Code of CPU: %-16.16s\n", + info->sequence); + len += sprintf(page + len, "Plant of CPU: %-16.16s\n", + info->plant); + return len; } +#endif -static inline int stsi_1_2_2 (struct sysinfo_1_2_2 *info) +static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len) { - int rc = stsi (info, 1, 2, 2); - return rc == -1 ? rc : 0; + struct sysinfo_1_2_2_extension *ext; + int i; + + if (stsi(info, 1, 2, 2) == -ENOSYS) + return len; + ext = (struct sysinfo_1_2_2_extension *) + ((unsigned long) info + info->acc_offset); + + len += sprintf(page + len, "\n"); + len += sprintf(page + len, "CPUs Total: %d\n", + info->cpus_total); + len += sprintf(page + len, "CPUs Configured: %d\n", + info->cpus_configured); + len += sprintf(page + len, "CPUs Standby: %d\n", + info->cpus_standby); + len += sprintf(page + len, "CPUs Reserved: %d\n", + info->cpus_reserved); + + if (info->format == 1) { + /* + * Sigh 2. According to the specification the alternate + * capability field is a 32 bit floating point number + * if the higher order 8 bits are not zero. Printing + * a floating point number in the kernel is a no-no, + * always print the number as 32 bit unsigned integer. + * The user-space needs to know about the stange + * encoding of the alternate cpu capability. + */ + len += sprintf(page + len, "Capability: %u %u\n", + info->capability, ext->alt_capability); + for (i = 2; i <= info->cpus_total; i++) + len += sprintf(page + len, + "Adjustment %02d-way: %u %u\n", + i, info->adjustment[i-2], + ext->alt_adjustment[i-2]); + + } else { + len += sprintf(page + len, "Capability: %u\n", + info->capability); + for (i = 2; i <= info->cpus_total; i++) + len += sprintf(page + len, + "Adjustment %02d-way: %u\n", + i, info->adjustment[i-2]); + } + + if (info->secondary_capability != 0) + len += sprintf(page + len, "Secondary Capability: %d\n", + info->secondary_capability); + + return len; } -static inline int stsi_2_2_1 (struct sysinfo_2_2_1 *info) +#if 0 /* Currently unused */ +static int stsi_2_2_1(struct sysinfo_2_2_1 *info, char *page, int len) { - int rc = stsi (info, 2, 2, 1); - if (rc != -1) - { - EBCASC (info->sequence, sizeof(info->sequence)); - EBCASC (info->plant, sizeof(info->plant)); - } - return rc == -1 ? rc : 0; + if (stsi(info, 2, 2, 1) == -ENOSYS) + return len; + + len += sprintf(page + len, "\n"); + EBCASC (info->sequence, sizeof(info->sequence)); + EBCASC (info->plant, sizeof(info->plant)); + len += sprintf(page + len, "Sequence Code of logical CPU: %-16.16s\n", + info->sequence); + len += sprintf(page + len, "Plant of logical CPU: %-16.16s\n", + info->plant); + return len; } +#endif -static inline int stsi_2_2_2 (struct sysinfo_2_2_2 *info) +static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len) { - int rc = stsi (info, 2, 2, 2); - if (rc != -1) - { - EBCASC (info->name, sizeof(info->name)); - } - return rc == -1 ? rc : 0; + if (stsi(info, 2, 2, 2) == -ENOSYS) + return len; + + EBCASC (info->name, sizeof(info->name)); + + len += sprintf(page + len, "\n"); + len += sprintf(page + len, "LPAR Number: %d\n", + info->lpar_number); + + len += sprintf(page + len, "LPAR Characteristics: "); + if (info->characteristics & LPAR_CHAR_DEDICATED) + len += sprintf(page + len, "Dedicated "); + if (info->characteristics & LPAR_CHAR_SHARED) + len += sprintf(page + len, "Shared "); + if (info->characteristics & LPAR_CHAR_LIMITED) + len += sprintf(page + len, "Limited "); + len += sprintf(page + len, "\n"); + + len += sprintf(page + len, "LPAR Name: %-8.8s\n", + info->name); + + len += sprintf(page + len, "LPAR Adjustment: %d\n", + info->caf); + + len += sprintf(page + len, "LPAR CPUs Total: %d\n", + info->cpus_total); + len += sprintf(page + len, "LPAR CPUs Configured: %d\n", + info->cpus_configured); + len += sprintf(page + len, "LPAR CPUs Standby: %d\n", + info->cpus_standby); + len += sprintf(page + len, "LPAR CPUs Reserved: %d\n", + info->cpus_reserved); + len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n", + info->cpus_dedicated); + len += sprintf(page + len, "LPAR CPUs Shared: %d\n", + info->cpus_shared); + return len; } -static inline int stsi_3_2_2 (struct sysinfo_3_2_2 *info) +static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len) { - int rc = stsi (info, 3, 2, 2); - if (rc != -1) - { - int i; - for (i = 0; i < info->count; i++) - { - EBCASC (info->vm[i].name, sizeof(info->vm[i].name)); - EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi)); - } + int i; + + if (stsi(info, 3, 2, 2) == -ENOSYS) + return len; + for (i = 0; i < info->count; i++) { + EBCASC (info->vm[i].name, sizeof(info->vm[i].name)); + EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi)); + len += sprintf(page + len, "\n"); + len += sprintf(page + len, "VM%02d Name: %-8.8s\n", + i, info->vm[i].name); + len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n", + i, info->vm[i].cpi); + + len += sprintf(page + len, "VM%02d Adjustment: %d\n", + i, info->vm[i].caf); + + len += sprintf(page + len, "VM%02d CPUs Total: %d\n", + i, info->vm[i].cpus_total); + len += sprintf(page + len, "VM%02d CPUs Configured: %d\n", + i, info->vm[i].cpus_configured); + len += sprintf(page + len, "VM%02d CPUs Standby: %d\n", + i, info->vm[i].cpus_standby); + len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n", + i, info->vm[i].cpus_reserved); } - return rc == -1 ? rc : 0; + return len; } @@ -227,118 +318,34 @@ static int proc_read_sysinfo(char *page, char **start, off_t off, int count, int *eof, void *data) { - unsigned long info_page = get_zeroed_page (GFP_KERNEL); - union s390_sysinfo *info = (union s390_sysinfo *) info_page; - int len = 0; - int level; - int i; + unsigned long info = get_zeroed_page (GFP_KERNEL); + int level, len; if (!info) return 0; - level = stsi_0 (); - - if (level >= 1 && stsi_1_1_1 (&info->sysinfo_1_1_1) == 0) - { - len += sprintf (page+len, "Manufacturer: %-16.16s\n", - info->sysinfo_1_1_1.manufacturer); - len += sprintf (page+len, "Type: %-4.4s\n", - info->sysinfo_1_1_1.type); - len += sprintf (page+len, "Model: %-16.16s\n", - info->sysinfo_1_1_1.model); - len += sprintf (page+len, "Sequence Code: %-16.16s\n", - info->sysinfo_1_1_1.sequence); - len += sprintf (page+len, "Plant: %-4.4s\n", - info->sysinfo_1_1_1.plant); - } - - if (level >= 1 && stsi_1_2_2 (&info->sysinfo_1_2_2) == 0) - { - len += sprintf (page+len, "\n"); - len += sprintf (page+len, "CPUs Total: %d\n", - info->sysinfo_1_2_2.cpus_total); - len += sprintf (page+len, "CPUs Configured: %d\n", - info->sysinfo_1_2_2.cpus_configured); - len += sprintf (page+len, "CPUs Standby: %d\n", - info->sysinfo_1_2_2.cpus_standby); - len += sprintf (page+len, "CPUs Reserved: %d\n", - info->sysinfo_1_2_2.cpus_reserved); - - len += sprintf (page+len, "Capability: %d\n", - info->sysinfo_1_2_2.capability); + len = 0; + level = stsi_0(); + if (level >= 1) + len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len); - for (i = 2; i <= info->sysinfo_1_2_2.cpus_total; i++) - len += sprintf (page+len, "Adjustment %02d-way: %d\n", - i, info->sysinfo_1_2_2.adjustment[i-2]); - } + if (level >= 1) + len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len); - if (level >= 2 && stsi_2_2_2 (&info->sysinfo_2_2_2) == 0) - { - len += sprintf (page+len, "\n"); - len += sprintf (page+len, "LPAR Number: %d\n", - info->sysinfo_2_2_2.lpar_number); - - len += sprintf (page+len, "LPAR Characteristics: "); - if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_DEDICATED) - len += sprintf (page+len, "Dedicated "); - if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_SHARED) - len += sprintf (page+len, "Shared "); - if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_LIMITED) - len += sprintf (page+len, "Limited "); - len += sprintf (page+len, "\n"); - - len += sprintf (page+len, "LPAR Name: %-8.8s\n", - info->sysinfo_2_2_2.name); - - len += sprintf (page+len, "LPAR Adjustment: %d\n", - info->sysinfo_2_2_2.caf); - - len += sprintf (page+len, "LPAR CPUs Total: %d\n", - info->sysinfo_2_2_2.cpus_total); - len += sprintf (page+len, "LPAR CPUs Configured: %d\n", - info->sysinfo_2_2_2.cpus_configured); - len += sprintf (page+len, "LPAR CPUs Standby: %d\n", - info->sysinfo_2_2_2.cpus_standby); - len += sprintf (page+len, "LPAR CPUs Reserved: %d\n", - info->sysinfo_2_2_2.cpus_reserved); - len += sprintf (page+len, "LPAR CPUs Dedicated: %d\n", - info->sysinfo_2_2_2.cpus_dedicated); - len += sprintf (page+len, "LPAR CPUs Shared: %d\n", - info->sysinfo_2_2_2.cpus_shared); - } + if (level >= 2) + len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len); - if (level >= 3 && stsi_3_2_2 (&info->sysinfo_3_2_2) == 0) - { - for (i = 0; i < info->sysinfo_3_2_2.count; i++) - { - len += sprintf (page+len, "\n"); - len += sprintf (page+len, "VM%02d Name: %-8.8s\n", - i, info->sysinfo_3_2_2.vm[i].name); - len += sprintf (page+len, "VM%02d Control Program: %-16.16s\n", - i, info->sysinfo_3_2_2.vm[i].cpi); - - len += sprintf (page+len, "VM%02d Adjustment: %d\n", - i, info->sysinfo_3_2_2.vm[i].caf); - - len += sprintf (page+len, "VM%02d CPUs Total: %d\n", - i, info->sysinfo_3_2_2.vm[i].cpus_total); - len += sprintf (page+len, "VM%02d CPUs Configured: %d\n", - i, info->sysinfo_3_2_2.vm[i].cpus_configured); - len += sprintf (page+len, "VM%02d CPUs Standby: %d\n", - i, info->sysinfo_3_2_2.vm[i].cpus_standby); - len += sprintf (page+len, "VM%02d CPUs Reserved: %d\n", - i, info->sysinfo_3_2_2.vm[i].cpus_reserved); - } - } + if (level >= 3) + len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len); - free_page (info_page); + free_page (info); return len; } static __init int create_proc_sysinfo(void) { - create_proc_read_entry ("sysinfo", 0444, NULL, - proc_read_sysinfo, NULL); + create_proc_read_entry("sysinfo", 0444, NULL, + proc_read_sysinfo, NULL); return 0; } diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index ed22b96..01b8ac6 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -156,8 +156,8 @@ static void gather_partition_info(void) { struct device_node *rootdn; - char *ppartition_name; - unsigned int *p_number_ptr; + const char *ppartition_name; + const unsigned int *p_number_ptr; /* Retrieve information about this partition */ rootdn = find_path_device("/"); @@ -165,14 +165,11 @@ static void gather_partition_info(void) return; } - ppartition_name = - get_property(rootdn, "ibm,partition-name", NULL); + ppartition_name = get_property(rootdn, "ibm,partition-name", NULL); if (ppartition_name) strncpy(partition_name, ppartition_name, sizeof(partition_name)); - p_number_ptr = - (unsigned int *)get_property(rootdn, "ibm,partition-no", - NULL); + p_number_ptr = get_property(rootdn, "ibm,partition-no", NULL); if (p_number_ptr) partition_number = *p_number_ptr; } diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c index 89ef34d..6422de7 100644 --- a/drivers/scsi/mac53c94.c +++ b/drivers/scsi/mac53c94.c @@ -431,7 +431,7 @@ static int mac53c94_probe(struct macio_dev *mdev, const struct of_device_id *mat struct fsc_state *state; struct Scsi_Host *host; void *dma_cmd_space; - unsigned char *clkprop; + const unsigned char *clkprop; int proplen, rc = -ENODEV; if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) { diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index 5572981..592b52a 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -1850,7 +1850,8 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) { struct device_node *mesh = macio_get_of_node(mdev); struct pci_dev* pdev = macio_get_pci_dev(mdev); - int tgt, *cfp, minper; + int tgt, minper; + const int *cfp; struct mesh_state *ms; struct Scsi_Host *mesh_host; void *dma_cmd_space; @@ -1939,7 +1940,7 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) ms->tgts[tgt].current_req = NULL; } - if ((cfp = (int *) get_property(mesh, "clock-frequency", NULL))) + if ((cfp = get_property(mesh, "clock-frequency", NULL))) ms->clk_freq = *cfp; else { printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n"); diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c index 7d08580..6b70c3c 100644 --- a/drivers/scsi/sata_svw.c +++ b/drivers/scsi/sata_svw.c @@ -268,7 +268,7 @@ static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, /* Match it to a port node */ index = (ap == ap->host_set->ports[0]) ? 0 : 1; for (np = np->child; np != NULL; np = np->sibling) { - u32 *reg = (u32 *)get_property(np, "reg", NULL); + const u32 *reg = get_property(np, "reg", NULL); if (!reg) continue; if (index == *reg) diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index bfd2a22..a3b99ca 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -1400,8 +1400,8 @@ static struct uart_ops pmz_pops = { static int __init pmz_init_port(struct uart_pmac_port *uap) { struct device_node *np = uap->node; - char *conn; - struct slot_names_prop { + const char *conn; + const struct slot_names_prop { int count; char name[1]; } *slots; @@ -1458,7 +1458,7 @@ no_dma: uap->flags |= PMACZILOG_FLAG_IS_IRDA; uap->port_type = PMAC_SCC_ASYNC; /* 1999 Powerbook G3 has slot-names property instead */ - slots = (struct slot_names_prop *)get_property(np, "slot-names", &len); + slots = get_property(np, "slot-names", &len); if (slots && slots->count > 0) { if (strcmp(slots->name, "IrDA") == 0) uap->flags |= PMACZILOG_FLAG_IS_IRDA; @@ -1470,7 +1470,8 @@ no_dma: if (ZS_IS_INTMODEM(uap)) { struct device_node* i2c_modem = find_devices("i2c-modem"); if (i2c_modem) { - char* mid = get_property(i2c_modem, "modem-id", NULL); + const char* mid = + get_property(i2c_modem, "modem-id", NULL); if (mid) switch(*mid) { case 0x04 : case 0x05 : diff --git a/drivers/video/S3triofb.c b/drivers/video/S3triofb.c index afd146f..397005e 100644 --- a/drivers/video/S3triofb.c +++ b/drivers/video/S3triofb.c @@ -349,30 +349,30 @@ static void __init s3triofb_of_init(struct device_node *dp) s3trio_name[sizeof(s3trio_name)-1] = '\0'; strcpy(fb_fix.id, s3trio_name); - if((pp = (int *)get_property(dp, "vendor-id", &len)) != NULL + if((pp = get_property(dp, "vendor-id", &len)) != NULL && *pp!=PCI_VENDOR_ID_S3) { printk("%s: can't find S3 Trio board\n", dp->full_name); return; } - if((pp = (int *)get_property(dp, "device-id", &len)) != NULL + if((pp = get_property(dp, "device-id", &len)) != NULL && *pp!=PCI_DEVICE_ID_S3_TRIO) { printk("%s: can't find S3 Trio board\n", dp->full_name); return; } - if ((pp = (int *)get_property(dp, "depth", &len)) != NULL + if ((pp = get_property(dp, "depth", &len)) != NULL && len == sizeof(int) && *pp != 8) { printk("%s: can't use depth = %d\n", dp->full_name, *pp); return; } - if ((pp = (int *)get_property(dp, "width", &len)) != NULL + if ((pp = get_property(dp, "width", &len)) != NULL && len == sizeof(int)) fb_var.xres = fb_var.xres_virtual = *pp; - if ((pp = (int *)get_property(dp, "height", &len)) != NULL + if ((pp = get_property(dp, "height", &len)) != NULL && len == sizeof(int)) fb_var.yres = fb_var.yres_virtual = *pp; - if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL + if ((pp = get_property(dp, "linebytes", &len)) != NULL && len == sizeof(int)) fb_fix.line_length = *pp; else diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 8e3400d..0ed577e 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -413,11 +413,11 @@ static int __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo) static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo) { struct device_node *dp = rinfo->of_node; - u32 *val; + const u32 *val; if (dp == NULL) return -ENODEV; - val = (u32 *) get_property(dp, "ATY,RefCLK", NULL); + val = get_property(dp, "ATY,RefCLK", NULL); if (!val || !*val) { printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n"); return -EINVAL; @@ -425,11 +425,11 @@ static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo) rinfo->pll.ref_clk = (*val) / 10; - val = (u32 *) get_property(dp, "ATY,SCLK", NULL); + val = get_property(dp, "ATY,SCLK", NULL); if (val && *val) rinfo->pll.sclk = (*val) / 10; - val = (u32 *) get_property(dp, "ATY,MCLK", NULL); + val = get_property(dp, "ATY,MCLK", NULL); if (val && *val) rinfo->pll.mclk = (*val) / 10; diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c index 98c05bc..ea531a6 100644 --- a/drivers/video/aty/radeon_monitor.c +++ b/drivers/video/aty/radeon_monitor.c @@ -64,13 +64,13 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_ { static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", "EDID2", NULL }; - u8 *pedid = NULL; - u8 *pmt = NULL; + const u8 *pedid = NULL; + const u8 *pmt = NULL; u8 *tmp; int i, mt = MT_NONE; RTRACE("analyzing OF properties...\n"); - pmt = (u8 *)get_property(dp, "display-type", NULL); + pmt = get_property(dp, "display-type", NULL); if (!pmt) return MT_NONE; RTRACE("display-type: %s\n", pmt); @@ -89,7 +89,7 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_ } for (i = 0; propnames[i] != NULL; ++i) { - pedid = (u8 *)get_property(dp, propnames[i], NULL); + pedid = get_property(dp, propnames[i], NULL); if (pedid != NULL) break; } @@ -124,14 +124,14 @@ static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_ return MT_NONE; if (rinfo->has_CRTC2) { - char *pname; + const char *pname; int len, second = 0; dp = dp->child; do { if (!dp) return MT_NONE; - pname = (char *)get_property(dp, "name", NULL); + pname = get_property(dp, "name", NULL); if (!pname) return MT_NONE; len = strlen(pname); diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index f31e606..e308ed2 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -1268,7 +1268,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) 0x21320032, 0xa1320032, 0x21320032, 0xffffffff, 0x31320032 }; - u32 *mrtable = default_mrtable; + const u32 *mrtable = default_mrtable; int i, mrtable_size = ARRAY_SIZE(default_mrtable); mdelay(30); @@ -1287,7 +1287,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) if (rinfo->of_node != NULL) { int size; - mrtable = (u32 *)get_property(rinfo->of_node, "ATY,MRT", &size); + mrtable = get_property(rinfo->of_node, "ATY,MRT", &size); if (mrtable) mrtable_size = size >> 2; else diff --git a/drivers/video/nvidia/nv_of.c b/drivers/video/nvidia/nv_of.c index 8209106..d9af88c 100644 --- a/drivers/video/nvidia/nv_of.c +++ b/drivers/video/nvidia/nv_of.c @@ -32,7 +32,7 @@ int nvidia_probe_of_connector(struct fb_info *info, int conn, u8 **out_edid) { struct nvidia_par *par = info->par; struct device_node *parent, *dp; - unsigned char *pedid = NULL; + const unsigned char *pedid = NULL; static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", "EDID,B", "EDID,A", NULL }; @@ -42,20 +42,19 @@ int nvidia_probe_of_connector(struct fb_info *info, int conn, u8 **out_edid) if (parent == NULL) return -1; if (par->twoHeads) { - char *pname; + const char *pname; int len; for (dp = NULL; (dp = of_get_next_child(parent, dp)) != NULL;) { - pname = (char *)get_property(dp, "name", NULL); + pname = get_property(dp, "name", NULL); if (!pname) continue; len = strlen(pname); if ((pname[len-1] == 'A' && conn == 1) || (pname[len-1] == 'B' && conn == 2)) { for (i = 0; propnames[i] != NULL; ++i) { - pedid = (unsigned char *) - get_property(dp, propnames[i], + pedid = get_property(dp, propnames[i], NULL); if (pedid != NULL) break; @@ -67,8 +66,7 @@ int nvidia_probe_of_connector(struct fb_info *info, int conn, u8 **out_edid) } if (pedid == NULL) { for (i = 0; propnames[i] != NULL; ++i) { - pedid = (unsigned char *) - get_property(parent, propnames[i], NULL); + pedid = get_property(parent, propnames[i], NULL); if (pedid != NULL) break; } diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 0013311..bad0e98 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -409,30 +409,30 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) unsigned int flags, rsize, addr_prop = 0; unsigned long max_size = 0; u64 rstart, address = OF_BAD_ADDR; - u32 *pp, *addrp, *up; + const u32 *pp, *addrp, *up; u64 asize; - pp = (u32 *)get_property(dp, "linux,bootx-depth", &len); + pp = get_property(dp, "linux,bootx-depth", &len); if (pp == NULL) - pp = (u32 *)get_property(dp, "depth", &len); + pp = get_property(dp, "depth", &len); if (pp && len == sizeof(u32)) depth = *pp; - pp = (u32 *)get_property(dp, "linux,bootx-width", &len); + pp = get_property(dp, "linux,bootx-width", &len); if (pp == NULL) - pp = (u32 *)get_property(dp, "width", &len); + pp = get_property(dp, "width", &len); if (pp && len == sizeof(u32)) width = *pp; - pp = (u32 *)get_property(dp, "linux,bootx-height", &len); + pp = get_property(dp, "linux,bootx-height", &len); if (pp == NULL) - pp = (u32 *)get_property(dp, "height", &len); + pp = get_property(dp, "height", &len); if (pp && len == sizeof(u32)) height = *pp; - pp = (u32 *)get_property(dp, "linux,bootx-linebytes", &len); + pp = get_property(dp, "linux,bootx-linebytes", &len); if (pp == NULL) - pp = (u32 *)get_property(dp, "linebytes", &len); + pp = get_property(dp, "linebytes", &len); if (pp && len == sizeof(u32)) pitch = *pp; else @@ -450,9 +450,9 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) * ranges and pick one that is both big enough and if possible encloses * the "address" property. If none match, we pick the biggest */ - up = (u32 *)get_property(dp, "linux,bootx-addr", &len); + up = get_property(dp, "linux,bootx-addr", &len); if (up == NULL) - up = (u32 *)get_property(dp, "address", &len); + up = get_property(dp, "address", &len); if (up && len == sizeof(u32)) addr_prop = *up; diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 8ddb47a..67d1e1c 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -1835,14 +1835,13 @@ static int __devinit riva_get_EDID_OF(struct fb_info *info, struct pci_dev *pd) NVTRACE_ENTER(); dp = pci_device_to_OF_node(pd); for (; dp != NULL; dp = dp->child) { - disptype = (unsigned char *)get_property(dp, "display-type", NULL); + disptype = get_property(dp, "display-type", NULL); if (disptype == NULL) continue; if (strncmp(disptype, "LCD", 3) != 0) continue; for (i = 0; propnames[i] != NULL; ++i) { - pedid = (unsigned char *) - get_property(dp, propnames[i], NULL); + pedid = get_property(dp, propnames[i], NULL); if (pedid != NULL) { par->EDID = pedid; NVTRACE("LCD found.\n"); |