/* * Start SCLP request */ static int sclp_ctl_ioctl_sccb(void __user *user_area) { struct sclp_ctl_sccb ctl_sccb; struct sccb_header *sccb; int rc; if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) return -EFAULT; if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) return -EOPNOTSUPP; sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sccb) return -ENOMEM; if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { rc = -EFAULT; goto out_free; } if (sccb->length > PAGE_SIZE || sccb->length < 8) return -EINVAL; if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { rc = -EFAULT; goto out_free; } rc = sclp_sync_request(ctl_sccb.cmdw, sccb); if (rc) goto out_free; if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) rc = -EFAULT; out_free: free_page((unsigned long) sccb); return rc; }
static int ioctl_get_info(struct client *client, void *buffer) { struct fw_cdev_get_info *get_info = buffer; struct fw_cdev_event_bus_reset bus_reset; unsigned long ret = 0; client->version = get_info->version; get_info->version = FW_CDEV_VERSION; get_info->card = client->device->card->index; down_read(&fw_device_rwsem); if (get_info->rom != 0) { void __user *uptr = u64_to_uptr(get_info->rom); size_t want = get_info->rom_length; size_t have = client->device->config_rom_length * 4; ret = copy_to_user(uptr, client->device->config_rom, min(want, have)); } get_info->rom_length = client->device->config_rom_length * 4; up_read(&fw_device_rwsem); if (ret != 0) return -EFAULT; client->bus_reset_closure = get_info->bus_reset_closure; if (get_info->bus_reset != 0) { void __user *uptr = u64_to_uptr(get_info->bus_reset); fill_bus_reset_event(&bus_reset, client); if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) return -EFAULT; } return 0; }
static int ioctl_add_descriptor(struct client *client, void *buffer) { struct fw_cdev_add_descriptor *request = buffer; struct descriptor_resource *r; int ret; /* Access policy: Allow this ioctl only on local nodes' device files. */ if (!client->device->is_local) return -ENOSYS; if (request->length > 256) return -EINVAL; r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL); if (r == NULL) return -ENOMEM; if (copy_from_user(r->data, u64_to_uptr(request->data), request->length * 4)) { ret = -EFAULT; goto failed; } r->descriptor.length = request->length; r->descriptor.immediate = request->immediate; r->descriptor.key = request->key; r->descriptor.data = r->data; ret = fw_core_add_descriptor(&r->descriptor); if (ret < 0) goto failed; r->resource.release = release_descriptor; ret = add_client_resource(client, &r->resource, GFP_KERNEL); if (ret < 0) { fw_core_remove_descriptor(&r->descriptor); goto failed; } request->handle = r->resource.handle; return 0; failed: kfree(r); return ret; }
static int init_request(struct client *client, struct fw_cdev_send_request *request, int destination_id, int speed) { struct outbound_transaction_event *e; int ret; if (request->tcode != TCODE_STREAM_DATA && (request->length > 4096 || request->length > 512 << speed)) return -EIO; e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); if (e == NULL) return -ENOMEM; e->client = client; e->response.length = request->length; e->response.closure = request->closure; if (request->data && copy_from_user(e->response.data, u64_to_uptr(request->data), request->length)) { ret = -EFAULT; goto failed; } e->r.resource.release = release_transaction; ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); if (ret < 0) goto failed; /* Get a reference for the transaction callback */ client_get(client); fw_send_request(client->device->card, &e->r.transaction, request->tcode, destination_id, request->generation, speed, request->offset, e->response.data, request->length, complete_transaction, e); return 0; failed: kfree(e); return ret; }
static int ioctl_send_response(struct client *client, void *buffer) { struct fw_cdev_send_response *request = buffer; struct client_resource *resource; struct inbound_transaction_resource *r; if (release_client_resource(client, request->handle, release_request, &resource) < 0) return -EINVAL; r = container_of(resource, struct inbound_transaction_resource, resource); if (request->length < r->length) r->length = request->length; if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) return -EFAULT; fw_send_response(client->device->card, r->request, request->rcode); kfree(r); return 0; }
static int ioctl_queue_iso(struct client *client, void *buffer) { struct fw_cdev_queue_iso *request = buffer; struct fw_cdev_iso_packet __user *p, *end, *next; struct fw_iso_context *ctx = client->iso_context; unsigned long payload, buffer_end, header_length; u32 control; int count; struct { struct fw_iso_packet packet; u8 header[256]; } u; if (ctx == NULL || request->handle != 0) return -EINVAL; /* * If the user passes a non-NULL data pointer, has mmap()'ed * the iso buffer, and the pointer points inside the buffer, * we setup the payload pointers accordingly. Otherwise we * set them both to 0, which will still let packets with * payload_length == 0 through. In other words, if no packets * use the indirect payload, the iso buffer need not be mapped * and the request->data pointer is ignored. */ payload = (unsigned long)request->data - client->vm_start; buffer_end = client->buffer.page_count << PAGE_SHIFT; if (request->data == 0 || client->buffer.pages == NULL || payload >= buffer_end) { payload = 0; buffer_end = 0; } p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets); if (!access_ok(VERIFY_READ, p, request->size)) return -EFAULT; end = (void __user *)p + request->size; count = 0; while (p < end) { if (get_user(control, &p->control)) return -EFAULT; u.packet.payload_length = GET_PAYLOAD_LENGTH(control); u.packet.interrupt = GET_INTERRUPT(control); u.packet.skip = GET_SKIP(control); u.packet.tag = GET_TAG(control); u.packet.sy = GET_SY(control); u.packet.header_length = GET_HEADER_LENGTH(control); if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { header_length = u.packet.header_length; } else { /* * We require that header_length is a multiple of * the fixed header size, ctx->header_size. */ if (ctx->header_size == 0) { if (u.packet.header_length > 0) return -EINVAL; } else if (u.packet.header_length % ctx->header_size != 0) { return -EINVAL; } header_length = 0; } next = (struct fw_cdev_iso_packet __user *) &p->header[header_length / 4]; if (next > end) return -EINVAL; if (__copy_from_user (u.packet.header, p->header, header_length)) return -EFAULT; if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && u.packet.header_length + u.packet.payload_length > 0) return -EINVAL; if (payload + u.packet.payload_length > buffer_end) return -EINVAL; if (fw_iso_context_queue(ctx, &u.packet, &client->buffer, payload)) break; p = next; payload += u.packet.payload_length; count++; } request->size -= uptr_to_u64(p) - request->packets; request->packets = uptr_to_u64(p); request->data = client->vm_start + payload; return count; }