[PATCH 2/4] Add GPIO ioctl decoding
Dmitry V. Levin
ldv at altlinux.org
Wed Dec 30 02:07:42 UTC 2020
On Wed, Dec 23, 2020 at 06:07:46AM +0800, Kent Gibson wrote:
> Decode the GPIO character device ioctls first introduced in Linux v4.8,
> as well as those added in subsequent kernel releases up to Linux v5.7.
>
> Signed-off-by: Kent Gibson <warthog618 at gmail.com>
[...]
> diff --git a/gpio_ioctl.c b/gpio_ioctl.c
> new file mode 100644
> index 00000000..228b4cf4
> --- /dev/null
> +++ b/gpio_ioctl.c
> @@ -0,0 +1,252 @@
> +/*
> + * Copyright (c) 2020 The strace developers.
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier: LGPL-2.1-or-later
> + */
> +
> +#include "defs.h"
> +
> +#if HAVE_LINUX_GPIO_H
> +
> +#include <linux/gpio.h>
> +
> +#include MPERS_DEFS
There are no DEF_MPERS_TYPE definitions in this file, so
I don't think you need to include MPERS_DEFS.
> +#include "print_fields.h"
> +
> +#ifndef GPIOHANDLES_MAX
> +#define GPIOHANDLES_MAX 64
> +#endif
> +
> +#ifndef GPIOHANDLE_SET_CONFIG_IOCTL
> +/* added in Linux v5.5 */
> +#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0A, struct gpiohandle_config)
> +struct gpiohandle_config {
> + __u32 flags;
> + __u8 default_values[GPIOHANDLES_MAX];
> + __u32 padding[4];
> +};
We prefer stdint types (uint32_t, uint8_t, etc).
> +#endif
> +
> +#ifndef GPIO_GET_LINEINFO_WATCH_IOCTL
> +/* added in Linux v5.7 */
> +#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0B, struct gpioline_info)
> +#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0C, __u32)
Likewise.
> +#endif
> +
> +static int
> +print_gpiochip_info(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpiochip_info info;
> +
> + if (entering(tcp))
> + return 0;
> +
> + tprints(", ");
> + if (umove_or_printaddr(tcp, arg, &info))
> + return RVAL_IOCTL_DECODED;
> + PRINT_FIELD_CSTRING("{", info, name);
> + PRINT_FIELD_CSTRING(", ", info, label);
> + PRINT_FIELD_U(", ", info, lines);
> + tprints("}");
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +#include "xlat/gpio_line_flags.h"
> +
> +static int
> +print_gpioline_info(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpioline_info info;
> +
> + if (entering(tcp))
> + return 0;
> +
> + tprints(", ");
> + if (umove_or_printaddr_ignore_syserror(tcp, arg, &info))
> + return RVAL_IOCTL_DECODED;
Why did you choose to ignore syscall error here?
> + PRINT_FIELD_U("{", info, line_offset);
> + if (!tcp->u_rval) {
> + tprints(", flags=");
> + printflags(gpio_line_flags, info.flags, "GPIOLINE_FLAG_???");
> + PRINT_FIELD_CSTRING(", ", info, name);
> + PRINT_FIELD_CSTRING(", ", info, consumer);
> + }
> + tprints("}");
Both GPIO_GET_LINEINFO_IOCTL and GPIO_GET_LINEINFO_WATCH_IOCTL are
read-write ioctls, that is, the kernel reads struct gpioline_info on
entering syscall and writes it back on exiting. A comprehensive decoder
would do the same, i.e. print the relevant parts of the structure both on
entering and on exiting. There would be no need to use
umove_or_printaddr_ignore_syserror and ignore syscall errors.
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpioline_info_unwatch(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + __u32 offset;
> +
> + if (entering(tcp))
> + return 0;
Although GPIO_GET_LINEINFO_UNWATCH_IOCTL is an _IOWR ioctl, the kernel
only reads the integer value, so this ioctl can be fully decoded on
entering syscall.
> +
> + tprints(", ");
> + if (umove_or_printaddr_ignore_syserror(tcp, arg, &offset))
> + return RVAL_IOCTL_DECODED;
> +
> + tprintf("{offset=%u}", offset);
I'm not sure whether it worth printing the integer value as the only field
in an imaginary structure. If not, you could just use
printnum_int(tcp, arg, "%u");
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +#include "xlat/gpio_handle_flags.h"
> +
> +static int
> +print_gpiohandle_request(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpiohandle_request hr;
> +
> + if (entering(tcp))
> + return 0;
> +
> + tprints(", ");
> + if (umove_or_printaddr_ignore_syserror(tcp, arg, &hr))
> + return RVAL_IOCTL_DECODED;
Why did you choose to ignore syscall error here?
> +
> + PRINT_FIELD_U("{", hr, lines);
> + tprints(", lineoffsets=");
> + if (hr.lines > GPIOHANDLES_MAX)
> + hr.lines = GPIOHANDLES_MAX;
> + print_local_array_ex(tcp, hr.lineoffsets, hr.lines,
> + sizeof(hr.lineoffsets[0]), print_uint32_array_member,
> + NULL, 0, NULL, NULL);
> + PRINT_FIELD_FLAGS(", ", hr, flags, gpio_handle_flags,
> + "GPIOHANDLE_FLAG_???");
> + tprints(", default_values=");
> + print_local_array_ex(tcp, hr.default_values, hr.lines,
> + sizeof(hr.default_values[0]), print_uint8_array_member,
> + NULL, 0, NULL, NULL);
> + PRINT_FIELD_CSTRING(", ", hr, consumer_label);
> + if (!tcp->u_rval)
> + PRINT_FIELD_FD(", ", hr, fd, tcp);
> + tprints("}");
GPIO_GET_LINEHANDLE_IOCTL is a read-write ioctl, that is, the kernel reads
struct gpiohandle_request on entering syscall and writes it back on
exiting. A comprehensive decoder would do the same, i.e. print the
relevant parts of the structure both on entering and on exiting. There
would be no need to use umove_or_printaddr_ignore_syserror and ignore
syscall errors.
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +#include "xlat/gpio_event_flags.h"
> +
> +static int
> +print_gpioevent_request(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpioevent_request er;
> +
> + if (entering(tcp))
> + return 0;
> +
> + tprints(", ");
> + if (umove_or_printaddr_ignore_syserror(tcp, arg, &er))
> + return RVAL_IOCTL_DECODED;
> +
> + PRINT_FIELD_U("{", er, lineoffset);
> + PRINT_FIELD_FLAGS(", ", er, handleflags, gpio_handle_flags,
> + "GPIOHANDLE_FLAG_???");
> + PRINT_FIELD_FLAGS(", ", er, eventflags, gpio_event_flags,
> + "GPIOEVENT_FLAG_???");
> + PRINT_FIELD_CSTRING(", ", er, consumer_label);
> + if (!tcp->u_rval)
> + PRINT_FIELD_FD(", ", er, fd, tcp);
> + tprints("}");
> +
> + return RVAL_IOCTL_DECODED;
> +}
Likewise, GPIO_GET_LINEEVENT_IOCTL is a read-write ioctl.
> +
> +static void
> +print_handle_data(struct tcb *const tcp, struct gpiohandle_data *vals)
> +{
> + tprints("{values=");
> + print_local_array(tcp, vals->values, print_uint8_array_member);
> + tprints("}");
> +}
> +
> +static int
> +print_gpiohandle_get_values(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpiohandle_data vals;
> +
> + if (entering(tcp))
> + return 0;
> +
> + tprints(", ");
> + if (umove_or_printaddr(tcp, arg, &vals))
> + return RVAL_IOCTL_DECODED;
> +
> + print_handle_data(tcp, &vals);
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpiohandle_set_values(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpiohandle_data vals;
> +
> + if (exiting(tcp))
> + return 0;
Although GPIOHANDLE_SET_LINE_VALUES_IOCTL is an _IOWR ioctl, the kernel
only reads struct gpiohandle_data, so this ioctl can be fully decoded on
entering syscall. If this function returns RVAL_IOCTL_DECODED, it won't
be called on exiting syscall, so there is no need to check for exiting.
> +
> + tprints(", ");
> + if (umove_or_printaddr_ignore_syserror(tcp, arg, &vals))
> + return RVAL_IOCTL_DECODED;
There is no syscall error on entering syscall.
> +
> + print_handle_data(tcp, &vals);
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpiohandle_set_config(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> + struct gpiohandle_config hc;
> +
> + if (exiting(tcp))
> + return 0;
Likewise, for GPIOHANDLE_SET_CONFIG_IOCTL.
> + tprints(", ");
> + if (umove_or_printaddr_ignore_syserror(tcp, arg, &hc))
> + return RVAL_IOCTL_DECODED;
Likewise, there is no syscall error on entering syscall.
> +
> + PRINT_FIELD_FLAGS("{", hc, flags, gpio_handle_flags,
> + "GPIOHANDLE_FLAG_???");
> + tprints(", default_values=");
> + print_local_array(tcp, hc.default_values, print_uint8_array_member);
> + tprints("}");
> +
> + return RVAL_IOCTL_DECODED;
> +}
> +
> +MPERS_PRINTER_DECL(int, gpio_ioctl,
> + struct tcb *const tcp, const unsigned int code,
> + const kernel_ulong_t arg)
There are no DEF_MPERS_TYPE definitions in this file, so
I don't think you need to use MPERS_PRINTER_DECL.
> +{
> + switch (code) {
> + case GPIO_GET_CHIPINFO_IOCTL:
> + return print_gpiochip_info(tcp, arg);
> + case GPIO_GET_LINEINFO_UNWATCH_IOCTL:
> + return print_gpioline_info_unwatch(tcp, arg);
> + case GPIO_GET_LINEINFO_IOCTL:
> + case GPIO_GET_LINEINFO_WATCH_IOCTL:
> + return print_gpioline_info(tcp, arg);
> + case GPIO_GET_LINEHANDLE_IOCTL:
> + return print_gpiohandle_request(tcp, arg);
> + case GPIO_GET_LINEEVENT_IOCTL:
> + return print_gpioevent_request(tcp, arg);
> + case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
> + return print_gpiohandle_get_values(tcp, arg);
> + case GPIOHANDLE_SET_LINE_VALUES_IOCTL:
> + return print_gpiohandle_set_values(tcp, arg);
> + case GPIOHANDLE_SET_CONFIG_IOCTL:
> + return print_gpiohandle_set_config(tcp, arg);
> + default:
> + return RVAL_DECODED;
> + }
> +}
> +
> +#endif
> diff --git a/ioctl.c b/ioctl.c
> index 1e3ec910..fa4aed27 100644
> --- a/ioctl.c
> +++ b/ioctl.c
> @@ -347,6 +347,10 @@ ioctl_decode(struct tcb *tcp)
> #ifdef HAVE_LINUX_KVM_H
> case 0xae:
> return kvm_ioctl(tcp, code, arg);
> +#endif
> +#ifdef HAVE_LINUX_GPIO_H
> + case 0xb4:
> + return gpio_ioctl(tcp, code, arg);
> #endif
> case 0xb7:
> return nsfs_ioctl(tcp, code, arg);
> diff --git a/xlat/gpio_event_flags.in b/xlat/gpio_event_flags.in
> new file mode 100644
> index 00000000..9b87f50d
> --- /dev/null
> +++ b/xlat/gpio_event_flags.in
> @@ -0,0 +1,3 @@
> +GPIOEVENT_REQUEST_BOTH_EDGES 0x00000003
> +GPIOEVENT_REQUEST_RISING_EDGE 0x00000001
> +GPIOEVENT_REQUEST_FALLING_EDGE 0x00000002
> diff --git a/xlat/gpio_handle_flags.in b/xlat/gpio_handle_flags.in
> new file mode 100644
> index 00000000..2b027647
> --- /dev/null
> +++ b/xlat/gpio_handle_flags.in
> @@ -0,0 +1,8 @@
> +GPIOHANDLE_REQUEST_INPUT 0x00000001
> +GPIOHANDLE_REQUEST_OUTPUT 0x00000002
> +GPIOHANDLE_REQUEST_ACTIVE_LOW 0x00000004
> +GPIOHANDLE_REQUEST_OPEN_DRAIN 0x00000008
> +GPIOHANDLE_REQUEST_OPEN_SOURCE 0x00000010
> +GPIOHANDLE_REQUEST_BIAS_PULL_UP 0x00000020
> +GPIOHANDLE_REQUEST_BIAS_PULL_DOWN 0x00000040
> +GPIOHANDLE_REQUEST_BIAS_DISABLE 0x00000080
> diff --git a/xlat/gpio_line_flags.in b/xlat/gpio_line_flags.in
> new file mode 100644
> index 00000000..fd4b7004
> --- /dev/null
> +++ b/xlat/gpio_line_flags.in
> @@ -0,0 +1,8 @@
> +GPIOLINE_FLAG_KERNEL 0x00000001
> +GPIOLINE_FLAG_IS_OUT 0x00000002
> +GPIOLINE_FLAG_ACTIVE_LOW 0x00000004
> +GPIOLINE_FLAG_OPEN_DRAIN 0x00000008
> +GPIOLINE_FLAG_OPEN_SOURCE 0x00000010
> +GPIOLINE_FLAG_BIAS_PULL_UP 0x00000020
> +GPIOLINE_FLAG_BIAS_PULL_DOWN 0x00000040
> +GPIOLINE_FLAG_BIAS_DISABLE 0x00000080
There is a slight inconsistency in xlat files: most lines use tabs, but
some use spaces.
--
ldv
More information about the Strace-devel
mailing list