How do I use ioctl() to manipulate my kernel module?

后端 未结 3 1138
别那么骄傲
别那么骄傲 2020-12-08 03:45

So I\'m trying to write a kernel module that uses the linux/timer.h file. I got it to work inside just the module, and now I am trying to get it to work from a user program.

3条回答
  •  情书的邮戳
    2020-12-08 03:47

    Minimal runnable example

    Tested in a fully reproducible QEMU + Buildroot environment, so might help others get their ioctl working. GitHub upstream: kernel module | shared header | userland.

    The most annoying part was understanding that some low ids are hijacked: ioctl is not called if cmd = 2 , you have to use _IOx macros.

    Kernel module:

    #include  /* copy_from_user, copy_to_user */
    #include 
    #include 
    #include  /* printk */
    
    #include "ioctl.h"
    
    MODULE_LICENSE("GPL");
    
    static struct dentry *dir;
    
    static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
    {
        void __user *arg_user;
        union {
            int i;
            lkmc_ioctl_struct s;
        } arg_kernel;
    
        arg_user = (void __user *)argp;
        pr_info("cmd = %x\n", cmd);
        switch (cmd) {
            case LKMC_IOCTL_INC:
                if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) {
                    return -EFAULT;
                }
                pr_info("0 arg = %d\n", arg_kernel.i);
                arg_kernel.i += 1;
                if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) {
                    return -EFAULT;
                }
            break;
            case LKMC_IOCTL_INC_DEC:
                if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) {
                    return -EFAULT;
                }
                pr_info("1 arg = %d %d\n", arg_kernel.s.i, arg_kernel.s.j);
                arg_kernel.s.i += 1;
                arg_kernel.s.j -= 1;
                if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) {
                    return -EFAULT;
                }
            break;
            default:
                return -EINVAL;
            break;
        }
        return 0;
    }
    
    static const struct file_operations fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = unlocked_ioctl
    };
    
    static int myinit(void)
    {
        dir = debugfs_create_dir("lkmc_ioctl", 0);
        /* ioctl permissions are not automatically restricted by rwx as for read / write,
         * but we could of course implement that ourselves:
         * https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */
        debugfs_create_file("f", 0, dir, NULL, &fops);
        return 0;
    }
    
    static void myexit(void)
    {
        debugfs_remove_recursive(dir);
    }
    
    module_init(myinit)
    module_exit(myexit)
    

    Shared header between the kernel module and userland:

    ioctl.h

    #ifndef IOCTL_H
    #define IOCTL_H
    
    #include 
    
    typedef struct {
        int i;
        int j;
    } lkmc_ioctl_struct;
    #define LKMC_IOCTL_MAGIC 0x33
    #define LKMC_IOCTL_INC     _IOWR(LKMC_IOCTL_MAGIC, 0, int)
    #define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)
    
    #endif
    

    Userland:

    #define _GNU_SOURCE
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include "../ioctl.h"
    
    int main(int argc, char **argv)
    {
        int fd, arg_int, ret;
        lkmc_ioctl_struct arg_struct;
    
        if (argc < 2) {
            puts("Usage: ./prog ");
            return EXIT_FAILURE;
        }
        fd = open(argv[1], O_RDONLY);
        if (fd == -1) {
            perror("open");
            return EXIT_FAILURE;
        }
        /* 0 */
        {
            arg_int = 1;
            ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
            if (ret == -1) {
                perror("ioctl");
                return EXIT_FAILURE;
            }
            printf("arg = %d\n", arg_int);
            printf("ret = %d\n", ret);
            printf("errno = %d\n", errno);
        }
        puts("");
        /* 1 */
        {
            arg_struct.i = 1;
            arg_struct.j = 1;
            ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct);
            if (ret == -1) {
                perror("ioctl");
                return EXIT_FAILURE;
            }
            printf("arg = %d %d\n", arg_struct.i, arg_struct.j);
            printf("ret = %d\n", ret);
            printf("errno = %d\n", errno);
        }
        close(fd);
        return EXIT_SUCCESS;
    }
    

提交回复
热议问题