From d840b0cb65ca211ab4cf8a6eef42ffaf4a9b0431 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Sun, 1 Jul 2018 17:44:38 +0100 Subject: [PATCH] ioctl: move doc to README --- README.adoc | 53 ++++++++++++++++++++++++++++++++- kernel_module/README.adoc | 1 - kernel_module/ioctl.c | 21 +------------ kernel_module/ioctl.h | 18 +++++++++--- kernel_module/user/ioctl.c | 60 +++++++++++++++++++++++--------------- rootfs_overlay/ioctl.sh | 4 ++- rootfs_overlay/test_all.sh | 1 + 7 files changed, 107 insertions(+), 51 deletions(-) diff --git a/README.adoc b/README.adoc index 99511dd..d235c75 100644 --- a/README.adoc +++ b/README.adoc @@ -3094,6 +3094,57 @@ ab cd .... +==== ioctl + +In guest: + +.... +/ioctl.sh +echo $? +.... + +Outcome: the test passes: + +.... +0 +.... + +Sources: + +* link:kernel_module/ioctl.c[] +* link:kernel_module/ioctl.h[] +* link:kernel_module/user/ioctl.c[] +* link:rootfs_overlay/ioctl.sh[] + +The `ioctl` system call is the best ways to provide an arbitrary number of parameters to the kernel in a single go. + +It is therefore one of the most important methods of communication with real device drivers, which often take several fields as input. + +`ioctl` takes as input: + +* an integer `request` : it usually identifies what type of operation we want to do on this call +* an untyped pointer to memory: can be anything, but is typically a pointer to a `struct` ++ +The type of the `struct` often depends on the `request` input ++ +This `struct` is defined on a uapi-style C header that is used both to compile the kernel module and the userland executable. ++ +The fields of this `struct` can be thought of as arbitrary input parameters. + +And the output is: + +* an integer return value. `man ioctl` documents: ++ +____ +Usually, on success zero is returned. A few `ioctl()` requests use the return value as an output parameter and return a nonnegative value on success. On error, -1 is returned, and errno is set appropriately. +____ +* the input pointer data may be overwritten to contain arbitrary output + +Bibliography: + +* https://stackoverflow.com/questions/2264384/how-do-i-use-ioctl-to-manipulate-my-kernel-module/44613896#44613896 +* https://askubuntu.com/questions/54239/problem-with-ioctl-in-a-simple-kernel-module/926675#926675 + ==== Character devices In guest: @@ -3193,7 +3244,7 @@ Sources: * link:kernel_module/user/anonymous_inode.c[] * link:rootfs_overlay/anonymous_inode.sh[] -This example gets an anonymous inode via `ioctl` from a debugfs entry by using `anon_inode_getfd`. +This example gets an anonymous inode via <> from a debugfs entry by using `anon_inode_getfd`. Reads to that inode return the sequence: `1`, `10`, `100`, ... `10000000`, `1`, `100`, ... diff --git a/kernel_module/README.adoc b/kernel_module/README.adoc index c7c6a6f..b63ab0a 100644 --- a/kernel_module/README.adoc +++ b/kernel_module/README.adoc @@ -19,7 +19,6 @@ ... link:dep.c[] ... link:dep2.c[] . Pseudo filesystems -.. link:ioctl.c[] .. link:mmap.c[] .. link:poll.c[] . Asynchronous diff --git a/kernel_module/ioctl.c b/kernel_module/ioctl.c index cd2f9f9..e045c7f 100644 --- a/kernel_module/ioctl.c +++ b/kernel_module/ioctl.c @@ -1,23 +1,4 @@ -/* -Input: an integer (with some annoying restrictions) and a pointer - -Output: - -- positive integer return value, which for sanity should only be used with negative values for success -- the input pointer data may be overwritten to contain output - -Feels like an archaic API... so many weird restrictions and types for something that could be so simple! - -Documentation/ioctl/ioctl-number.txt has some info: - - _IO an ioctl with no parameters - _IOW an ioctl with write parameters (copy_from_user) - _IOR an ioctl with read parameters (copy_to_user) - _IOWR an ioctl with both write and read parameters. - -- https://stackoverflow.com/questions/2264384/how-do-i-use-ioctl-to-manipulate-my-kernel-module/44613896#44613896 -- https://askubuntu.com/questions/54239/problem-with-ioctl-in-a-simple-kernel-module/926675#926675 -*/ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#ioctl */ #include #include diff --git a/kernel_module/ioctl.h b/kernel_module/ioctl.h index 3f4d5bb..14d6cdf 100644 --- a/kernel_module/ioctl.h +++ b/kernel_module/ioctl.h @@ -9,11 +9,10 @@ typedef struct { int j; } lkmc_ioctl_struct; -/* Some random number I can't understand how to choose. */ +/* TODO some random number I can't understand how to choose. */ #define LKMC_IOCTL_MAGIC 0x33 -/* - * I think those number does not *need* to be unique across, that is just to help debugging: +/* I think those number do not *need* to be unique across, that is just to help debugging: * https://stackoverflow.com/questions/22496123/what-is-the-meaning-of-this-macro-iormy-macig-0-int * * However, the ioctl syscall highjacks several low values at do_vfs_ioctl, e.g. @@ -25,8 +24,19 @@ typedef struct { * https://stackoverflow.com/questions/6125068/what-does-the-fd-cloexec-fcntl-flag-do * * TODO are the W or R of _IOx and type functional, or only to help with uniqueness? - * */ + * + * Documentation/ioctl/ioctl-number.txt documents: + * + * .... + * _IO an ioctl with no parameters + * _IOW an ioctl with write parameters (copy_from_user) + * _IOR an ioctl with read parameters (copy_to_user) + * _IOWR an ioctl with both write and read parameters. + * .... + */ +/* Take an int, increment it. */ #define LKMC_IOCTL_INC _IOWR(LKMC_IOCTL_MAGIC, 0, int) +/* Take a struct with two ints, increment the first, and decrement the second. */ #define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct) #endif diff --git a/kernel_module/user/ioctl.c b/kernel_module/user/ioctl.c index 9df0b4c..7741747 100644 --- a/kernel_module/user/ioctl.c +++ b/kernel_module/user/ioctl.c @@ -1,3 +1,5 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#ioctl */ + #define _GNU_SOURCE #include #include @@ -12,43 +14,53 @@ int main(int argc, char **argv) { - int fd, arg_int, ret; + char *ioctl_path; + int fd, request, arg0, arg1, arg_int, ret; lkmc_ioctl_struct arg_struct; if (argc < 2) { - puts("Usage: ./prog "); + puts("Usage: ./prog [...]"); return EXIT_FAILURE; } - fd = open(argv[1], O_RDONLY); + ioctl_path = argv[1]; + request = strtol(argv[2], NULL, 10); + if (argc > 3) { + arg0 = strtol(argv[3], NULL, 10); + } + if (argc > 4) { + arg1 = strtol(argv[4], NULL, 10); + } + + fd = open(ioctl_path, O_RDONLY); if (fd == -1) { perror("open"); return EXIT_FAILURE; } - /* 0 */ + switch (request) { - arg_int = 1; - ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int); - if (ret == -1) { - perror("ioctl"); + case 0: + arg_int = arg0; + ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int); + if (ret != -1) { + printf("%d\n", arg_int); + } + break; + case 1: + arg_struct.i = arg0; + arg_struct.j = arg1; + ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct); + if (ret != -1) { + printf("%d %d\n", arg_struct.i, arg_struct.j); + } + break; + default: + puts("error: unknown request"); 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); + if (ret == -1) { + perror("ioctl"); printf("errno = %d\n", errno); + return EXIT_FAILURE; } close(fd); return EXIT_SUCCESS; diff --git a/rootfs_overlay/ioctl.sh b/rootfs_overlay/ioctl.sh index 4c48308..16ed9c6 100755 --- a/rootfs_overlay/ioctl.sh +++ b/rootfs_overlay/ioctl.sh @@ -1,5 +1,7 @@ #!/bin/sh set -e +f=/sys/kernel/debug/lkmc_ioctl insmod /ioctl.ko -/ioctl.out /sys/kernel/debug/lkmc_ioctl +[ "$(/ioctl.out "$f" 0 1)" = 2 ] +[ "$(/ioctl.out "$f" 1 1 1)" = '2 0' ] rmmod ioctl diff --git a/rootfs_overlay/test_all.sh b/rootfs_overlay/test_all.sh index 1b5c133..83fdaf5 100755 --- a/rootfs_overlay/test_all.sh +++ b/rootfs_overlay/test_all.sh @@ -5,6 +5,7 @@ for test in \ /character_device_create.sh \ /debugfs.sh \ /fops.sh \ + /ioctl.sh \ /procfs.sh \ /seq_file.sh \ /seq_file_single_open.sh \