18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IDE ioctls handling. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/compat.h> 78c2ecf20Sopenharmony_ci#include <linux/export.h> 88c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 98c2ecf20Sopenharmony_ci#include <linux/ide.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int put_user_long(long val, unsigned long arg) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci if (in_compat_syscall()) 158c2ecf20Sopenharmony_ci return put_user(val, (compat_long_t __user *)compat_ptr(arg)); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci return put_user(val, (long __user *)arg); 188c2ecf20Sopenharmony_ci} 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const struct ide_ioctl_devset ide_ioctl_settings[] = { 218c2ecf20Sopenharmony_ci{ HDIO_GET_32BIT, HDIO_SET_32BIT, &ide_devset_io_32bit }, 228c2ecf20Sopenharmony_ci{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, &ide_devset_keepsettings }, 238c2ecf20Sopenharmony_ci{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, &ide_devset_unmaskirq }, 248c2ecf20Sopenharmony_ci{ HDIO_GET_DMA, HDIO_SET_DMA, &ide_devset_using_dma }, 258c2ecf20Sopenharmony_ci{ -1, HDIO_SET_PIO_MODE, &ide_devset_pio_mode }, 268c2ecf20Sopenharmony_ci{ 0 } 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciint ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, 308c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg, 318c2ecf20Sopenharmony_ci const struct ide_ioctl_devset *s) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci const struct ide_devset *ds; 348c2ecf20Sopenharmony_ci int err = -EOPNOTSUPP; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci for (; (ds = s->setting); s++) { 378c2ecf20Sopenharmony_ci if (ds->get && s->get_ioctl == cmd) 388c2ecf20Sopenharmony_ci goto read_val; 398c2ecf20Sopenharmony_ci else if (ds->set && s->set_ioctl == cmd) 408c2ecf20Sopenharmony_ci goto set_val; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return err; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciread_val: 468c2ecf20Sopenharmony_ci mutex_lock(&ide_setting_mtx); 478c2ecf20Sopenharmony_ci err = ds->get(drive); 488c2ecf20Sopenharmony_ci mutex_unlock(&ide_setting_mtx); 498c2ecf20Sopenharmony_ci return err >= 0 ? put_user_long(err, arg) : err; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciset_val: 528c2ecf20Sopenharmony_ci if (bdev_is_partition(bdev)) 538c2ecf20Sopenharmony_ci err = -EINVAL; 548c2ecf20Sopenharmony_ci else { 558c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 568c2ecf20Sopenharmony_ci err = -EACCES; 578c2ecf20Sopenharmony_ci else { 588c2ecf20Sopenharmony_ci mutex_lock(&ide_setting_mtx); 598c2ecf20Sopenharmony_ci err = ide_devset_execute(drive, ds, arg); 608c2ecf20Sopenharmony_ci mutex_unlock(&ide_setting_mtx); 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci return err; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_setting_ioctl); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, 688c2ecf20Sopenharmony_ci void __user *argp) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u16 *id = NULL; 718c2ecf20Sopenharmony_ci int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142; 728c2ecf20Sopenharmony_ci int rc = 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) { 758c2ecf20Sopenharmony_ci rc = -ENOMSG; 768c2ecf20Sopenharmony_ci goto out; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* ata_id_to_hd_driveid() relies on 'id' to be fully allocated. */ 808c2ecf20Sopenharmony_ci id = kmalloc(ATA_ID_WORDS * 2, GFP_KERNEL); 818c2ecf20Sopenharmony_ci if (id == NULL) { 828c2ecf20Sopenharmony_ci rc = -ENOMEM; 838c2ecf20Sopenharmony_ci goto out; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci memcpy(id, drive->id, size); 878c2ecf20Sopenharmony_ci ata_id_to_hd_driveid(id); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (copy_to_user(argp, id, size)) 908c2ecf20Sopenharmony_ci rc = -EFAULT; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci kfree(id); 938c2ecf20Sopenharmony_ciout: 948c2ecf20Sopenharmony_ci return rc; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return put_user_long((!!(drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) 1008c2ecf20Sopenharmony_ci << IDE_NICE_DSC_OVERLAP) | 1018c2ecf20Sopenharmony_ci (!!(drive->dev_flags & IDE_DFLAG_NICE1) 1028c2ecf20Sopenharmony_ci << IDE_NICE_1), arg); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) 1088c2ecf20Sopenharmony_ci return -EPERM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && 1118c2ecf20Sopenharmony_ci (drive->media != ide_tape)) 1128c2ecf20Sopenharmony_ci return -EPERM; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if ((arg >> IDE_NICE_DSC_OVERLAP) & 1) 1158c2ecf20Sopenharmony_ci drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP; 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if ((arg >> IDE_NICE_1) & 1) 1208c2ecf20Sopenharmony_ci drive->dev_flags |= IDE_DFLAG_NICE1; 1218c2ecf20Sopenharmony_ci else 1228c2ecf20Sopenharmony_ci drive->dev_flags &= ~IDE_DFLAG_NICE1; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int ide_cmd_ioctl(ide_drive_t *drive, void __user *argp) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci u8 *buf = NULL; 1308c2ecf20Sopenharmony_ci int bufsize = 0, err = 0; 1318c2ecf20Sopenharmony_ci u8 args[4], xfer_rate = 0; 1328c2ecf20Sopenharmony_ci struct ide_cmd cmd; 1338c2ecf20Sopenharmony_ci struct ide_taskfile *tf = &cmd.tf; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (NULL == argp) { 1368c2ecf20Sopenharmony_ci struct request *rq; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); 1398c2ecf20Sopenharmony_ci ide_req(rq)->type = ATA_PRIV_TASKFILE; 1408c2ecf20Sopenharmony_ci blk_execute_rq(drive->queue, NULL, rq, 0); 1418c2ecf20Sopenharmony_ci err = scsi_req(rq)->result ? -EIO : 0; 1428c2ecf20Sopenharmony_ci blk_put_request(rq); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (copy_from_user(args, argp, 4)) 1488c2ecf20Sopenharmony_ci return -EFAULT; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 1518c2ecf20Sopenharmony_ci tf->feature = args[2]; 1528c2ecf20Sopenharmony_ci if (args[0] == ATA_CMD_SMART) { 1538c2ecf20Sopenharmony_ci tf->nsect = args[3]; 1548c2ecf20Sopenharmony_ci tf->lbal = args[1]; 1558c2ecf20Sopenharmony_ci tf->lbam = ATA_SMART_LBAM_PASS; 1568c2ecf20Sopenharmony_ci tf->lbah = ATA_SMART_LBAH_PASS; 1578c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_OUT_TF; 1588c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_NSECT; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci tf->nsect = args[1]; 1618c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT; 1628c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_NSECT; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci tf->command = args[0]; 1658c2ecf20Sopenharmony_ci cmd.protocol = args[3] ? ATA_PROT_PIO : ATA_PROT_NODATA; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (args[3]) { 1688c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_IO_16BIT; 1698c2ecf20Sopenharmony_ci bufsize = SECTOR_SIZE * args[3]; 1708c2ecf20Sopenharmony_ci buf = kzalloc(bufsize, GFP_KERNEL); 1718c2ecf20Sopenharmony_ci if (buf == NULL) 1728c2ecf20Sopenharmony_ci return -ENOMEM; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (tf->command == ATA_CMD_SET_FEATURES && 1768c2ecf20Sopenharmony_ci tf->feature == SETFEATURES_XFER && 1778c2ecf20Sopenharmony_ci tf->nsect >= XFER_SW_DMA_0) { 1788c2ecf20Sopenharmony_ci xfer_rate = ide_find_dma_mode(drive, tf->nsect); 1798c2ecf20Sopenharmony_ci if (xfer_rate != tf->nsect) { 1808c2ecf20Sopenharmony_ci err = -EINVAL; 1818c2ecf20Sopenharmony_ci goto abort; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci cmd.tf_flags |= IDE_TFLAG_SET_XFER; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci err = ide_raw_taskfile(drive, &cmd, buf, args[3]); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci args[0] = tf->status; 1908c2ecf20Sopenharmony_ci args[1] = tf->error; 1918c2ecf20Sopenharmony_ci args[2] = tf->nsect; 1928c2ecf20Sopenharmony_ciabort: 1938c2ecf20Sopenharmony_ci if (copy_to_user(argp, &args, 4)) 1948c2ecf20Sopenharmony_ci err = -EFAULT; 1958c2ecf20Sopenharmony_ci if (buf) { 1968c2ecf20Sopenharmony_ci if (copy_to_user((argp + 4), buf, bufsize)) 1978c2ecf20Sopenharmony_ci err = -EFAULT; 1988c2ecf20Sopenharmony_ci kfree(buf); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci return err; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int ide_task_ioctl(ide_drive_t *drive, void __user *p) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int err = 0; 2068c2ecf20Sopenharmony_ci u8 args[7]; 2078c2ecf20Sopenharmony_ci struct ide_cmd cmd; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (copy_from_user(args, p, 7)) 2108c2ecf20Sopenharmony_ci return -EFAULT; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 2138c2ecf20Sopenharmony_ci memcpy(&cmd.tf.feature, &args[1], 6); 2148c2ecf20Sopenharmony_ci cmd.tf.command = args[0]; 2158c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; 2168c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci err = ide_no_data_taskfile(drive, &cmd); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci args[0] = cmd.tf.command; 2218c2ecf20Sopenharmony_ci memcpy(&args[1], &cmd.tf.feature, 6); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (copy_to_user(p, args, 7)) 2248c2ecf20Sopenharmony_ci err = -EFAULT; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return err; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int generic_drive_reset(ide_drive_t *drive) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct request *rq; 2328c2ecf20Sopenharmony_ci int ret = 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); 2358c2ecf20Sopenharmony_ci ide_req(rq)->type = ATA_PRIV_MISC; 2368c2ecf20Sopenharmony_ci scsi_req(rq)->cmd_len = 1; 2378c2ecf20Sopenharmony_ci scsi_req(rq)->cmd[0] = REQ_DRIVE_RESET; 2388c2ecf20Sopenharmony_ci blk_execute_rq(drive->queue, NULL, rq, 1); 2398c2ecf20Sopenharmony_ci ret = scsi_req(rq)->result; 2408c2ecf20Sopenharmony_ci blk_put_request(rq); 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciint generic_ide_ioctl(ide_drive_t *drive, struct block_device *bdev, 2458c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int err; 2488c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (in_compat_syscall()) 2518c2ecf20Sopenharmony_ci argp = compat_ptr(arg); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings); 2548c2ecf20Sopenharmony_ci if (err != -EOPNOTSUPP) 2558c2ecf20Sopenharmony_ci return err; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci switch (cmd) { 2588c2ecf20Sopenharmony_ci case HDIO_OBSOLETE_IDENTITY: 2598c2ecf20Sopenharmony_ci case HDIO_GET_IDENTITY: 2608c2ecf20Sopenharmony_ci if (bdev_is_partition(bdev)) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci return ide_get_identity_ioctl(drive, cmd, argp); 2638c2ecf20Sopenharmony_ci case HDIO_GET_NICE: 2648c2ecf20Sopenharmony_ci return ide_get_nice_ioctl(drive, arg); 2658c2ecf20Sopenharmony_ci case HDIO_SET_NICE: 2668c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 2678c2ecf20Sopenharmony_ci return -EACCES; 2688c2ecf20Sopenharmony_ci return ide_set_nice_ioctl(drive, arg); 2698c2ecf20Sopenharmony_ci#ifdef CONFIG_IDE_TASK_IOCTL 2708c2ecf20Sopenharmony_ci case HDIO_DRIVE_TASKFILE: 2718c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) 2728c2ecf20Sopenharmony_ci return -EACCES; 2738c2ecf20Sopenharmony_ci /* missing compat handler for HDIO_DRIVE_TASKFILE */ 2748c2ecf20Sopenharmony_ci if (in_compat_syscall()) 2758c2ecf20Sopenharmony_ci return -ENOTTY; 2768c2ecf20Sopenharmony_ci if (drive->media == ide_disk) 2778c2ecf20Sopenharmony_ci return ide_taskfile_ioctl(drive, arg); 2788c2ecf20Sopenharmony_ci return -ENOMSG; 2798c2ecf20Sopenharmony_ci#endif 2808c2ecf20Sopenharmony_ci case HDIO_DRIVE_CMD: 2818c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 2828c2ecf20Sopenharmony_ci return -EACCES; 2838c2ecf20Sopenharmony_ci return ide_cmd_ioctl(drive, argp); 2848c2ecf20Sopenharmony_ci case HDIO_DRIVE_TASK: 2858c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 2868c2ecf20Sopenharmony_ci return -EACCES; 2878c2ecf20Sopenharmony_ci return ide_task_ioctl(drive, argp); 2888c2ecf20Sopenharmony_ci case HDIO_DRIVE_RESET: 2898c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 2908c2ecf20Sopenharmony_ci return -EACCES; 2918c2ecf20Sopenharmony_ci return generic_drive_reset(drive); 2928c2ecf20Sopenharmony_ci case HDIO_GET_BUSSTATE: 2938c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 2948c2ecf20Sopenharmony_ci return -EACCES; 2958c2ecf20Sopenharmony_ci if (put_user_long(BUSSTATE_ON, arg)) 2968c2ecf20Sopenharmony_ci return -EFAULT; 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci case HDIO_SET_BUSSTATE: 2998c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 3008c2ecf20Sopenharmony_ci return -EACCES; 3018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(generic_ide_ioctl); 307