1/* 2 * zfcp device driver 3 * 4 * Userspace interface for accessing the 5 * Access Control Lists / Control File Data Channel 6 * 7 * Copyright IBM Corporation 2008, 2009 8 */ 9 10#define KMSG_COMPONENT "zfcp" 11#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 12 13#include <linux/slab.h> 14#include <linux/types.h> 15#include <linux/miscdevice.h> 16#include <asm/compat.h> 17#include <asm/ccwdev.h> 18#include "zfcp_def.h" 19#include "zfcp_ext.h" 20#include "zfcp_fsf.h" 21 22#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL 0x00010001 23#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE 0x00010101 24#define ZFCP_CFDC_CMND_FULL_ACCESS 0x00000201 25#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS 0x00000401 26#define ZFCP_CFDC_CMND_UPLOAD 0x00010002 27 28#define ZFCP_CFDC_DOWNLOAD 0x00000001 29#define ZFCP_CFDC_UPLOAD 0x00000002 30#define ZFCP_CFDC_WITH_CONTROL_FILE 0x00010000 31 32#define ZFCP_CFDC_IOC_MAGIC 0xDD 33#define ZFCP_CFDC_IOC \ 34 _IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data) 35 36/** 37 * struct zfcp_cfdc_data - data for ioctl cfdc interface 38 * @signature: request signature 39 * @devno: FCP adapter device number 40 * @command: command code 41 * @fsf_status: returns status of FSF command to userspace 42 * @fsf_status_qual: returned to userspace 43 * @payloads: access conflicts list 44 * @control_file: access control table 45 */ 46struct zfcp_cfdc_data { 47 u32 signature; 48 u32 devno; 49 u32 command; 50 u32 fsf_status; 51 u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; 52 u8 payloads[256]; 53 u8 control_file[0]; 54}; 55 56static int zfcp_cfdc_copy_from_user(struct scatterlist *sg, 57 void __user *user_buffer) 58{ 59 unsigned int length; 60 unsigned int size = ZFCP_CFDC_MAX_SIZE; 61 62 while (size) { 63 length = min((unsigned int)size, sg->length); 64 if (copy_from_user(sg_virt(sg++), user_buffer, length)) 65 return -EFAULT; 66 user_buffer += length; 67 size -= length; 68 } 69 return 0; 70} 71 72static int zfcp_cfdc_copy_to_user(void __user *user_buffer, 73 struct scatterlist *sg) 74{ 75 unsigned int length; 76 unsigned int size = ZFCP_CFDC_MAX_SIZE; 77 78 while (size) { 79 length = min((unsigned int) size, sg->length); 80 if (copy_to_user(user_buffer, sg_virt(sg++), length)) 81 return -EFAULT; 82 user_buffer += length; 83 size -= length; 84 } 85 return 0; 86} 87 88static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno) 89{ 90 char busid[9]; 91 struct ccw_device *cdev; 92 struct zfcp_adapter *adapter; 93 94 snprintf(busid, sizeof(busid), "0.0.%04x", devno); 95 cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); 96 if (!cdev) 97 return NULL; 98 99 adapter = zfcp_ccw_adapter_by_cdev(cdev); 100 101 put_device(&cdev->dev); 102 return adapter; 103} 104 105static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command) 106{ 107 switch (command) { 108 case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL: 109 fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; 110 fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE; 111 break; 112 case ZFCP_CFDC_CMND_DOWNLOAD_FORCE: 113 fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; 114 fsf_cfdc->option = FSF_CFDC_OPTION_FORCE; 115 break; 116 case ZFCP_CFDC_CMND_FULL_ACCESS: 117 fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; 118 fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS; 119 break; 120 case ZFCP_CFDC_CMND_RESTRICTED_ACCESS: 121 fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE; 122 fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS; 123 break; 124 case ZFCP_CFDC_CMND_UPLOAD: 125 fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE; 126 fsf_cfdc->option = 0; 127 break; 128 default: 129 return -EINVAL; 130 } 131 132 return 0; 133} 134 135static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg, 136 u8 __user *control_file) 137{ 138 int retval; 139 retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES); 140 if (retval) 141 return retval; 142 143 sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE; 144 145 if (command & ZFCP_CFDC_WITH_CONTROL_FILE && 146 command & ZFCP_CFDC_DOWNLOAD) { 147 retval = zfcp_cfdc_copy_from_user(sg, control_file); 148 if (retval) { 149 zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES); 150 return -EFAULT; 151 } 152 } 153 154 return 0; 155} 156 157static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data, 158 struct zfcp_fsf_req *req) 159{ 160 data->fsf_status = req->qtcb->header.fsf_status; 161 memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual, 162 sizeof(union fsf_status_qual)); 163 memcpy(&data->payloads, &req->qtcb->bottom.support.els, 164 sizeof(req->qtcb->bottom.support.els)); 165} 166 167static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command, 168 unsigned long arg) 169{ 170 struct zfcp_cfdc_data *data; 171 struct zfcp_cfdc_data __user *data_user; 172 struct zfcp_adapter *adapter; 173 struct zfcp_fsf_req *req; 174 struct zfcp_fsf_cfdc *fsf_cfdc; 175 int retval; 176 177 if (command != ZFCP_CFDC_IOC) 178 return -ENOTTY; 179 180 if (is_compat_task()) 181 data_user = compat_ptr(arg); 182 else 183 data_user = (void __user *)arg; 184 185 if (!data_user) 186 return -EINVAL; 187 188 fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL); 189 if (!fsf_cfdc) 190 return -ENOMEM; 191 192 data = memdup_user(data_user, sizeof(*data_user)); 193 if (IS_ERR(data)) { 194 retval = PTR_ERR(data); 195 goto no_mem_sense; 196 } 197 198 if (data->signature != 0xCFDCACDF) { 199 retval = -EINVAL; 200 goto free_buffer; 201 } 202 203 retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command); 204 205 adapter = zfcp_cfdc_get_adapter(data->devno); 206 if (!adapter) { 207 retval = -ENXIO; 208 goto free_buffer; 209 } 210 211 retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg, 212 data_user->control_file); 213 if (retval) 214 goto adapter_put; 215 req = zfcp_fsf_control_file(adapter, fsf_cfdc); 216 if (IS_ERR(req)) { 217 retval = PTR_ERR(req); 218 goto free_sg; 219 } 220 221 if (req->status & ZFCP_STATUS_FSFREQ_ERROR) { 222 retval = -ENXIO; 223 goto free_fsf; 224 } 225 226 zfcp_cfdc_req_to_sense(data, req); 227 retval = copy_to_user(data_user, data, sizeof(*data_user)); 228 if (retval) { 229 retval = -EFAULT; 230 goto free_fsf; 231 } 232 233 if (data->command & ZFCP_CFDC_UPLOAD) 234 retval = zfcp_cfdc_copy_to_user(&data_user->control_file, 235 fsf_cfdc->sg); 236 237 free_fsf: 238 zfcp_fsf_req_free(req); 239 free_sg: 240 zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES); 241 adapter_put: 242 zfcp_ccw_adapter_put(adapter); 243 free_buffer: 244 kfree(data); 245 no_mem_sense: 246 kfree(fsf_cfdc); 247 return retval; 248} 249 250static const struct file_operations zfcp_cfdc_fops = { 251 .open = nonseekable_open, 252 .unlocked_ioctl = zfcp_cfdc_dev_ioctl, 253#ifdef CONFIG_COMPAT 254 .compat_ioctl = zfcp_cfdc_dev_ioctl 255#endif 256}; 257 258struct miscdevice zfcp_cfdc_misc = { 259 .minor = MISC_DYNAMIC_MINOR, 260 .name = "zfcp_cfdc", 261 .fops = &zfcp_cfdc_fops, 262}; 263