Blame view

drivers/ide/ide-ioctls.c 6.7 KB
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
1
2
3
  /*
   * IDE ioctls handling.
   */
38789fda2   Paul Gortmaker   ide/ata: Add expo...
4
  #include <linux/export.h>
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
5
6
  #include <linux/hdreg.h>
  #include <linux/ide.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
7
  #include <linux/slab.h>
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
8
9
  
  static const struct ide_ioctl_devset ide_ioctl_settings[] = {
92f1f8fd8   Elias Oltmanns   ide: Remove ide_s...
10
11
12
13
14
  { HDIO_GET_32BIT,	 HDIO_SET_32BIT,	&ide_devset_io_32bit  },
  { HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS,	&ide_devset_keepsettings },
  { HDIO_GET_UNMASKINTR,	 HDIO_SET_UNMASKINTR,	&ide_devset_unmaskirq },
  { HDIO_GET_DMA,		 HDIO_SET_DMA,		&ide_devset_using_dma },
  { -1,			 HDIO_SET_PIO_MODE,	&ide_devset_pio_mode  },
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
15
16
17
18
19
20
21
  { 0 }
  };
  
  int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
  		      unsigned int cmd, unsigned long arg,
  		      const struct ide_ioctl_devset *s)
  {
92f1f8fd8   Elias Oltmanns   ide: Remove ide_s...
22
  	const struct ide_devset *ds;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
23
  	int err = -EOPNOTSUPP;
92f1f8fd8   Elias Oltmanns   ide: Remove ide_s...
24
25
  	for (; (ds = s->setting); s++) {
  		if (ds->get && s->get_ioctl == cmd)
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
26
  			goto read_val;
92f1f8fd8   Elias Oltmanns   ide: Remove ide_s...
27
  		else if (ds->set && s->set_ioctl == cmd)
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
28
29
30
31
32
33
34
  			goto set_val;
  	}
  
  	return err;
  
  read_val:
  	mutex_lock(&ide_setting_mtx);
92f1f8fd8   Elias Oltmanns   ide: Remove ide_s...
35
  	err = ds->get(drive);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
36
37
38
39
40
41
42
43
44
45
46
  	mutex_unlock(&ide_setting_mtx);
  	return err >= 0 ? put_user(err, (long __user *)arg) : err;
  
  set_val:
  	if (bdev != bdev->bd_contains)
  		err = -EINVAL;
  	else {
  		if (!capable(CAP_SYS_ADMIN))
  			err = -EACCES;
  		else {
  			mutex_lock(&ide_setting_mtx);
92f1f8fd8   Elias Oltmanns   ide: Remove ide_s...
47
  			err = ide_devset_execute(drive, ds, arg);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
48
49
50
51
52
53
54
55
56
57
58
59
60
  			mutex_unlock(&ide_setting_mtx);
  		}
  	}
  	return err;
  }
  EXPORT_SYMBOL_GPL(ide_setting_ioctl);
  
  static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd,
  				  unsigned long arg)
  {
  	u16 *id = NULL;
  	int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142;
  	int rc = 0;
97100fc81   Bartlomiej Zolnierkiewicz   ide: add device f...
61
  	if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
62
63
64
  		rc = -ENOMSG;
  		goto out;
  	}
e18ed145c   Christian Engelmayer   ide: memory overr...
65
66
  	/* ata_id_to_hd_driveid() relies on 'id' to be fully allocated. */
  	id = kmalloc(ATA_ID_WORDS * 2, GFP_KERNEL);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  	if (id == NULL) {
  		rc = -ENOMEM;
  		goto out;
  	}
  
  	memcpy(id, drive->id, size);
  	ata_id_to_hd_driveid(id);
  
  	if (copy_to_user((void __user *)arg, id, size))
  		rc = -EFAULT;
  
  	kfree(id);
  out:
  	return rc;
  }
  
  static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg)
  {
97100fc81   Bartlomiej Zolnierkiewicz   ide: add device f...
85
86
87
88
  	return put_user((!!(drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)
  			 << IDE_NICE_DSC_OVERLAP) |
  			(!!(drive->dev_flags & IDE_DFLAG_NICE1)
  			 << IDE_NICE_1), (long __user *)arg);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
89
90
91
92
93
94
95
96
  }
  
  static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg)
  {
  	if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
  		return -EPERM;
  
  	if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) &&
5317464dc   Borislav Petkov   ide: remove the l...
97
  	    (drive->media != ide_tape))
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
98
  		return -EPERM;
97100fc81   Bartlomiej Zolnierkiewicz   ide: add device f...
99
100
101
102
103
104
105
106
107
  	if ((arg >> IDE_NICE_DSC_OVERLAP) & 1)
  		drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
  	else
  		drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
  
  	if ((arg >> IDE_NICE_1) & 1)
  		drive->dev_flags |= IDE_DFLAG_NICE1;
  	else
  		drive->dev_flags &= ~IDE_DFLAG_NICE1;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
108
109
110
  
  	return 0;
  }
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
111
  static int ide_cmd_ioctl(ide_drive_t *drive, unsigned long arg)
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
112
113
114
115
  {
  	u8 *buf = NULL;
  	int bufsize = 0, err = 0;
  	u8 args[4], xfer_rate = 0;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
116
117
  	struct ide_cmd cmd;
  	struct ide_taskfile *tf = &cmd.tf;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  
  	if (NULL == (void *) arg) {
  		struct request *rq;
  
  		rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
  		rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
  		err = blk_execute_rq(drive->queue, NULL, rq, 0);
  		blk_put_request(rq);
  
  		return err;
  	}
  
  	if (copy_from_user(args, (void __user *)arg, 4))
  		return -EFAULT;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
132
  	memset(&cmd, 0, sizeof(cmd));
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
133
134
135
136
137
138
  	tf->feature = args[2];
  	if (args[0] == ATA_CMD_SMART) {
  		tf->nsect = args[3];
  		tf->lbal  = args[1];
  		tf->lbam  = 0x4f;
  		tf->lbah  = 0xc2;
60f85019c   Sergei Shtylyov   ide: replace IDE_...
139
140
  		cmd.valid.out.tf = IDE_VALID_OUT_TF;
  		cmd.valid.in.tf  = IDE_VALID_NSECT;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
141
142
  	} else {
  		tf->nsect = args[1];
60f85019c   Sergei Shtylyov   ide: replace IDE_...
143
144
  		cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
  		cmd.valid.in.tf  = IDE_VALID_NSECT;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
145
146
  	}
  	tf->command = args[0];
0dfb991c6   Bartlomiej Zolnierkiewicz   ide: use ata_tf_p...
147
  	cmd.protocol = args[3] ? ATA_PROT_PIO : ATA_PROT_NODATA;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
148
149
  
  	if (args[3]) {
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
150
  		cmd.tf_flags |= IDE_TFLAG_IO_16BIT;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
151
152
153
154
155
156
157
158
  		bufsize = SECTOR_SIZE * args[3];
  		buf = kzalloc(bufsize, GFP_KERNEL);
  		if (buf == NULL)
  			return -ENOMEM;
  	}
  
  	if (tf->command == ATA_CMD_SET_FEATURES &&
  	    tf->feature == SETFEATURES_XFER &&
a9c415090   Bartlomiej Zolnierkiewicz   ide: filter out i...
159
  	    tf->nsect >= XFER_SW_DMA_0) {
28c1969ff   Hemant Pedanekar   ide: fix ioctl to...
160
  		xfer_rate = ide_find_dma_mode(drive, tf->nsect);
a9c415090   Bartlomiej Zolnierkiewicz   ide: filter out i...
161
162
  		if (xfer_rate != tf->nsect) {
  			err = -EINVAL;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
163
164
  			goto abort;
  		}
665d66e8f   Bartlomiej Zolnierkiewicz   ide: fix races in...
165
166
  
  		cmd.tf_flags |= IDE_TFLAG_SET_XFER;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
167
  	}
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
168
  	err = ide_raw_taskfile(drive, &cmd, buf, args[3]);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
169
170
171
172
  
  	args[0] = tf->status;
  	args[1] = tf->error;
  	args[2] = tf->nsect;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
173
174
175
176
177
178
179
180
181
182
  abort:
  	if (copy_to_user((void __user *)arg, &args, 4))
  		err = -EFAULT;
  	if (buf) {
  		if (copy_to_user((void __user *)(arg + 4), buf, bufsize))
  			err = -EFAULT;
  		kfree(buf);
  	}
  	return err;
  }
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
183
  static int ide_task_ioctl(ide_drive_t *drive, unsigned long arg)
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
184
185
186
187
  {
  	void __user *p = (void __user *)arg;
  	int err = 0;
  	u8 args[7];
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
188
  	struct ide_cmd cmd;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
189
190
191
  
  	if (copy_from_user(args, p, 7))
  		return -EFAULT;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
192
  	memset(&cmd, 0, sizeof(cmd));
745483f10   Sergei Shtylyov   ide: simplify 'st...
193
  	memcpy(&cmd.tf.feature, &args[1], 6);
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
194
  	cmd.tf.command = args[0];
60f85019c   Sergei Shtylyov   ide: replace IDE_...
195
196
  	cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
  	cmd.valid.in.tf  = IDE_VALID_IN_TF  | IDE_VALID_DEVICE;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
197

22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
198
  	err = ide_no_data_taskfile(drive, &cmd);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
199

22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
200
  	args[0] = cmd.tf.command;
745483f10   Sergei Shtylyov   ide: simplify 'st...
201
  	memcpy(&args[1], &cmd.tf.feature, 6);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  
  	if (copy_to_user(p, args, 7))
  		err = -EFAULT;
  
  	return err;
  }
  
  static int generic_drive_reset(ide_drive_t *drive)
  {
  	struct request *rq;
  	int ret = 0;
  
  	rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
  	rq->cmd_type = REQ_TYPE_SPECIAL;
  	rq->cmd_len = 1;
  	rq->cmd[0] = REQ_DRIVE_RESET;
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
218
219
220
221
222
  	if (blk_execute_rq(drive->queue, NULL, rq, 1))
  		ret = rq->errors;
  	blk_put_request(rq);
  	return ret;
  }
1bddd9e64   Al Viro   [PATCH] lose the ...
223
  int generic_ide_ioctl(ide_drive_t *drive, struct block_device *bdev,
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  		      unsigned int cmd, unsigned long arg)
  {
  	int err;
  
  	err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings);
  	if (err != -EOPNOTSUPP)
  		return err;
  
  	switch (cmd) {
  	case HDIO_OBSOLETE_IDENTITY:
  	case HDIO_GET_IDENTITY:
  		if (bdev != bdev->bd_contains)
  			return -EINVAL;
  		return ide_get_identity_ioctl(drive, cmd, arg);
  	case HDIO_GET_NICE:
  		return ide_get_nice_ioctl(drive, arg);
  	case HDIO_SET_NICE:
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		return ide_set_nice_ioctl(drive, arg);
  #ifdef CONFIG_IDE_TASK_IOCTL
  	case HDIO_DRIVE_TASKFILE:
  		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
  			return -EACCES;
  		if (drive->media == ide_disk)
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
249
  			return ide_taskfile_ioctl(drive, arg);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
250
251
252
253
254
  		return -ENOMSG;
  #endif
  	case HDIO_DRIVE_CMD:
  		if (!capable(CAP_SYS_RAWIO))
  			return -EACCES;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
255
  		return ide_cmd_ioctl(drive, arg);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
256
257
258
  	case HDIO_DRIVE_TASK:
  		if (!capable(CAP_SYS_RAWIO))
  			return -EACCES;
22aa4b32a   Bartlomiej Zolnierkiewicz   ide: remove ide_t...
259
  		return ide_task_ioctl(drive, arg);
05236ea6d   Bartlomiej Zolnierkiewicz   ide: move ioctls ...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  	case HDIO_DRIVE_RESET:
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		return generic_drive_reset(drive);
  	case HDIO_GET_BUSSTATE:
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		if (put_user(BUSSTATE_ON, (long __user *)arg))
  			return -EFAULT;
  		return 0;
  	case HDIO_SET_BUSSTATE:
  		if (!capable(CAP_SYS_ADMIN))
  			return -EACCES;
  		return -EOPNOTSUPP;
  	default:
  		return -EINVAL;
  	}
  }
  EXPORT_SYMBOL(generic_ide_ioctl);