[PATCH v2 2/6] Add GPIO ioctl decoding

Dmitry V. Levin ldv at altlinux.org
Tue Jan 19 20:01:45 UTC 2021


On Mon, Jan 11, 2021 at 11:09:08PM +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>
> ---
>  Makefile.am               |   1 +
>  configure.ac              |   1 +
>  defs.h                    |   1 +
>  gpio_ioctl.c              | 259 ++++++++++++++++++++++++++++++++++++++
>  ioctl.c                   |   4 +
>  xlat/gpio_event_flags.in  |   3 +
>  xlat/gpio_handle_flags.in |   8 ++
>  xlat/gpio_line_flags.in   |   8 ++
>  8 files changed, 285 insertions(+)
>  create mode 100644 gpio_ioctl.c
>  create mode 100644 xlat/gpio_event_flags.in
>  create mode 100644 xlat/gpio_handle_flags.in
>  create mode 100644 xlat/gpio_line_flags.in
> 
> diff --git a/Makefile.am b/Makefile.am
> index c0199f65..5fbee7b5 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -139,6 +139,7 @@ libstrace_a_SOURCES =	\
>  	getpagesize.c \
>  	getpid.c	\
>  	getrandom.c	\
> +	gpio_ioctl.c	\
>  	hdio.c		\
>  	hostname.c	\
>  	inotify.c	\
> diff --git a/configure.ac b/configure.ac
> index a736e70b..5de50f04 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -415,6 +415,7 @@ AC_CHECK_HEADERS(m4_normalize([
>  	linux/dqblk_xfs.h
>  	linux/falloc.h
>  	linux/fib_rules.h
> +	linux/gpio.h
>  	linux/hiddev.h
>  	linux/ip_vs.h
>  	linux/ipc.h
> diff --git a/defs.h b/defs.h
> index 8a12f751..2cfb550a 100644
> --- a/defs.h
> +++ b/defs.h
> @@ -1216,6 +1216,7 @@ DECL_IOCTL(evdev);
>  DECL_IOCTL(fs_0x94);
>  DECL_IOCTL(fs_f);
>  DECL_IOCTL(fs_x);
> +DECL_IOCTL(gpio);
>  DECL_IOCTL(inotify);
>  DECL_IOCTL(kvm);
>  DECL_IOCTL(nbd);
> diff --git a/gpio_ioctl.c b/gpio_ioctl.c
> new file mode 100644
> index 00000000..7bd5dfc8
> --- /dev/null
> +++ b/gpio_ioctl.c
> @@ -0,0 +1,259 @@
> +/*
> + * Copyright (c) 2020 The strace developers.
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier: LGPL-2.1-or-later
> + */
> +
> +#include "defs.h"
> +#include "print_fields.h"
> +
> +#if HAVE_LINUX_GPIO_H
> +
> +#include <linux/gpio.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 {
> +	uint32_t flags;
> +	uint8_t default_values[GPIOHANDLES_MAX];
> +	uint32_t padding[4];
> +};
> +#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, uint32_t)
> +#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))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &info))
> +		return RVAL_IOCTL_DECODED;
> +
> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("{", info, line_offset);
> +		tprints("}");
> +		return 0;
> +	}
> +
> +	/* exiting */
> +	PRINT_FIELD_FLAGS("{", info, flags, gpio_line_flags, "GPIOLINE_FLAG_???");
> +	PRINT_FIELD_CSTRING(", ", info, name);
> +	PRINT_FIELD_CSTRING(", ", info, consumer);
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +print_gpioline_info_unwatch(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	tprintf(", {offset=%u}", (uint32_t)arg);

I'm not sure there is a real structure with an offset field, the kernel does
	__u32 offset;
	...
	if (copy_from_user(&offset, ip, sizeof(offset)))
		return -EFAULT;

Anyway, printing the uint32_t part of the address itself doesn't look
particularly useful, maybe printnum_int(tcp, arg, "%u") on exiting syscall
would be more appropriate here.

> +
> +	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))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &hr))
> +		return RVAL_IOCTL_DECODED;
> +
> +	if (entering(tcp)) {
> +		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);

We've added PRINT_FIELD_ARRAY_UPTO recently, please consider using it
instead, not only it would look simpler:

		PRINT_FIELD_ARRAY_UPTO(", ", hr, lineoffsets, hr.lines, tcp,
				       print_uint32_array_member);

it would also help us in achieving our big hope to implement structured
output in the future.

> +		PRINT_FIELD_FLAGS(", ", hr, flags, gpio_handle_flags,
> +			"GPIOHANDLE_REQUEST_???");
> +		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);

Likewise, consider using PRINT_FIELD_ARRAY_UPTO.

> +		PRINT_FIELD_CSTRING(", ", hr, consumer_label);
> +		tprints("}");
> +		return 0;
> +	}
> +
> +	/* exiting */
> +	PRINT_FIELD_FD("{", hr, fd, tcp);
> +	tprints("}");
> +
> +	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))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &er))
> +		return RVAL_IOCTL_DECODED;
> +
> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("{", er, lineoffset);
> +		PRINT_FIELD_FLAGS(", ", er, handleflags, gpio_handle_flags,
> +				  "GPIOHANDLE_REQUEST_???");
> +		PRINT_FIELD_FLAGS(", ", er, eventflags, gpio_event_flags,
> +				  "GPIOEVENT_REQUEST_???");
> +		PRINT_FIELD_CSTRING(", ", er, consumer_label);
> +		tprints("}");
> +		return 0;
> +	}
> +
> +	/* exiting */
> +	PRINT_FIELD_FD("{", er, fd, tcp);
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +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);

Please consider using PRINT_FIELD_ARRAY instead.

> +	tprints("}");
> +}
> +
> +static int
> +print_gpiohandle_get_values(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpiohandle_data vals;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		return 0;
> +	}

You can do this tprints(", ") on exiting instead, this would probably be
a bit more consistent.

> +	/* exiting */
> +	if (syserror(tcp)) {
> +		printaddr(arg);
> +		return RVAL_IOCTL_DECODED;
> +	}
> +	if (umove_or_printaddr(tcp, arg, &vals))
> +		return RVAL_IOCTL_DECODED;

You can safely omit the "if (syserror(tcp)) {...}" part because
umove_or_printaddr already does this.

> +	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;
> +
> +	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_config(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct gpiohandle_config hc;
> +
> +	tprints(", ");
> +	if (umove_or_printaddr(tcp, arg, &hc))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_FLAGS("{", hc, flags, gpio_handle_flags, "GPIOHANDLE_REQUEST_???");
> +	tprints(", default_values=");
> +	print_local_array(tcp, hc.default_values, print_uint8_array_member);

Please consider using PRINT_FIELD_ARRAY instead.

> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +int
> +gpio_ioctl(struct tcb *const tcp, const unsigned int code,
> +	   const kernel_ulong_t arg)
> +{
> +	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);
> +	}
> +	return RVAL_DECODED;
> +}
> +
> +#endif /* HAVE_LINUX_GPIO_H */
> diff --git a/ioctl.c b/ioctl.c
> index f3f6f0ab..944834df 100644
> --- a/ioctl.c
> +++ b/ioctl.c
> @@ -345,6 +345,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..c355b10b
> --- /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..0c75034e
> --- /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..a7d505e2
> --- /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

-- 
ldv


More information about the Strace-devel mailing list