Domanda Come posso usare ioctl () per manipolare il mio modulo del kernel?


Quindi sto provando a scrivere un modulo del kernel che usa il file linux / timer.h. L'ho fatto funzionare all'interno del modulo, e ora sto cercando di farlo funzionare da un programma utente.

Ecco il mio modulo del kernel:

//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>

#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0

MODULE_LICENSE("Dual BSD/GPL");

static struct timer_list my_timer;

struct file_operations FileOps = 
{
    //No File Operations for this timer.
};

//Function to perform when timer expires.
void TimerExpire(int data)
{
    printk("Timer Data: %d\n", data);
}

//Function to set up timers.
void TimerSetup(void)
{
    setup_timer(&my_timer, TimerExpire, 5678);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(5000));
}

//Module Init and Exit Functions.
int init_module(void)
{
    int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);

    if (initResult < 0)
    {
        printk("Cannot obtain major number %d\n", MAJOR_NUM);

        return initResult;
    }

printk("Loading MyTimer Kernel Module...\n");


return 0;
}
void cleanup_module(void)
{
    unregister_chrdev(MAJOR_NUM, "mytimer");
    printk("Unloading MyTimer Kernel Module...\n");
}

Più specificamente, voglio che il mio programma utente chiami la funzione TimerSetup (). So che avrò bisogno di usare ioctl () ma non sono sicuro di come specificare nel mio MODULO FILE che TimerSetup () dovrebbe essere chiamato tramite ioctl ().

Inoltre, la mia seconda domanda: sono stato in grado di insmod il mio modulo e anche mknod in / dev / mytimer con il numero maggiore corretto. Ma quando ho provato ad aprirlo () così da poter ottenere il descrittore di file da esso, ha continuato a restituire -1, che presumo sia sbagliato. Mi sono assicurato che le autorizzazioni fossero valide (in effetti l'ho fatto 777 per sicurezza) ... Non funziona ancora ... C'è qualcosa che mi manca?

Ecco il programma utente nel caso in cui:

#include <stdio.h>

int main(int argc, char* argv[])
{
    int fd = open("/dev/mytimer", "r");
    printf("fd: %d\n", fd);

    return 0;
}

12
2018-02-15 06:47


origine


risposte:


Il codice di esempio di cui hai bisogno può essere trovato in drivers/watchdog/softdog.c (da Linux 2.6.33 al momento in cui questo è stato scritto), che illustra le corrette operazioni sui file e come consentire a userland di riempire una struttura con ioctl ().

In realtà è un ottimo tutorial di lavoro per chiunque abbia bisogno di scrivere driver di dispositivo di carattere banale.

Ho sezionato l'interfaccia ioctl del softdog quando rispondendo alla mia stessa domanda, che potrebbe esserti utile.

Ecco il succo di ciò (anche se non è esaustivo) ...

In softdog_ioctl() si vede una semplice inizializzazione di struct watchdog_info che pubblicizza funzionalità, versione e informazioni sul dispositivo:

    static const struct watchdog_info ident = {
            .options =              WDIOF_SETTIMEOUT |
                                    WDIOF_KEEPALIVEPING |
                                    WDIOF_MAGICCLOSE,
            .firmware_version =     0,
            .identity =             "Software Watchdog",
    };

Quindi esaminiamo un caso semplice in cui l'utente desidera solo ottenere queste funzionalità:

    switch (cmd) {
    case WDIOC_GETSUPPORT:
            return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;

... che ovviamente riempirà lo userspace corrispondente watchdog_info con i valori inizializzati sopra. Se copy_to_user () fallisce, viene restituito -EFAULT che fa in modo che la chiamata ioctl () userspace corrispondente restituisca -1 con un errno significativo impostato.

Nota, le richieste magiche sono effettivamente definite in linux / watchdog.h, in modo che il kernel e lo spazio utente li condividano:

#define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)

WDIOC ovviamente a significare "Watchdog ioctl"

Puoi facilmente fare un passo in più, fare fare qualcosa al tuo autista e posizionare il risultato di quel qualcosa nella struttura e copiarlo nello spazio utente. Ad esempio, se struct watchdog_info aveva anche un membro __u32 result_code. Nota, __u32 è solo la versione del kernel di uint32_t.

Con ioctl (), l'utente passa l'indirizzo di un oggetto, sia esso una struttura, un numero intero, qualunque sia il kernel che si aspetta che il kernel scriva la sua risposta in un oggetto identico e copi i risultati nell'indirizzo fornito.

La seconda cosa che devi fare è assicurarti che il tuo dispositivo sappia cosa fare quando qualcuno apre, legge da esso, scrive su di esso o usa un hook come ioctl (), che puoi facilmente vedere studiando il softdog.

Di interesse è:

static const struct file_operations softdog_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .write          = softdog_write,
        .unlocked_ioctl = softdog_ioctl,
        .open           = softdog_open,
        .release        = softdog_release,
};

Dove vedi il gestore di unlocked_ioctl che sta per ... hai indovinato, softdog_ioctl ().

Penso che potresti giustapporre uno strato di complessità che in realtà non esiste quando si ha a che fare con ioctl (), è davvero così semplice. Per lo stesso motivo, la maggior parte degli sviluppatori del kernel aggrotta le sopracciglia su nuove interfacce ioctl che vengono aggiunte a meno che non siano assolutamente necessarie. È troppo facile perdere traccia del tipo che ioctl () sta per riempire con la magia che si usa per farlo, che è la ragione principale per cui copy_to_user () fallisce spesso con il risultato che il kernel marcisce con orde di processi userspace bloccati in il sonno del disco.

Per un timer, sono d'accordo, ioctl () è la via più breve per la sanità mentale.


20
2018-02-15 07:16



Ti manca un .open puntatore di funzione nel tuo file_operations struttura per specificare la funzione da chiamare quando un processo tenta di aprire il file del dispositivo. Dovrai specificare a .ioctl puntatore di funzione anche per la funzione ioctl.

Prova a leggere La guida alla programmazione del modulo kernel Kernel Linux, in particolare i capitoli 4 (Character Device Files) e 7 (Talking to Device Files).

capitolo 4 introduce il file_operations struttura, che contiene puntatori alle funzioni definite dal modulo / driver che eseguono varie operazioni come open o ioctl.

Capitolo 7 fornisce informazioni sulla comunicazione con un modulo / unità tramite ioctls.

Driver di dispositivo Linux, terza edizione è un'altra buona risorsa.


8
2018-02-15 07:13



Esempio eseguibile minimo

Testato in un ambiente QEMU + Buildroot completamente riproducibile, quindi potrebbe aiutare gli altri a ottenere il loro ioctl lavoro. GitHub a monte: modulo del kernel | intestazione condivisa | userland.

La parte più fastidiosa è stata la comprensione che alcuni ID bassi sono stati dirottati: ioctl non viene chiamato se cmd = 2 , devi usare _IOx macro.

Modulo del kernel:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/printk.h> /* 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)

Intestazione condivisa:

#ifndef IOCTL_H
#define IOCTL_H

#include <linux/ioctl.h>

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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#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 <ioctl-file>");
        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;
}

0
2018-06-18 10:09