xref: /OK3568_Linux_fs/kernel/drivers/ide/ide-ioctls.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * IDE ioctls handling.
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/compat.h>
7*4882a593Smuzhiyun #include <linux/export.h>
8*4882a593Smuzhiyun #include <linux/hdreg.h>
9*4882a593Smuzhiyun #include <linux/ide.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun 
put_user_long(long val,unsigned long arg)12*4882a593Smuzhiyun static int put_user_long(long val, unsigned long arg)
13*4882a593Smuzhiyun {
14*4882a593Smuzhiyun 	if (in_compat_syscall())
15*4882a593Smuzhiyun 		return put_user(val, (compat_long_t __user *)compat_ptr(arg));
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun 	return put_user(val, (long __user *)arg);
18*4882a593Smuzhiyun }
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static const struct ide_ioctl_devset ide_ioctl_settings[] = {
21*4882a593Smuzhiyun { HDIO_GET_32BIT,	 HDIO_SET_32BIT,	&ide_devset_io_32bit  },
22*4882a593Smuzhiyun { HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS,	&ide_devset_keepsettings },
23*4882a593Smuzhiyun { HDIO_GET_UNMASKINTR,	 HDIO_SET_UNMASKINTR,	&ide_devset_unmaskirq },
24*4882a593Smuzhiyun { HDIO_GET_DMA,		 HDIO_SET_DMA,		&ide_devset_using_dma },
25*4882a593Smuzhiyun { -1,			 HDIO_SET_PIO_MODE,	&ide_devset_pio_mode  },
26*4882a593Smuzhiyun { 0 }
27*4882a593Smuzhiyun };
28*4882a593Smuzhiyun 
ide_setting_ioctl(ide_drive_t * drive,struct block_device * bdev,unsigned int cmd,unsigned long arg,const struct ide_ioctl_devset * s)29*4882a593Smuzhiyun int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
30*4882a593Smuzhiyun 		      unsigned int cmd, unsigned long arg,
31*4882a593Smuzhiyun 		      const struct ide_ioctl_devset *s)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun 	const struct ide_devset *ds;
34*4882a593Smuzhiyun 	int err = -EOPNOTSUPP;
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	for (; (ds = s->setting); s++) {
37*4882a593Smuzhiyun 		if (ds->get && s->get_ioctl == cmd)
38*4882a593Smuzhiyun 			goto read_val;
39*4882a593Smuzhiyun 		else if (ds->set && s->set_ioctl == cmd)
40*4882a593Smuzhiyun 			goto set_val;
41*4882a593Smuzhiyun 	}
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	return err;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun read_val:
46*4882a593Smuzhiyun 	mutex_lock(&ide_setting_mtx);
47*4882a593Smuzhiyun 	err = ds->get(drive);
48*4882a593Smuzhiyun 	mutex_unlock(&ide_setting_mtx);
49*4882a593Smuzhiyun 	return err >= 0 ? put_user_long(err, arg) : err;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun set_val:
52*4882a593Smuzhiyun 	if (bdev_is_partition(bdev))
53*4882a593Smuzhiyun 		err = -EINVAL;
54*4882a593Smuzhiyun 	else {
55*4882a593Smuzhiyun 		if (!capable(CAP_SYS_ADMIN))
56*4882a593Smuzhiyun 			err = -EACCES;
57*4882a593Smuzhiyun 		else {
58*4882a593Smuzhiyun 			mutex_lock(&ide_setting_mtx);
59*4882a593Smuzhiyun 			err = ide_devset_execute(drive, ds, arg);
60*4882a593Smuzhiyun 			mutex_unlock(&ide_setting_mtx);
61*4882a593Smuzhiyun 		}
62*4882a593Smuzhiyun 	}
63*4882a593Smuzhiyun 	return err;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ide_setting_ioctl);
66*4882a593Smuzhiyun 
ide_get_identity_ioctl(ide_drive_t * drive,unsigned int cmd,void __user * argp)67*4882a593Smuzhiyun static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd,
68*4882a593Smuzhiyun 				  void __user *argp)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	u16 *id = NULL;
71*4882a593Smuzhiyun 	int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142;
72*4882a593Smuzhiyun 	int rc = 0;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
75*4882a593Smuzhiyun 		rc = -ENOMSG;
76*4882a593Smuzhiyun 		goto out;
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	/* ata_id_to_hd_driveid() relies on 'id' to be fully allocated. */
80*4882a593Smuzhiyun 	id = kmalloc(ATA_ID_WORDS * 2, GFP_KERNEL);
81*4882a593Smuzhiyun 	if (id == NULL) {
82*4882a593Smuzhiyun 		rc = -ENOMEM;
83*4882a593Smuzhiyun 		goto out;
84*4882a593Smuzhiyun 	}
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	memcpy(id, drive->id, size);
87*4882a593Smuzhiyun 	ata_id_to_hd_driveid(id);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (copy_to_user(argp, id, size))
90*4882a593Smuzhiyun 		rc = -EFAULT;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	kfree(id);
93*4882a593Smuzhiyun out:
94*4882a593Smuzhiyun 	return rc;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
ide_get_nice_ioctl(ide_drive_t * drive,unsigned long arg)97*4882a593Smuzhiyun static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	return put_user_long((!!(drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)
100*4882a593Smuzhiyun 			 << IDE_NICE_DSC_OVERLAP) |
101*4882a593Smuzhiyun 			(!!(drive->dev_flags & IDE_DFLAG_NICE1)
102*4882a593Smuzhiyun 			 << IDE_NICE_1), arg);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
ide_set_nice_ioctl(ide_drive_t * drive,unsigned long arg)105*4882a593Smuzhiyun static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
108*4882a593Smuzhiyun 		return -EPERM;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) &&
111*4882a593Smuzhiyun 	    (drive->media != ide_tape))
112*4882a593Smuzhiyun 		return -EPERM;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	if ((arg >> IDE_NICE_DSC_OVERLAP) & 1)
115*4882a593Smuzhiyun 		drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
116*4882a593Smuzhiyun 	else
117*4882a593Smuzhiyun 		drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if ((arg >> IDE_NICE_1) & 1)
120*4882a593Smuzhiyun 		drive->dev_flags |= IDE_DFLAG_NICE1;
121*4882a593Smuzhiyun 	else
122*4882a593Smuzhiyun 		drive->dev_flags &= ~IDE_DFLAG_NICE1;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	return 0;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
ide_cmd_ioctl(ide_drive_t * drive,void __user * argp)127*4882a593Smuzhiyun static int ide_cmd_ioctl(ide_drive_t *drive, void __user *argp)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	u8 *buf = NULL;
130*4882a593Smuzhiyun 	int bufsize = 0, err = 0;
131*4882a593Smuzhiyun 	u8 args[4], xfer_rate = 0;
132*4882a593Smuzhiyun 	struct ide_cmd cmd;
133*4882a593Smuzhiyun 	struct ide_taskfile *tf = &cmd.tf;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	if (NULL == argp) {
136*4882a593Smuzhiyun 		struct request *rq;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 		rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0);
139*4882a593Smuzhiyun 		ide_req(rq)->type = ATA_PRIV_TASKFILE;
140*4882a593Smuzhiyun 		blk_execute_rq(drive->queue, NULL, rq, 0);
141*4882a593Smuzhiyun 		err = scsi_req(rq)->result ? -EIO : 0;
142*4882a593Smuzhiyun 		blk_put_request(rq);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 		return err;
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	if (copy_from_user(args, argp, 4))
148*4882a593Smuzhiyun 		return -EFAULT;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	memset(&cmd, 0, sizeof(cmd));
151*4882a593Smuzhiyun 	tf->feature = args[2];
152*4882a593Smuzhiyun 	if (args[0] == ATA_CMD_SMART) {
153*4882a593Smuzhiyun 		tf->nsect = args[3];
154*4882a593Smuzhiyun 		tf->lbal  = args[1];
155*4882a593Smuzhiyun 		tf->lbam  = ATA_SMART_LBAM_PASS;
156*4882a593Smuzhiyun 		tf->lbah  = ATA_SMART_LBAH_PASS;
157*4882a593Smuzhiyun 		cmd.valid.out.tf = IDE_VALID_OUT_TF;
158*4882a593Smuzhiyun 		cmd.valid.in.tf  = IDE_VALID_NSECT;
159*4882a593Smuzhiyun 	} else {
160*4882a593Smuzhiyun 		tf->nsect = args[1];
161*4882a593Smuzhiyun 		cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
162*4882a593Smuzhiyun 		cmd.valid.in.tf  = IDE_VALID_NSECT;
163*4882a593Smuzhiyun 	}
164*4882a593Smuzhiyun 	tf->command = args[0];
165*4882a593Smuzhiyun 	cmd.protocol = args[3] ? ATA_PROT_PIO : ATA_PROT_NODATA;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	if (args[3]) {
168*4882a593Smuzhiyun 		cmd.tf_flags |= IDE_TFLAG_IO_16BIT;
169*4882a593Smuzhiyun 		bufsize = SECTOR_SIZE * args[3];
170*4882a593Smuzhiyun 		buf = kzalloc(bufsize, GFP_KERNEL);
171*4882a593Smuzhiyun 		if (buf == NULL)
172*4882a593Smuzhiyun 			return -ENOMEM;
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (tf->command == ATA_CMD_SET_FEATURES &&
176*4882a593Smuzhiyun 	    tf->feature == SETFEATURES_XFER &&
177*4882a593Smuzhiyun 	    tf->nsect >= XFER_SW_DMA_0) {
178*4882a593Smuzhiyun 		xfer_rate = ide_find_dma_mode(drive, tf->nsect);
179*4882a593Smuzhiyun 		if (xfer_rate != tf->nsect) {
180*4882a593Smuzhiyun 			err = -EINVAL;
181*4882a593Smuzhiyun 			goto abort;
182*4882a593Smuzhiyun 		}
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 		cmd.tf_flags |= IDE_TFLAG_SET_XFER;
185*4882a593Smuzhiyun 	}
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	err = ide_raw_taskfile(drive, &cmd, buf, args[3]);
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	args[0] = tf->status;
190*4882a593Smuzhiyun 	args[1] = tf->error;
191*4882a593Smuzhiyun 	args[2] = tf->nsect;
192*4882a593Smuzhiyun abort:
193*4882a593Smuzhiyun 	if (copy_to_user(argp, &args, 4))
194*4882a593Smuzhiyun 		err = -EFAULT;
195*4882a593Smuzhiyun 	if (buf) {
196*4882a593Smuzhiyun 		if (copy_to_user((argp + 4), buf, bufsize))
197*4882a593Smuzhiyun 			err = -EFAULT;
198*4882a593Smuzhiyun 		kfree(buf);
199*4882a593Smuzhiyun 	}
200*4882a593Smuzhiyun 	return err;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun 
ide_task_ioctl(ide_drive_t * drive,void __user * p)203*4882a593Smuzhiyun static int ide_task_ioctl(ide_drive_t *drive, void __user *p)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	int err = 0;
206*4882a593Smuzhiyun 	u8 args[7];
207*4882a593Smuzhiyun 	struct ide_cmd cmd;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	if (copy_from_user(args, p, 7))
210*4882a593Smuzhiyun 		return -EFAULT;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	memset(&cmd, 0, sizeof(cmd));
213*4882a593Smuzhiyun 	memcpy(&cmd.tf.feature, &args[1], 6);
214*4882a593Smuzhiyun 	cmd.tf.command = args[0];
215*4882a593Smuzhiyun 	cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
216*4882a593Smuzhiyun 	cmd.valid.in.tf  = IDE_VALID_IN_TF  | IDE_VALID_DEVICE;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	err = ide_no_data_taskfile(drive, &cmd);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	args[0] = cmd.tf.command;
221*4882a593Smuzhiyun 	memcpy(&args[1], &cmd.tf.feature, 6);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	if (copy_to_user(p, args, 7))
224*4882a593Smuzhiyun 		err = -EFAULT;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	return err;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun 
generic_drive_reset(ide_drive_t * drive)229*4882a593Smuzhiyun static int generic_drive_reset(ide_drive_t *drive)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	struct request *rq;
232*4882a593Smuzhiyun 	int ret = 0;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0);
235*4882a593Smuzhiyun 	ide_req(rq)->type = ATA_PRIV_MISC;
236*4882a593Smuzhiyun 	scsi_req(rq)->cmd_len = 1;
237*4882a593Smuzhiyun 	scsi_req(rq)->cmd[0] = REQ_DRIVE_RESET;
238*4882a593Smuzhiyun 	blk_execute_rq(drive->queue, NULL, rq, 1);
239*4882a593Smuzhiyun 	ret = scsi_req(rq)->result;
240*4882a593Smuzhiyun 	blk_put_request(rq);
241*4882a593Smuzhiyun 	return ret;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun 
generic_ide_ioctl(ide_drive_t * drive,struct block_device * bdev,unsigned int cmd,unsigned long arg)244*4882a593Smuzhiyun int generic_ide_ioctl(ide_drive_t *drive, struct block_device *bdev,
245*4882a593Smuzhiyun 		      unsigned int cmd, unsigned long arg)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	int err;
248*4882a593Smuzhiyun 	void __user *argp = (void __user *)arg;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	if (in_compat_syscall())
251*4882a593Smuzhiyun 		argp = compat_ptr(arg);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings);
254*4882a593Smuzhiyun 	if (err != -EOPNOTSUPP)
255*4882a593Smuzhiyun 		return err;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	switch (cmd) {
258*4882a593Smuzhiyun 	case HDIO_OBSOLETE_IDENTITY:
259*4882a593Smuzhiyun 	case HDIO_GET_IDENTITY:
260*4882a593Smuzhiyun 		if (bdev_is_partition(bdev))
261*4882a593Smuzhiyun 			return -EINVAL;
262*4882a593Smuzhiyun 		return ide_get_identity_ioctl(drive, cmd, argp);
263*4882a593Smuzhiyun 	case HDIO_GET_NICE:
264*4882a593Smuzhiyun 		return ide_get_nice_ioctl(drive, arg);
265*4882a593Smuzhiyun 	case HDIO_SET_NICE:
266*4882a593Smuzhiyun 		if (!capable(CAP_SYS_ADMIN))
267*4882a593Smuzhiyun 			return -EACCES;
268*4882a593Smuzhiyun 		return ide_set_nice_ioctl(drive, arg);
269*4882a593Smuzhiyun #ifdef CONFIG_IDE_TASK_IOCTL
270*4882a593Smuzhiyun 	case HDIO_DRIVE_TASKFILE:
271*4882a593Smuzhiyun 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
272*4882a593Smuzhiyun 			return -EACCES;
273*4882a593Smuzhiyun 		/* missing compat handler for HDIO_DRIVE_TASKFILE */
274*4882a593Smuzhiyun 		if (in_compat_syscall())
275*4882a593Smuzhiyun 			return -ENOTTY;
276*4882a593Smuzhiyun 		if (drive->media == ide_disk)
277*4882a593Smuzhiyun 			return ide_taskfile_ioctl(drive, arg);
278*4882a593Smuzhiyun 		return -ENOMSG;
279*4882a593Smuzhiyun #endif
280*4882a593Smuzhiyun 	case HDIO_DRIVE_CMD:
281*4882a593Smuzhiyun 		if (!capable(CAP_SYS_RAWIO))
282*4882a593Smuzhiyun 			return -EACCES;
283*4882a593Smuzhiyun 		return ide_cmd_ioctl(drive, argp);
284*4882a593Smuzhiyun 	case HDIO_DRIVE_TASK:
285*4882a593Smuzhiyun 		if (!capable(CAP_SYS_RAWIO))
286*4882a593Smuzhiyun 			return -EACCES;
287*4882a593Smuzhiyun 		return ide_task_ioctl(drive, argp);
288*4882a593Smuzhiyun 	case HDIO_DRIVE_RESET:
289*4882a593Smuzhiyun 		if (!capable(CAP_SYS_ADMIN))
290*4882a593Smuzhiyun 			return -EACCES;
291*4882a593Smuzhiyun 		return generic_drive_reset(drive);
292*4882a593Smuzhiyun 	case HDIO_GET_BUSSTATE:
293*4882a593Smuzhiyun 		if (!capable(CAP_SYS_ADMIN))
294*4882a593Smuzhiyun 			return -EACCES;
295*4882a593Smuzhiyun 		if (put_user_long(BUSSTATE_ON, arg))
296*4882a593Smuzhiyun 			return -EFAULT;
297*4882a593Smuzhiyun 		return 0;
298*4882a593Smuzhiyun 	case HDIO_SET_BUSSTATE:
299*4882a593Smuzhiyun 		if (!capable(CAP_SYS_ADMIN))
300*4882a593Smuzhiyun 			return -EACCES;
301*4882a593Smuzhiyun 		return -EOPNOTSUPP;
302*4882a593Smuzhiyun 	default:
303*4882a593Smuzhiyun 		return -EINVAL;
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun EXPORT_SYMBOL(generic_ide_ioctl);
307