mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-26 19:51:35 +01:00
ioctl: move doc to README
This commit is contained in:
53
README.adoc
53
README.adoc
@@ -3094,6 +3094,57 @@ ab
|
|||||||
cd
|
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
|
==== Character devices
|
||||||
|
|
||||||
In guest:
|
In guest:
|
||||||
@@ -3193,7 +3244,7 @@ Sources:
|
|||||||
* link:kernel_module/user/anonymous_inode.c[]
|
* link:kernel_module/user/anonymous_inode.c[]
|
||||||
* link:rootfs_overlay/anonymous_inode.sh[]
|
* 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 <<ioctl>> from a debugfs entry by using `anon_inode_getfd`.
|
||||||
|
|
||||||
Reads to that inode return the sequence: `1`, `10`, `100`, ... `10000000`, `1`, `100`, ...
|
Reads to that inode return the sequence: `1`, `10`, `100`, ... `10000000`, `1`, `100`, ...
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
... link:dep.c[]
|
... link:dep.c[]
|
||||||
... link:dep2.c[]
|
... link:dep2.c[]
|
||||||
. Pseudo filesystems
|
. Pseudo filesystems
|
||||||
.. link:ioctl.c[]
|
|
||||||
.. link:mmap.c[]
|
.. link:mmap.c[]
|
||||||
.. link:poll.c[]
|
.. link:poll.c[]
|
||||||
. Asynchronous
|
. Asynchronous
|
||||||
|
|||||||
@@ -1,23 +1,4 @@
|
|||||||
/*
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#ioctl */
|
||||||
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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ typedef struct {
|
|||||||
int j;
|
int j;
|
||||||
} lkmc_ioctl_struct;
|
} 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
|
#define LKMC_IOCTL_MAGIC 0x33
|
||||||
|
|
||||||
/*
|
/* I think those number do not *need* to be unique across, that is just to help debugging:
|
||||||
* I think those number does 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
|
* 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.
|
* 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
|
* 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?
|
* 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)
|
#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)
|
#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#ioctl */
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -12,43 +14,53 @@
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
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;
|
lkmc_ioctl_struct arg_struct;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
puts("Usage: ./prog <ioctl-file>");
|
puts("Usage: ./prog <ioctl-file> <request> [<arg>...]");
|
||||||
return EXIT_FAILURE;
|
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) {
|
if (fd == -1) {
|
||||||
perror("open");
|
perror("open");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
/* 0 */
|
switch (request)
|
||||||
{
|
{
|
||||||
arg_int = 1;
|
case 0:
|
||||||
ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
|
arg_int = arg0;
|
||||||
if (ret == -1) {
|
ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
|
||||||
perror("ioctl");
|
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;
|
return EXIT_FAILURE;
|
||||||
}
|
|
||||||
printf("arg = %d\n", arg_int);
|
|
||||||
printf("ret = %d\n", ret);
|
|
||||||
printf("errno = %d\n", errno);
|
|
||||||
}
|
}
|
||||||
puts("");
|
if (ret == -1) {
|
||||||
/* 1 */
|
perror("ioctl");
|
||||||
{
|
|
||||||
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);
|
printf("errno = %d\n", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
f=/sys/kernel/debug/lkmc_ioctl
|
||||||
insmod /ioctl.ko
|
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
|
rmmod ioctl
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ for test in \
|
|||||||
/character_device_create.sh \
|
/character_device_create.sh \
|
||||||
/debugfs.sh \
|
/debugfs.sh \
|
||||||
/fops.sh \
|
/fops.sh \
|
||||||
|
/ioctl.sh \
|
||||||
/procfs.sh \
|
/procfs.sh \
|
||||||
/seq_file.sh \
|
/seq_file.sh \
|
||||||
/seq_file_single_open.sh \
|
/seq_file_single_open.sh \
|
||||||
|
|||||||
Reference in New Issue
Block a user