[RFC PATCH v3 2/3] drm: implement decoding of DRM ioctls

Eugene Syromiatnikov esyr at redhat.com
Mon Jul 29 01:57:51 UTC 2019


This is just a cursory review, since I haven't looked at in-kernel
DRM ioctl implementation yet.

On Sat, Jul 27, 2019 at 11:57:16PM +0800, Zhibin Li wrote:

> (drm_version, drm_unique, drm_get_map, drm_get_client, drm_get_stats,
> drm_add_map, drm_add_bufs, drm_mark_bufs, drm_info_bufs, drm_map_bufs,
> drm_free_bufs, drm_rm_map, drm_sarea_ctx, drm_res_ctx, drm_agp_enable,
> drm_agp_info, drm_agp_alloc, drm_agp_free, drm_mode_get_connector,
> drm_scatter_gather, drm_wait_vblank, drm_mode_getplaneresources,
> drm_mode_add_fb2, drm_mode_obj_getproperties, drm_mode_obj_setproperty
> drm_agp_bind): New functions.
> (drm_ioctl_mpers): Use them.

I don't thing the list of functions is required for a new file GNU-style
changelog entry.

> (print_drm_iowr, drm_decode_number, drm_get_magic, drm_irq_busid,
> drm_set_version, drm_modeset_ctl, drm_gem_close, drm_gem_flink,
> drm_gem_open, drm_get_cap, drm_set_client_cap, drm_auth_magic,
> drm_noop, drm_control, drm_ctx, drm_rm_ctx, drm_get_ctx, drm_lock,
> drm_prime_handle_to_fd, drm_prime_fd_to_handle, drm_crtc_get_sequence,
> drm_crtc_queue_sequence, drm_mode_get_resources, drm_mode_crtc,
> drm_mode_print_modeinfo, drm_mode_cursor, drm_mode_get_encoder,
> drm_mode_gamma, drm_mode_get_property, drm_mode_set_property,
> drm_mode_get_prop_blob, drm_mode_get_fb, drm_mode_add_fb,
> drm_mode_rm_fb, drm_mode_page_flip, drm_mode_create_dumb,
> drm_mode_dirty_fb, drm_mode_map_dumb, drm_mode_destroy_dumb,
> drm_mode_getplane, drm_mode_setplane, drm_mode_cursor2,
> drm_mode_atomic, drm_mode_createpropblob, drm_mode_destroypropblob,
> drm_syncobj_create, drm_syncobj_destroy, drm_syncobj_handle_fd,
> drm_syncobj_wait, drm_syncobj_reset_or_signal, drm_mode_create_lease,
> drm_mode_list_lessees, drm_mode_get_lease, drm_mode_revoke_lease,
> drm_syncobj_timeline_wait, drm_syncobj_query_or_timeline_signal,
> drm_syncobj_transfer): New functions.
> (drm_ioctl): Use them and use drm_ioctl_mpers.

Ditto.

> --- /dev/null
> +++ b/drm.c
> @@ -0,0 +1,1531 @@
> +/*
> + * Copyright (c) 2019 Patrik Jakobsson <pjakobsson at suse.de>
The copyright of the original patch dates back to (at least) 2015[1],
and it was claimed by Intel back then.

Your copyright attribution is also missing (adding generic "The strace
developers" attribution would be sufficient as well).

[1] https://lists.freedesktop.org/archives/intel-gfx/2015-August/074252.html

> + *
> + * SPDX-License-Identifier: LGPL-2.1-or-later
> + */
> +
> +#include "defs.h"
> +#include "print_fields.h"
> +
> +#if defined(HAVE_DRM_H) || defined(HAVE_DRM_DRM_H)
> +

> +# ifdef HAVE_DRM_H
> +#  include <drm.h>
> +# else
> +#  include <drm/drm.h>
> +# endif
I wonder what happens if neither of those is found.

Note also that this file can be rather old and do not contain all the
ioctl constants, so it is likely also worth having an xlat with all
related DRM_IOC constants.

> +
> +static void
> +print_drm_iowr(const unsigned int nr, const unsigned int size,
> +	       const char *str)
> +{
> +	tprintf("DRM_IOWR(%#x, %#x) /* %s */", nr, size, str);
This likely needs xlat style support.

> +}
> +
> +int
> +drm_decode_number(struct tcb *const tcp, const unsigned int code)
> +{
> +	const unsigned int nr = _IOC_NR(code);
> +
> +	if (_IOC_DIR(code) == (_IOC_READ | _IOC_WRITE)) {
> +		switch (nr) {

> +			case 0xa7:
Unnecessary indentation.

> +				/* In Linux commit v3.12-rc7~26^2~2 a u32 padding was added */
> +				/* to struct drm_mode_get_connector so in old kernel headers */
> +				/* the size of this structure is 0x4c instead of 0x50. */
drm argument sizes are constantly changing (please take a look at Gleb's
ioctl-related commits, they can provide a rough impression; I'm not sure
to which extent it is applicable to core DRM ioctls, however), a more
robust way of decoding drm ioctl number have to be implemented.

Considering the code below, it may be the case for
DRM_IOCTL_MODE_GETPLANERESOURCES, DRM_IOCTL_MODE_OBJ_GETPROPERTIES,
and DRM_IOCTL_MODE_OBJ_SETPROPERTY as well.

> +				if (_IOC_SIZE(code) == 0x4c) {
> +					print_drm_iowr(nr, _IOC_SIZE(code),
> +						       "DRM_IOCTL_MODE_GETCONNECTOR");
> +					return IOCTL_NUMBER_STOP_LOOKUP;
> +				} else {
> +					return 0;
> +				}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +drm_get_magic(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_auth auth;
> +
> +	if (exiting(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &auth))
> +			return RVAL_IOCTL_DECODED;
> +
> +		PRINT_FIELD_U("{", auth, magic);
> +		tprints("}");
> +
> +		return RVAL_IOCTL_DECODED;
> +	}
> +
> +	return 0;


I'd rewrite exiting-only decoding as

	if (entering(tcp)) {
		tprints(", ");
		return 0;
	}

	/* exiting-only code */

As it allows remove one indentation level. It's slightly less readable,
though, but it is also common enough among strace decoders to sacrifice
a bit of readability.

The other thing is printing comma on exiting vs. on entering elsewhere,
which is somewhat inconsistent.

> +}

> +# include "xlat/drm_modeset_cmd.h"
I'd prefer to have all the includes at the beginning of the file, it
simplifies maintenance of the code in the long run.

> +# include "xlat/drm_capability.h"
Ditto.

> +
> +static int
> +drm_get_cap(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_get_cap cap;
> +
> +	if (entering(tcp)) {

> +		tprints(", ");
Okay, now I feel that this comma printing can well be re-factored out of
the specific argument printing functions; moreover, that would make
these printing functions structure printing funtions, ergo more
re-usable (in case printing of the same structures will be needed
somewhere else in the code).

> +		if (umove_or_printaddr(tcp, arg, &cap))
> +			return RVAL_IOCTL_DECODED;

> +		tprints("{capability=");
> +		printxval(drm_capability, cap.capability, "DRM_CAP_???");
Why not "PRINT_FIELD_XVAL("{", cap, capability, drm_capability, "DRM_CAP_???")"?

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &cap)) {
Adding a separate umove_no_syserror() macro might be a good idea.

> +		PRINT_FIELD_U(", ", cap, value);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}

> +# include "xlat/drm_client_capability.h"
Inline include.

> +
> +static int
> +drm_set_client_cap(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_set_client_cap cap;
> +
> +	tprints(", ");

> +	if (!umove_or_printaddr(tcp, arg, &cap)) {

I'd stick to

	if (umove_or_printaddr(tcp, arg, &cap))
		return RVAL_IOCTL_DECODED;

	/* umove success path */

Again, for the sake of reducing indentation.

> +		tprints("{capability=");
> +		printxval(drm_client_capability, cap.capability, "DRM_CLIENT_CAP_???");
Why not PRINT_FIELD_XVAL?

> +		PRINT_FIELD_U(", ", cap, value);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_auth_magic(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_auth auth;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &auth)) {
> +		PRINT_FIELD_U("{", auth, magic);
I suspect that "magic" is expected to be hexadecimal.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}

> +# include "xlat/drm_control_func.h"
Inline include.

> +
> +static int
> +drm_control(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_control control;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &control)) {

> +		tprints("{func=");
> +		printxval(drm_control_func, control.func, "DRM_???");
Why not PRINT_FIELD_XVAL?

> +		PRINT_FIELD_D(", ", control, irq);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_ctx(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_ctx ctx;
> +
> +	if (exiting(tcp)) {
Exiting-only decoding.

> +		tprints(", ");
Comma on exiting.

> +		if (umove_or_printaddr(tcp, arg, &ctx))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", ctx, handle);
Are handles sequential or random in nature? In the latter case, please
consider hexadecimal output format.

> +		tprints("}");

This code can be factored out into a separate function that is called by
drm_ctx/drm_rm_ctx (and optionally drm_get_ctx).

> +
> +		return RVAL_IOCTL_DECODED;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +drm_rm_ctx(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_ctx ctx;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");

> +		if (umove_or_printaddr(tcp, arg, &ctx))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", ctx, handle);
> +		tprints("}");
Can be factored out.

> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_ctx_flags.h"
> +
> +static int
> +drm_get_ctx(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_ctx ctx;
> +
> +	if (exiting(tcp)) {
Exiting-only decoding.

> +		tprints(", ");
Comma on exiting.

> +		if (umove_or_printaddr(tcp, arg, &ctx))
> +			return RVAL_IOCTL_DECODED;

> +		tprints("{flags=");
> +		printxval(drm_ctx_flags, ctx.flags, "_DRM_CONTEXT_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

Why handle field is not printed here? I suspect it has to be printed on
entering, hasn't it?

> +		tprints("}");
> +
> +		return RVAL_IOCTL_DECODED;
> +	}
> +
> +	return 0;
> +}

> +# include "xlat/drm_lock_flags.h"
Inline include.

> +static int
> +drm_lock(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_lock lock;
> +
> +	if (exiting(tcp)) {
Exiting-only decoding.

> +		tprints(", ");
Comma on exiting.

> +		if (umove_or_printaddr(tcp, arg, &lock))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_D("{", lock, context);

> +		tprints(", flags=");
> +		printxval(drm_lock_flags, lock.flags, "_DRM_LOCK_???");
Why printxval and not printflags?

Why not PRINT_FIELD_XVAL?

> +		tprints("}");
> +
> +		return RVAL_IOCTL_DECODED;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +drm_prime_handle_to_fd(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_prime_handle handle;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &handle))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", handle, handle);

> +		PRINT_FIELD_X(", ", handle, flags);
The flags field can have DRM_CLOEXEC and DRM_RDWR flags.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &handle)) {

> +		PRINT_FIELD_U(", ", handle, fd);
Why not PRINT_FIELD_FD?

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_prime_fd_to_handle(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_prime_handle handle;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &handle))
> +			return RVAL_IOCTL_DECODED;

> +		PRINT_FIELD_U("{", handle, fd);
Why not PRINT_FIELD_FD?

This also makes order of printing different comparing to order of fields
in the structure.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &handle)) {

> +		PRINT_FIELD_U(", ", handle, handle);
I feel that drm_handle_t likely needs a separate PRINT_FIELD_* macro.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +
> +}
> +

> +# ifdef DRM_IOCTL_CRTC_GET_SEQUENCE
Ugh, those ifdef's make strace behaviour depend on the version of drm
headers it is built against, which is quite unreliable.

> +static int
> +drm_crtc_get_sequence(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_crtc_get_sequence seq;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &seq))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", seq, crtc_id);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &seq)) {
> +		PRINT_FIELD_U(", ", seq, active);
> +		PRINT_FIELD_U(", ", seq, sequence);
> +		PRINT_FIELD_D(", ", seq, sequence_ns);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_CRTC_QUEUE_SEQUENCE
> +
> +# include "xlat/drm_crtc_sequence_flags.h"
> +
> +static int
> +drm_crtc_queue_sequence(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_crtc_queue_sequence seq;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &seq))
> +		return RVAL_IOCTL_DECODED;
> +
> +	tprints("{");
It's probably better to put this opening brace printing into respective
PRINT_FIELD_* macros.

> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("", seq, crtc_id);

> +		tprints(", flags=");
> +		printxval(drm_crtc_sequence_flags, seq.flags, "DRM_CRTC_SEQUENCE_???");
Why printxval and not printflags?

Why not PRINT_FIELD_XVAL?

> +		PRINT_FIELD_U(", ", seq, user_data);
user_data is opaque and likely has to be printed as hexadecimal.

> +		PRINT_FIELD_U(", ", seq, sequence);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	PRINT_FIELD_U("", seq, sequence);
... or at least put this into "else" block, just to improve readability a bit.

> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +static int
> +drm_mode_get_resources(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_card_res res;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &res))
> +			return RVAL_IOCTL_DECODED;

> +		PRINT_FIELD_PTR("{", res, fb_id_ptr);
> +		PRINT_FIELD_PTR(", ", res, crtc_id_ptr);
> +		PRINT_FIELD_PTR(", ", res, connector_id_ptr);
> +		PRINT_FIELD_PTR(", ", res, encoder_id_ptr);
Why PRINT_FIELD_PTR if this is a non-mpers ioctl? Should PRINT_FIELD_ADDR
or PRINT_FIELD_ADDR64 be used, for example?

Those are arrays of count_* elements (which are R/W, by the way), they likely
has to be treated as such.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &res)) {
> +		PRINT_FIELD_U(", ", res, count_fbs);
> +		PRINT_FIELD_U(", ", res, count_crtcs);
> +		PRINT_FIELD_U(", ", res, count_connectors);
> +		PRINT_FIELD_U(", ", res, count_encoders);
> +		PRINT_FIELD_U(", ", res, min_width);
> +		PRINT_FIELD_U(", ", res, max_width);
> +		PRINT_FIELD_U(", ", res, min_height);
> +		PRINT_FIELD_U(", ", res, max_height);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_mode_type.h"
> +# include "xlat/drm_mode_flags.h"
Inline include.

> +
> +static void
> +drm_mode_print_modeinfo(struct drm_mode_modeinfo *info)
> +{
Why curly braces are not printed here?

> +	PRINT_FIELD_U("", *info, clock);
> +	PRINT_FIELD_U(", ", *info, hdisplay);
> +	PRINT_FIELD_U(", ", *info, hsync_start);
> +	PRINT_FIELD_U(", ", *info, hsync_end);
> +	PRINT_FIELD_U(", ", *info, htotal);
> +	PRINT_FIELD_U(", ", *info, hskew);
> +	PRINT_FIELD_U(", ", *info, vdisplay);
> +	PRINT_FIELD_U(", ", *info, vsync_start);
> +	PRINT_FIELD_U(", ", *info, vsync_end);
> +	PRINT_FIELD_U(", ", *info, vtotal);
> +	PRINT_FIELD_U(", ", *info, vscan);
> +	PRINT_FIELD_U(", ", *info, vrefresh);

> +	tprints(", flags=");
> +	printxval(drm_mode_flags, info->flags, "DRM_MODE_FLAG_PIC_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +	tprints(", type=");
> +	printxval(drm_mode_type, info->type, "DRM_MODE_TYPE_???");
Why not PRINT_FIELD_XVAL?

> +	PRINT_FIELD_CSTRING(", ", *info, name);
> +}
> +
> +static int
> +drm_mode_crtc(struct tcb *const tcp, const kernel_ulong_t arg,
> +		  bool is_get)
> +{
> +	struct drm_mode_crtc crtc;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &crtc))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", crtc, crtc_id);
> +		if (is_get)
> +			return 0;
> +		PRINT_FIELD_PTR(", ", crtc, set_connectors_ptr);
Why not PRINT_FIELD_ADDR64?

It is treated as a pointer to u32 array containing count_connectors
elements in kernel, why not printing it like that?

> +		PRINT_FIELD_U(", ", crtc, count_connectors);
This one is R/W.

> +		PRINT_FIELD_U(", ", crtc, fb_id);
> +		PRINT_FIELD_U(", ", crtc, x);
> +		PRINT_FIELD_U(", ", crtc, y);
> +		PRINT_FIELD_U(", ", crtc, gamma_size);
> +		PRINT_FIELD_U(", ", crtc, mode_valid);
> +		tprints(", mode={");
> +
> +		drm_mode_print_modeinfo(&crtc.mode);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &crtc)) {
Why not check for "is_get" first here and avoid nested "if" at all?

> +		if (is_get) {

> +			PRINT_FIELD_PTR(", ", crtc, set_connectors_ptr);
Why not PRINT_FIELD_ADDR64?

It is treated as a pointer to u32 array containing count_connectors
elements in kernel, why not printing it like that?

> +			PRINT_FIELD_U(", ", crtc, count_connectors);
> +			PRINT_FIELD_U(", ", crtc, fb_id);
> +			PRINT_FIELD_U(", ", crtc, x);
> +			PRINT_FIELD_U(", ", crtc, y);
> +			PRINT_FIELD_U(", ", crtc, gamma_size);
> +			PRINT_FIELD_U(", ", crtc, mode_valid);
> +			tprints(", mode={");
> +
> +			drm_mode_print_modeinfo(&crtc.mode);
> +			tprints("}");
This code looks the same for entering and exiting, it probably can be factored
out in a separate function (print_drm_mode_crtc_tail or something like
that).

> +		}
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_cursor(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_cursor cursor;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &cursor))
> +			return RVAL_IOCTL_DECODED;
> +
> +		PRINT_FIELD_X("{", cursor, flags);
It can have DRM_MODE_CURSOR_BO and DRM_MODE_CURSOR_MOVE.

> +		PRINT_FIELD_U(", ", cursor, crtc_id);
I feel like crtc_id needs a separate PRINT_FIELD_* macro.

> +		PRINT_FIELD_D(", ", cursor, x);
> +		PRINT_FIELD_D(", ", cursor, y);
> +		PRINT_FIELD_U(", ", cursor, width);
> +		PRINT_FIELD_U(", ", cursor, height);
> +		PRINT_FIELD_U(", ", cursor, handle);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_gamma(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_crtc_lut lut;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &lut))
> +			return RVAL_IOCTL_DECODED;
> +
> +		/* We don't print the entire table, just the pointers */
> +		PRINT_FIELD_U("{", lut, crtc_id);
> +		PRINT_FIELD_U(", ", lut, gamma_size);

> +		PRINT_FIELD_PTR(", ", lut, red);
> +		PRINT_FIELD_PTR(", ", lut, green);
> +		PRINT_FIELD_PTR(", ", lut, blue);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64? And why not retrieve and
print these arrays?

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_mode_encoder_type.h"
Inline include.

> +
> +static int
> +drm_mode_get_encoder(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_get_encoder enc;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &enc))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_U("{", enc, encoder_id);
> +
> +	if (entering(tcp)) {
> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	tprints(", encoder_type=");
> +	printxval(drm_mode_encoder_type, enc.encoder_type, "DRM_MODE_ENCODER_???");
Why not PRINT_FIELD_XVAL?

> +	PRINT_FIELD_U(", ", enc, crtc_id);
> +	PRINT_FIELD_X(", ", enc, possible_crtcs);
> +	PRINT_FIELD_X(", ", enc, possible_clones);
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_get_property(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_get_property prop;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &prop))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", prop, prop_id);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &prop)) {

> +		PRINT_FIELD_PTR(", ", prop, values_ptr);
> +		PRINT_FIELD_PTR(", ", prop, enum_blob_ptr);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

enum_blob_ptr is an array of count_enum_blobs elements of struct
drm_mode_property_enum type, why not print it as such?

> +		PRINT_FIELD_X(", ", prop, flags);
This can have DRM_MODE_PROP_PENDING, DRM_MODE_PROP_RANGE,
DRM_MODE_PROP_IMMUTABLE, DRM_MODE_PROP_ENUM, DRM_MODE_PROP_BLOB,
DRM_MODE_PROP_BITMASK as flags, and DRM_MODE_PROP_EXTENDED_TYPE in
higher bits.

> +		PRINT_FIELD_CSTRING(", ", prop, name);

> +		PRINT_FIELD_U(", ", prop, count_values);
> +		PRINT_FIELD_U(", ", prop, count_enum_blobs);
These are R/W.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_set_property(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_connector_set_property prop;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &prop)) {
> +		PRINT_FIELD_U("{", prop, value);
> +		PRINT_FIELD_U(", ", prop, prop_id);
> +		PRINT_FIELD_U(", ", prop, connector_id);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_get_prop_blob(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_get_blob blob;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &blob))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", blob, blob_id);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &blob)) {

> +		PRINT_FIELD_U(", ", blob, length);
This one is R/W.

> +		PRINT_FIELD_U(", ", blob, data);
Data is a pointer to blob, it likely has to be printed using printstr
with appropriate flags.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_get_fb(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_fb_cmd cmd;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &cmd))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", cmd, fb_id);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &cmd)) {

> +		PRINT_FIELD_U(", ", cmd, width);
> +		PRINT_FIELD_U(", ", cmd, height);
> +		PRINT_FIELD_U(", ", cmd, pitch);
> +		PRINT_FIELD_U(", ", cmd, bpp);
> +		PRINT_FIELD_U(", ", cmd, depth);
> +		PRINT_FIELD_U(", ", cmd, handle);
This code is duplicated between drm_mode_get_fb and drm_mode_add_fb,
it can be factored out in a separate function.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_add_fb(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_fb_cmd cmd;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &cmd))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", cmd, width);
> +		PRINT_FIELD_U(", ", cmd, height);
> +		PRINT_FIELD_U(", ", cmd, pitch);
> +		PRINT_FIELD_U(", ", cmd, bpp);
> +		PRINT_FIELD_U(", ", cmd, depth);
> +		PRINT_FIELD_U(", ", cmd, handle);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove_or_printaddr(tcp, arg, &cmd)) {
> +		PRINT_FIELD_U(", ", cmd, fb_id);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_rm_fb(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	unsigned int handle;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &handle))
> +		tprintf("%u", handle);
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_mode_page_flip_flags.h"
Inline include.

> +
> +static int
> +drm_mode_page_flip(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_crtc_page_flip flip;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &flip)) {
> +		PRINT_FIELD_U("{", flip, crtc_id);
> +		PRINT_FIELD_U(", ", flip, fb_id);

> +		tprints(", flags=");
> +		printxval(drm_mode_page_flip_flags, flip.flags, "DRM_MODE_PAGE_FLIP_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +		PRINT_FIELD_X(", ", flip, user_data);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_dirty_fb(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_fb_dirty_cmd cmd;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &cmd)) {
> +		PRINT_FIELD_U("{", cmd, fb_id);

> +		PRINT_FIELD_X(", ", cmd, flags);
This one can have DRM_MODE_FB_DIRTY_ANNOTATE_COPY and
DRM_MODE_FB_DIRTY_ANNOTATE_FILL as flags.

> +		PRINT_FIELD_X(", ", cmd, color);
> +		PRINT_FIELD_U(", ", cmd, num_clips);

> +		PRINT_FIELD_PTR(", ", cmd, clips_ptr);
Why not PRINT_FIELD_ADDr/PRINT_FIELD_ADDR64?

clips_ptr is a pointer to an array of num_clips elements of struct drm_clip_rect
type, why not print it as such?

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_destroy_dumb(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_destroy_dumb dumb;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &dumb)) {
> +		PRINT_FIELD_U("{", dumb, handle);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_getplane(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_get_plane plane;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &plane))
> +		return RVAL_IOCTL_DECODED;
> +
> +	tprints("{");
> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("", plane, plane_id);

> +		PRINT_FIELD_PTR(", ", plane, format_type_ptr);
Why not PRINT_FIELD_ADDR64?

This is a pointer to array of count_format_types elements of type
uint32_t, and likely has to be treated as such.

> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	PRINT_FIELD_PTR("", plane, format_type_ptr);
Why not PRINT_FIELD_ADDR64?

This is a pointer to array of count_format_types elements of type
uint32_t, and likely has to be treated as such.

> +	PRINT_FIELD_U(", ", plane, crtc_id);
> +	PRINT_FIELD_U(", ", plane, fb_id);
> +	PRINT_FIELD_U(", ", plane, possible_crtcs);
> +	PRINT_FIELD_U(", ", plane, gamma_size);

> +	PRINT_FIELD_U(", ", plane, count_format_types);
This one is R/W.

> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_setplane(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_set_plane plane;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &plane))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", plane, plane_id);
> +		PRINT_FIELD_U(", ", plane, crtc_id);
> +		PRINT_FIELD_U(", ", plane, fb_id);

> +		PRINT_FIELD_X(", ", plane, flags);
Seems like it can have DRM_MODE_PRESENT_TOP_FIELD and
DRM_MODE_PRESENT_BOTTOM_FIELD flags.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &plane)) {
> +		PRINT_FIELD_D(", ", plane, crtc_x);
> +		PRINT_FIELD_D(", ", plane, crtc_y);

> +		PRINT_FIELD_D(", ", plane, crtc_w);
> +		PRINT_FIELD_D(", ", plane, crtc_h);
These are unsigned.

> +		PRINT_FIELD_D(", ", plane, src_x);
> +		PRINT_FIELD_D(", ", plane, src_y);
> +		PRINT_FIELD_D(", ", plane, src_h);
> +		PRINT_FIELD_D(", ", plane, src_w);
These are unsigned and are in 16.16 fixed point format.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_cursor2(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_cursor2 cursor;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &cursor))
> +			return RVAL_IOCTL_DECODED;
> +
> +		PRINT_FIELD_X("{", cursor, flags);
This one employs the same DRM_MODE_CURSOR_BO and DRM_MODE_CURSOR_MOVE
flags as the flags field in drm_mode_cursor. 

> +		PRINT_FIELD_U(", ", cursor, crtc_id);
> +		PRINT_FIELD_D(", ", cursor, x);
> +		PRINT_FIELD_D(", ", cursor, y);
> +		PRINT_FIELD_U(", ", cursor, width);
> +		PRINT_FIELD_U(", ", cursor, height);
> +		PRINT_FIELD_U(", ", cursor, handle);
> +		PRINT_FIELD_D(", ", cursor, hot_x);
> +		PRINT_FIELD_D(", ", cursor, hot_y);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_atomic(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_atomic atomic;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &atomic)) {
> +		PRINT_FIELD_X("{", atomic, flags);
> +		PRINT_FIELD_U(", ", atomic, count_objs);

> +		PRINT_FIELD_PTR(", ", atomic, objs_ptr);
> +		PRINT_FIELD_PTR(", ", atomic, count_props_ptr);
> +		PRINT_FIELD_PTR(", ", atomic, props_ptr);
> +		PRINT_FIELD_PTR(", ", atomic, prop_values_ptr);
Why not PRINT_FIELD_ADDR64?

Those are arrays and likely hast to be treated as such.

> +		PRINT_FIELD_U(", ", atomic, reserved);
It usually makes sense to print reserved fields only in case they are
non-zero.

> +		PRINT_FIELD_U(", ", atomic, user_data);
user_data is opaque and likely has to be printed as hexadecimal.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_createpropblob(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_create_blob blob;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &blob))
> +			return RVAL_IOCTL_DECODED;
> +

> +		PRINT_FIELD_PTR("{", blob, data);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

Since it is a blob, it makes sense to print it using printstr with
appropriate flags.

> +		PRINT_FIELD_U(", ", blob, length);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &blob)) {
> +		PRINT_FIELD_U(", ", blob, blob_id);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_destroypropblob(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_destroy_blob blob;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &blob)) {
> +		PRINT_FIELD_U("{", blob, blob_id);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_CREATE
> +
> +# include "xlat/drm_syncobj_flags.h"
> +
> +static int
> +drm_syncobj_create(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_create create;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &create))
> +			return RVAL_IOCTL_DECODED;
> +

> +		tprints("{flags=");
> +		printxval(drm_syncobj_flags, create.flags, "DRM_SYNCOJB_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &create)) {
> +		PRINT_FIELD_U(", ", create, handle);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_DESTROY
> +static int
> +drm_syncobj_destroy(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_destroy destroy;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &destroy)) {
> +		PRINT_FIELD_U("{", destroy, handle);

> +		PRINT_FIELD_U(", ", destroy, pad);
It likely makes sense to print this one only in case it is non-zero.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
> +static int
> +drm_syncobj_handle_fd(struct tcb *const tcp, const kernel_ulong_t arg,
> +			 bool is_handle_to_fd)
> +{
> +	struct drm_syncobj_handle handle;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &handle))
> +			return RVAL_IOCTL_DECODED;
> +
> +		if (is_handle_to_fd)
> +			PRINT_FIELD_U("{", handle, handle);
> +		else

> +			PRINT_FIELD_D("{", handle, fd);
Why not PRINT_FIELD_FD?

> +		PRINT_FIELD_X(", ", handle, flags);
This one can have DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE or
DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE flag.

> +		PRINT_FIELD_U(", ", handle, pad);
It likely makes sense to print this one only in case it is non-zero.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &handle)) {
> +		if (is_handle_to_fd)

> +			PRINT_FIELD_D(", ", handle, fd);
Why not PRINT_FIELD_FD?

> +		else
> +			PRINT_FIELD_U(", ", handle, handle);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_WAIT
> +static int
> +drm_syncobj_wait(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_wait wait;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &wait))
> +			return RVAL_IOCTL_DECODED;

> +		PRINT_FIELD_PTR("{", wait, handles);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

This is an array of count_handles elements of uint32_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_D(", ", wait, timeout_nsec);
If this timeout is absolute, it might make sense to print a time stamp in
ISO 8601 format as well.

> +		PRINT_FIELD_U(", ", wait, count_handles);

> +		PRINT_FIELD_X(", ", wait, flags);
This one can have DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL,
ant DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT flags.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &wait)) {
> +		PRINT_FIELD_U(", ", wait, first_signaled);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_RESET
> +static int
> +drm_syncobj_reset_or_signal(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_array array;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &array)) {

> +		PRINT_FIELD_PTR("{", array, handles);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

This is an array of count_handles elements of uint32_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_U(", ", array, count_handles);

> +		PRINT_FIELD_U(", ", array, pad);
It likely makes sense to print this one only in case it is non-zero.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_MODE_CREATE_LEASE
> +static int
> +drm_mode_create_lease(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_create_lease lease;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &lease))
> +			return RVAL_IOCTL_DECODED;
> +

> +		PRINT_FIELD_PTR("{", lease, object_ids);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

This is an array of object_count elements of uint32_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_U(", ", lease, object_count);

> +		PRINT_FIELD_X(", ", lease, flags);
It can have O_CLOEXEC and O_NONBLOCK, at least.

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &lease)) {
> +		PRINT_FIELD_U(", ", lease, lessee_id);

> +		PRINT_FIELD_U(", ", lease, fd);
Why not PRINT_FIELD_FD?

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_MODE_LIST_LESSEES
> +static int
> +drm_mode_list_lessees(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_list_lessees lessees;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &lessees))
> +		return RVAL_IOCTL_DECODED;
> +
> +	tprints("{");
> +	if (entering(tcp)) {

> +		PRINT_FIELD_U("", lessees, pad);
It likely makes sense to print this one only in case it is non-zero.

> +		PRINT_FIELD_PTR(", ",lessees, lessees_ptr);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

This is an array of count_lessees elements of uint32_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_U(", ", lessees, count_lessees);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	PRINT_FIELD_U("", lessees, count_lessees);
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_MODE_GET_LEASE
> +static int
> +drm_mode_get_lease(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_get_lease lease;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &lease))
> +		return RVAL_IOCTL_DECODED;
> +
> +	tprints("{");
> +	if (entering(tcp)) {

> +		PRINT_FIELD_U("", lease, pad);
It likely makes sense to print this one only in case it is non-zero.

> +		PRINT_FIELD_PTR(", ", lease, objects_ptr);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

This is an array of count_object elements of uint32_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_U(", ", lease, count_objects);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	PRINT_FIELD_U("", lease, count_objects);
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_MODE_REVOKE_LEASE
> +static int
> +drm_mode_revoke_lease(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_mode_revoke_lease lease;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &lease)) {
> +		PRINT_FIELD_U("{", lease, lessee_id);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT
> +
> +# include "xlat/drm_syncobj_wait_flags.h"
> +
> +static int
> +drm_syncobj_timeline_wait(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_timeline_wait wait;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &wait))
> +			return RVAL_IOCTL_DECODED;

> +		PRINT_FIELD_PTR("{", wait, handles);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

This is an array of count_handles elements of uint32_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_D(", ", wait, timeout_nsec);
If this timeout is absolute, it might make sense to print a time stamp in
ISO 8601 format as well.

> +		PRINT_FIELD_U(", ", wait, count_handles);

> +		tprints(", flags=");
> +		printxval(drm_syncobj_wait_flags, wait.flags, "DRM_SYNCOBJ_WAIT_FLAGS_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &wait)) {
> +		PRINT_FIELD_U(", ", wait, first_signaled);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_QUERY
> +static int
> +drm_syncobj_query_or_timeline_signal(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_timeline_array array;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &array)) {

> +		PRINT_FIELD_PTR("{", array, handles);
> +		PRINT_FIELD_PTR(", ", array, points);
Why not PRINT_FIELD_ADDR/PRINT_FIELD_ADDR64?

"handles" is an array of count_handles elements of uint32_t type, and likely
has to be treated as such.

"points" is an array of count_handles elements of uint64_t type, and likely
has to be treated as such.

> +		PRINT_FIELD_U(", ", array, count_handles);

> +		PRINT_FIELD_U(", ", array, pad);
It likely makes sense to print this one only in case it is non-zero.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +# ifdef DRM_IOCTL_SYNCOBJ_TRANSFER
> +static int
> +drm_syncobj_transfer(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct drm_syncobj_transfer transfer;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &transfer)) {
> +		PRINT_FIELD_U("{", transfer, src_handle);
> +		PRINT_FIELD_U(", ", transfer, dst_handle);
> +		PRINT_FIELD_U(", ", transfer, src_point);
> +		PRINT_FIELD_U(", ", transfer, dst_point);

> +		tprints(", flags=");
> +		printxval(drm_syncobj_wait_flags, transfer.flags, "DRM_SYNCOBJ_WAIT_FLAGS_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +		PRINT_FIELD_U(", ", transfer, pad);
It likely makes sense to print this one only in case it is non-zero.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +# endif
> +
> +int
> +drm_ioctl(struct tcb *const tcp, const unsigned int code,
> +	  const kernel_ulong_t arg)
> +{

> +	switch (code) {
I'd rather perform dispatch against _IOC_NR(code) if there's nothing
preventing that.

> +	case DRM_IOCTL_GET_MAGIC: /* R */
> +		return drm_get_magic(tcp, arg);
> +	case DRM_IOCTL_IRQ_BUSID: /* RW */
> +		return drm_irq_busid(tcp, arg);
> +	case DRM_IOCTL_SET_VERSION: /* RW */
> +		return drm_set_version(tcp, arg);
> +	case DRM_IOCTL_MODESET_CTL: /* W */
> +		return drm_modeset_ctl(tcp, arg);
> +	case DRM_IOCTL_GEM_CLOSE: /* W */
> +		return drm_gem_close(tcp, arg);
> +	case DRM_IOCTL_GEM_FLINK: /* RW */
> +		return drm_gem_flink(tcp, arg);
> +	case DRM_IOCTL_GEM_OPEN: /* RW */
> +		return drm_gem_open(tcp, arg);
> +	case DRM_IOCTL_GET_CAP: /* RW */
> +		return drm_get_cap(tcp, arg);
> +	case DRM_IOCTL_SET_CLIENT_CAP: /* W */
> +		return drm_set_client_cap(tcp, arg);
> +	case DRM_IOCTL_AUTH_MAGIC: /* W */
> +		return drm_auth_magic(tcp, arg);
> +	case DRM_IOCTL_BLOCK: /* RW */
> +	case DRM_IOCTL_UNBLOCK: /* RW */
> +	case DRM_IOCTL_MOD_CTX: /* W */
> +	case DRM_IOCTL_ADD_DRAW: /* RW */
> +	case DRM_IOCTL_RM_DRAW: /* RW */
> +	case DRM_IOCTL_FINISH: /* W */
> +	case DRM_IOCTL_MODE_ATTACHMODE: /* RW */
> +	case DRM_IOCTL_MODE_DETACHMODE: /* RW */
> +		return drm_noop(tcp, arg);
> +	case DRM_IOCTL_CONTROL: /* W */
> +		return drm_control(tcp, arg);
> +	case DRM_IOCTL_ADD_CTX: /* RW */
> +	case DRM_IOCTL_SWITCH_CTX: /* W */
> +	case DRM_IOCTL_NEW_CTX: /* W */
> +		return drm_ctx(tcp, arg);
> +	case DRM_IOCTL_RM_CTX: /* RW */
> +		return drm_rm_ctx(tcp, arg);
> +	case DRM_IOCTL_GET_CTX: /* RW */
> +		return drm_get_ctx(tcp, arg);
> +	case DRM_IOCTL_LOCK: /* W */
> +	case DRM_IOCTL_UNLOCK: /* W */
> +		return drm_lock(tcp, arg);
> +	case DRM_IOCTL_PRIME_HANDLE_TO_FD: /* RW */
> +		return drm_prime_handle_to_fd(tcp, arg);
> +	case DRM_IOCTL_PRIME_FD_TO_HANDLE: /* RW */
> +		return drm_prime_fd_to_handle(tcp, arg);
> +# ifdef DRM_IOCTL_CRTC_GET_SEQUENCE
> +	case DRM_IOCTL_CRTC_GET_SEQUENCE: /* RW */
> +		return drm_crtc_get_sequence(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_CRTC_QUEUE_SEQUENCE
> +	case DRM_IOCTL_CRTC_QUEUE_SEQUENCE: /* RW */
> +		return drm_crtc_queue_sequence(tcp, arg);
> +# endif
> +	case DRM_IOCTL_MODE_GETRESOURCES: /* RW */
> +		return drm_mode_get_resources(tcp, arg);
> +	case DRM_IOCTL_MODE_GETCRTC: /* RW */
> +	case DRM_IOCTL_MODE_SETCRTC: /* RW */
> +		return drm_mode_crtc(tcp, arg, code == DRM_IOCTL_MODE_GETCRTC);
> +	case DRM_IOCTL_MODE_CURSOR: /* RW */
> +		return drm_mode_cursor(tcp, arg);
> +	case DRM_IOCTL_MODE_GETGAMMA: /* RW */
> +	case DRM_IOCTL_MODE_SETGAMMA: /* RW */
> +		return drm_mode_gamma(tcp, arg);
> +	case DRM_IOCTL_MODE_GETENCODER: /* RW */
> +		return drm_mode_get_encoder(tcp, arg);
> +	case DRM_IOCTL_MODE_GETPROPERTY: /* RW */
> +		return drm_mode_get_property(tcp, arg);
> +	case DRM_IOCTL_MODE_SETPROPERTY: /* RW */
> +		return drm_mode_set_property(tcp, arg);
> +	case DRM_IOCTL_MODE_GETPROPBLOB: /* RW */
> +		return drm_mode_get_prop_blob(tcp, arg);
> +	case DRM_IOCTL_MODE_GETFB: /* RW */
> +		return drm_mode_get_fb(tcp, arg);
> +	case DRM_IOCTL_MODE_ADDFB: /* RW */
> +		return drm_mode_add_fb(tcp, arg);
> +	case DRM_IOCTL_MODE_RMFB: /* RW */
> +		return drm_mode_rm_fb(tcp, arg);
> +	case DRM_IOCTL_MODE_PAGE_FLIP: /* RW */
> +		return drm_mode_page_flip(tcp, arg);
> +	case DRM_IOCTL_MODE_DIRTYFB: /* RW */
> +		return drm_mode_dirty_fb(tcp, arg);
> +	case DRM_IOCTL_MODE_CREATE_DUMB: /* RW */
> +		return drm_mode_create_dumb(tcp, arg);
> +	case DRM_IOCTL_MODE_MAP_DUMB: /* RW */
> +		return drm_mode_map_dumb(tcp, arg);
> +	case DRM_IOCTL_MODE_DESTROY_DUMB: /* RW */
> +		return drm_mode_destroy_dumb(tcp, arg);
> +	case DRM_IOCTL_MODE_GETPLANE: /* RW */
> +		return drm_mode_getplane(tcp, arg);
> +	case DRM_IOCTL_MODE_SETPLANE: /* RW */
> +		return drm_mode_setplane(tcp, arg);
> +	case DRM_IOCTL_MODE_CURSOR2: /* RW */
> +		return drm_mode_cursor2(tcp, arg);
> +	case DRM_IOCTL_MODE_ATOMIC: /* RW */
> +		return drm_mode_atomic(tcp, arg);//
> +	case DRM_IOCTL_MODE_CREATEPROPBLOB: /* RW */
> +		return drm_mode_createpropblob(tcp, arg);
> +	case DRM_IOCTL_MODE_DESTROYPROPBLOB: /* RW */
> +		return drm_mode_destroypropblob(tcp, arg);
> +# ifdef DRM_IOCTL_SYNCOBJ_CREATE
> +	case DRM_IOCTL_SYNCOBJ_CREATE: /* RW */
> +		return drm_syncobj_create(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_DESTROY
> +	case DRM_IOCTL_SYNCOBJ_DESTROY: /* RW */
> +		return drm_syncobj_destroy(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD
> +	case DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: /* RW */
> +	case DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: /* RW */
> +		return drm_syncobj_handle_fd(tcp, arg, code == DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_WAIT
> +	case DRM_IOCTL_SYNCOBJ_WAIT: /* RW */
> +		return drm_syncobj_wait(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_RESET
> +	case DRM_IOCTL_SYNCOBJ_RESET: /* RW */
> +	case DRM_IOCTL_SYNCOBJ_SIGNAL: /* RW */
> +		return drm_syncobj_reset_or_signal(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_MODE_CREATE_LEASE
> +	case DRM_IOCTL_MODE_CREATE_LEASE: /* RW */
> +		return drm_mode_create_lease(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_MODE_LIST_LESSEES
> +	case DRM_IOCTL_MODE_LIST_LESSEES: /* RW */
> +		return drm_mode_list_lessees(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_MODE_GET_LEASE
> +	case DRM_IOCTL_MODE_GET_LEASE: /* RW */
> +		return drm_mode_get_lease(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_MODE_REVOKE_LEASE
> +	case DRM_IOCTL_MODE_REVOKE_LEASE: /* RW */
> +		return drm_mode_revoke_lease(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT
> +	case DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT: /* RW */
> +		return drm_syncobj_timeline_wait(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_QUERY
> +	case DRM_IOCTL_SYNCOBJ_QUERY: /* RW */
> +	case DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL: /* RW */
> +		return drm_syncobj_query_or_timeline_signal(tcp, arg);
> +# endif
> +# ifdef DRM_IOCTL_SYNCOBJ_TRANSFER
> +	case DRM_IOCTL_SYNCOBJ_TRANSFER: /* RW */
> +		return drm_syncobj_transfer(tcp, arg);
> +# endif
> +	default:
> +		return drm_ioctl_mpers(tcp, code, arg);
> +	}
> +}
> +
> +#endif /* HAVE_DRM_H || HAVE_DRM_DRM_H */
> diff --git a/drm_mpers.c b/drm_mpers.c
> new file mode 100644
> index 00000000..1b685a52
> --- /dev/null
> +++ b/drm_mpers.c
> @@ -0,0 +1,815 @@
> +#include "defs.h"
> +
> +#if defined(HAVE_DRM_H) || defined(HAVE_DRM_DRM_H)
> +
> +#ifdef HAVE_DRM_H
> +# include <drm.h>
> +#else
> +# include <drm/drm.h>
> +#endif
> +
> +#include DEF_MPERS_TYPE(struct_drm_version)
> +#include DEF_MPERS_TYPE(struct_drm_unique)
> +#include DEF_MPERS_TYPE(struct_drm_map)
> +#include DEF_MPERS_TYPE(struct_drm_client)
> +#include DEF_MPERS_TYPE(struct_drm_stats)
> +#include DEF_MPERS_TYPE(struct_drm_buf_desc)
> +#include DEF_MPERS_TYPE(struct_drm_buf_info)
> +#include DEF_MPERS_TYPE(struct_drm_buf_map)
> +#include DEF_MPERS_TYPE(struct_drm_buf_free)
> +#include DEF_MPERS_TYPE(struct_drm_ctx_priv_map)
> +#include DEF_MPERS_TYPE(struct_drm_ctx_res)
> +#include DEF_MPERS_TYPE(struct_drm_agp_mode)
> +#include DEF_MPERS_TYPE(struct_drm_agp_info)
> +#include DEF_MPERS_TYPE(struct_drm_agp_buffer)
> +#include DEF_MPERS_TYPE(struct_drm_agp_binding)
> +#include DEF_MPERS_TYPE(struct_drm_scatter_gather)
> +#include DEF_MPERS_TYPE(union_drm_wait_vblank)
> +#include DEF_MPERS_TYPE(struct_drm_mode_get_connector)
> +#include DEF_MPERS_TYPE(struct_drm_mode_fb_cmd2)
> +#include DEF_MPERS_TYPE(struct_drm_mode_get_plane_res)
> +#include DEF_MPERS_TYPE(struct_drm_mode_obj_get_properties)
> +#include DEF_MPERS_TYPE(struct_drm_mode_obj_set_property)
> +
> +typedef struct drm_version struct_drm_version;
> +typedef struct drm_unique struct_drm_unique;
> +typedef struct drm_map struct_drm_map;
> +typedef struct drm_client struct_drm_client;
> +typedef struct drm_stats struct_drm_stats;
> +typedef struct drm_buf_desc struct_drm_buf_desc;
> +typedef struct drm_buf_info struct_drm_buf_info;
> +typedef struct drm_buf_map struct_drm_buf_map;
> +typedef struct drm_buf_free struct_drm_buf_free;
> +typedef struct drm_ctx_priv_map struct_drm_ctx_priv_map;
> +typedef struct drm_ctx_res struct_drm_ctx_res;
> +typedef struct drm_agp_mode struct_drm_agp_mode;
> +typedef struct drm_agp_info struct_drm_agp_info;
> +typedef struct drm_agp_buffer struct_drm_agp_buffer;
> +typedef struct drm_agp_binding struct_drm_agp_binding;
> +typedef struct drm_scatter_gather struct_drm_scatter_gather;
> +typedef union drm_wait_vblank union_drm_wait_vblank;
> +typedef struct drm_mode_get_connector struct_drm_mode_get_connector;
> +typedef struct drm_mode_fb_cmd2 struct_drm_mode_fb_cmd2;
> +typedef struct drm_mode_get_plane_res struct_drm_mode_get_plane_res;
> +typedef struct drm_mode_obj_get_properties struct_drm_mode_obj_get_properties;
> +typedef struct drm_mode_obj_set_property struct_drm_mode_obj_set_property;
> +
> +#endif /* HAVE_DRM_H || HAVE_DRM_DRM_H */
> +
> +#include MPERS_DEFS
> +
> +#if defined(HAVE_DRM_H) || defined(HAVE_DRM_DRM_H)
> +
> +# include "print_fields.h"
> +
> +static int
> +drm_version(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_version ver;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +	if (umove_or_printaddr(tcp, arg, &ver))
> +		return RVAL_IOCTL_DECODED;
> +	if (entering(tcp)) {
> +		PRINT_FIELD_U("{", ver, name_len);
> +		PRINT_FIELD_U(", ", ver, date_len);
> +		PRINT_FIELD_U(", ", ver, desc_len);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	PRINT_FIELD_D("{", ver, version_major);
> +	PRINT_FIELD_D(", ", ver, version_minor);
> +	PRINT_FIELD_D(", ", ver, version_patchlevel);
> +	PRINT_FIELD_U(", ", ver, name_len);

> +	tprints(", name=");
> +	printstrn(tcp, ptr_to_kulong(ver.name), ver.name_len);
Why not PRINT_FIELD_STRN?

> +	PRINT_FIELD_U(", ", ver, date_len);

> +	tprints(", date=");
> +	printstrn(tcp, ptr_to_kulong(ver.date), ver.date_len);
Why not PRINT_FIELD_STRN?

> +	PRINT_FIELD_U(", ", ver, desc_len);

> +	tprints(", desc=");
> +	printstrn(tcp, ptr_to_kulong(ver.desc), ver.desc_len);
Why not PRINT_FIELD_STRN?

> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_unique(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_unique unique;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +	if (umove_or_printaddr(tcp, arg, &unique))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_U("{", unique, unique_len);
> +	if (entering(tcp)) {
> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	tprints(", unique=");
> +	printstrn(tcp, ptr_to_kulong(unique.unique), unique.unique_len);
Why not PRINT_FIELD_STRN?

I suspect that a cast to uintptr_t is sufficient here.

> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +

> +# include "xlat/drm_map_type.h"
> +# include "xlat/drm_map_flags.h"
Inline include.

> +
> +static int
> +drm_get_map(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_map map;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &map))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_X("{", map, offset);
> +	if (entering(tcp)) {
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	PRINT_FIELD_U(", ", map, size);

> +	tprints(", type=");
> +	printxval(drm_map_type, map.type, "_DRM_???");
Why not PRINT_FIELD_XVAL?

> +	tprints(", flags=");
> +	printxval(drm_map_flags, map.flags, "_DRM_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +	PRINT_FIELD_PTR(", ", map, handle);
> +	PRINT_FIELD_D(", ", map, mtrr);
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_get_client(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_client client;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &client))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_D("{", client, idx);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &client)) {
> +		PRINT_FIELD_D(", ", client, auth);
> +		PRINT_FIELD_U(", ", client, pid);

> +		PRINT_FIELD_U(", ", client, uid);
Why not PRINT_FIELD_UID?

> +		PRINT_FIELD_U(", ", client, magic);
> +		PRINT_FIELD_U(", ", client, iocs);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_stat_type.h"
> +
> +static int
> +drm_get_stats(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_stats stats;
> +
> +	if (exiting(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &stats))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", stats, count);
> +		tprints(", data=[");
> +

> +		for (unsigned int i = 0; i < 15; i++) {
Why is "15" hard-coded?

Also, it seems that it should go up to "count".

> +			tprintf("%s%lu", i > 0 ? ", {value=" : "{value=", (unsigned long) stats.data[i].value);
Overly long line.

> +			tprints(", type=");

> +			printxval(drm_stat_type, stats.data[i].type, "_DRM_STAT_???");
Overly long line.

Why not PRINT_FIELD_XVAL?

> +			tprints("}");
> +		}

It could be worth considering incorporating something like
print_local_array from "Add support for printing local arrays to
print_array" commit from esyr/evdev_decode_bitset branch instead
of open-coding it.

> +		tprints("]}");
> +
> +		return RVAL_IOCTL_DECODED;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +drm_add_map(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_map map;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &map)) {
> +		PRINT_FIELD_X("{", map, offset);
> +		PRINT_FIELD_U(", ", map, size);

> +		tprints(", type=");
> +		printxval(drm_map_type, map.type, "_DRM_???");
Why not PRINT_FIELD_XVAL?

> +		tprints(", flags");
> +		printxval(drm_map_flags, map.flags, "_DRM_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_buf_desc_flags.h"
> +
> +static int
> +drm_add_bufs(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_buf_desc desc;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +	if (umove_or_printaddr(tcp, arg, &desc))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_D("{", desc, count);
> +	PRINT_FIELD_D(", ", desc, size);
> +	if (entering(tcp)) {

> +		tprints(", flags=");
> +		printxval(drm_buf_desc_flags, desc.flags, "_DRM_???");
Why printxval and not printflags?

Why not PRINT_FIELD_FLAGS?

> +		PRINT_FIELD_X(", ", desc, agp_start);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mark_bufs(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_buf_desc desc;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &desc)) {
> +		PRINT_FIELD_D("{", desc, size);
> +		PRINT_FIELD_D(", ", desc, low_mark);
> +		PRINT_FIELD_D(", ", desc, high_mark);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_info_bufs(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_buf_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;
> +
> +	tprints("{");
> +	if (entering(tcp)) {

> +		PRINT_FIELD_PTR("", info, list);
It is an array of count elements of struct drm_buf_desc type, and
likely has to be treated as such.

> +		PRINT_FIELD_D(", ", info, count);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	PRINT_FIELD_D("", info, count);
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_map_bufs(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_buf_map map;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &map))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_D("{", map, count);
> +		PRINT_FIELD_PTR(", ", map, virtual);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &map)) {

> +		PRINT_FIELD_PTR(", ", map, list);
It is an array of count elements of struct drm_buf_pub type, and
likely has to be treated as such.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_free_bufs(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_buf_free free;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &free)) {
> +		PRINT_FIELD_D("{", free, count);

> +		PRINT_FIELD_PTR(", ", free, list);
It's an array of count elements of int type, and likely has to be
treated as such.

> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_rm_map(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_map map;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &map)) {
> +		PRINT_FIELD_PTR("{", map, handle);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_sarea_ctx(struct tcb *const tcp, const kernel_ulong_t arg,
> +	      const bool is_get)
> +{
> +	struct_drm_ctx_priv_map map;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &map))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", map, ctx_id);
> +		if (!is_get)
> +			PRINT_FIELD_PTR(", ", map, handle);
> +		return 0;
> +	}
> +

> +	if (!syserror(tcp) && !umove(tcp, arg, &map)) {
> +		if (is_get)
"if" nesting can be avoided here.

> +			PRINT_FIELD_PTR(", ", map, handle);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_res_ctx(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_ctx_res ctx;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +	if (umove_or_printaddr(tcp, arg, &ctx))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_D("{", ctx, count);
> +
> +	if (entering(tcp)) {
> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	PRINT_FIELD_PTR(", ", ctx, contexts);
It's an array of count elements of struct drm_ctx type, and likely
has to be treated as such.

> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +
> +static int
> +drm_agp_enable(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_agp_mode mode;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &mode)) {
> +		PRINT_FIELD_U("{", mode, mode);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_agp_info(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_agp_info info;
> +
> +	if (exiting(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &info))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_D("{", info, agp_version_major);
> +		PRINT_FIELD_D(", ", info, agp_version_minor);
> +		PRINT_FIELD_U(", ", info, mode);

> +		PRINT_FIELD_U(", ", info, aperture_base);
This is likely better to be printed as hexadecimal.

> +		PRINT_FIELD_U(", ", info, aperture_size);
> +		PRINT_FIELD_U(", ", info, memory_allowed);
> +		PRINT_FIELD_U(", ", info, memory_used);

> +		PRINT_FIELD_U(", ", info, id_vendor);
> +		PRINT_FIELD_U(", ", info, id_device);
These are likely better to be printed as hexadecimal.

> +		tprints("}");
> +
> +		return RVAL_IOCTL_DECODED;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +drm_agp_alloc(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_agp_buffer buffer;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &buffer))
> +			return RVAL_IOCTL_DECODED;
> +		PRINT_FIELD_U("{", buffer, size);
> +		PRINT_FIELD_U(", ", buffer, type);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &buffer)) {
> +		PRINT_FIELD_U(", ", buffer, handle);
> +		PRINT_FIELD_U(", ", buffer, physical);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_agp_free(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_agp_buffer buffer;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &buffer)) {
> +		PRINT_FIELD_U("{", buffer, handle);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_agp_bind(struct tcb *const tcp, const kernel_ulong_t arg, bool is_bind)
> +{
> +	struct_drm_agp_binding binding;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &binding)) {
> +		PRINT_FIELD_U("{", binding, handle);
> +		if (is_bind)
> +			PRINT_FIELD_X(", ", binding, offset);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_scatter_gather(struct tcb *const tcp, const kernel_ulong_t arg,
> +		   bool is_alloc)
> +{
> +	struct_drm_scatter_gather sg;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &sg))
> +			return RVAL_IOCTL_DECODED;
> +		if (is_alloc)
> +			PRINT_FIELD_U("{", sg, size);
> +		else
> +			PRINT_FIELD_U("{", sg, handle);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &sg)) {
> +		if (is_alloc)
> +			PRINT_FIELD_U(", ", sg, handle);
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +# include "xlat/drm_vblank_seq_type.h"
> +
> +static int
> +drm_wait_vblank(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	union_drm_wait_vblank vblank;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &vblank))
> +			return RVAL_IOCTL_DECODED;
> +
> +		tprints("{request={type=");
> +		printxval(drm_vblank_seq_type, vblank.request.type, "_DRM_VBLANK_???");
Overly long line.

Seems like the value consists of three parts (_DRM_VBLANK_TYPES_MASK,
_DRM_VBLANK_HIGH_CRTC_MASK, and _DRM_VBLANK_FLAGS_MASK; the latter omits
_DRM_VBLANK_FLIP for unknown reason), and only bits covered by
_DRM_VBLANK_TYPES_MASK and  _DRM_VBLANK_HIGH_CRTC_MASK has to be decoded as xval,
the rest is flags.

> +		PRINT_FIELD_U(", ", vblank.request, sequence);
> +		PRINT_FIELD_U(", ", vblank.request, signal);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &vblank)) {

> +		PRINT_FIELD_U(", reply={", vblank.reply, type);
This field has the same "enum drm_vblank_seq_type" type.

> +		PRINT_FIELD_U(", ", vblank.reply, sequence);

> +		PRINT_FIELD_U(", ", vblank.reply, tval_sec);
> +		PRINT_FIELD_U(", ", vblank.reply, tval_usec);
They both are signed.

> +		tprints("}");
> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_get_connector(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_mode_get_connector con;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &con))
> +		return RVAL_IOCTL_DECODED;
> +
> +	PRINT_FIELD_U("{", con, connector_id);
> +	PRINT_FIELD_U(", ", con, count_encoders);
> +
> +	if (entering(tcp)) {
> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	PRINT_FIELD_PTR(", ", con, encoders_ptr);
It's an array of count_encoders elements of uint32_t type, and likely
has to be treated as such.

> +	PRINT_FIELD_PTR(", ", con, modes_ptr);
It's an array of count_modes elements of struct drm_mode_modeinfo type,
and likely has to be treated as such.

> +	PRINT_FIELD_PTR(", ", con, props_ptr);
It's an array of count_props elements of struct uint32_t type, and likely
has to be treated as such.

> +	PRINT_FIELD_PTR(", ", con, prop_values_ptr);
It's an array of count_props elements of struct uint64_t type, and likely
has to be treated as such.

> +	PRINT_FIELD_U(", ", con, count_modes);
> +	PRINT_FIELD_U(", ", con, count_props);
These seems to be R/W.

> +	PRINT_FIELD_U(", ", con, encoder_id);
> +	PRINT_FIELD_U(", ", con, connector_type);
> +	PRINT_FIELD_U(", ", con, connector_type_id);
> +	PRINT_FIELD_U(", ", con, connection);
> +	PRINT_FIELD_U(", ", con, mm_width);
> +	PRINT_FIELD_U(", ", con, mm_height);
> +	PRINT_FIELD_U(", ", con, subpixel);

There's also "pad" field that may be worth printing in case it is
non-zero.

> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_add_fb2(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_mode_fb_cmd2 cmd;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &cmd))
> +			return RVAL_IOCTL_DECODED;

> +		tprintf("{width=%u, height=%u, pixel_format=%#x, flags=%u, "
It may be worth considering rewriting this using PRINT_FIELDS_*.

"flags" field can contain DRM_MODE_FB_INTERLACED and DRM_MODE_FB_MODIFIERS flags.

> +			"handles=[%u, %u, %u, %u], "
> +			"pitches=[%u, %u, %u, %u], "
> +			"offsets=[%u, %u, %u, %u]",
> +			cmd.width, cmd.height, cmd.pixel_format, cmd.flags,

> +			cmd.handles[0], cmd.handles[1], cmd.handles[2],
> +			cmd.handles[3], cmd.pitches[0], cmd.pitches[1],
> +			cmd.pitches[2], cmd.pitches[3], cmd.offsets[0],
> +			cmd.offsets[1], cmd.offsets[2], cmd.offsets[3]);
This part is quite difficult to read.

> +#ifdef HAVE_STRUCT_DRM_MODE_FB_CMD2_MODIFIER
> +		tprintf(", modifiers=[%" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu64 "]",
> +			(uint64_t) cmd.modifier[0], (uint64_t) cmd.modifier[1],
> +			(uint64_t) cmd.modifier[2], (uint64_t) cmd.modifier[3]);
> +#endif
Again, wrapping part of decoding in ifdef's  seems fragile.

> +
> +			return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &cmd))
> +		PRINT_FIELD_U(", ", cmd, fb_id);
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_getplaneresources(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_mode_get_plane_res res;
> +
> +	if (entering(tcp))
> +		tprints(", ");
> +	else if (syserror(tcp))
> +		return RVAL_IOCTL_DECODED;
> +	else
> +		tprints(" => ");
> +
> +	if (umove_or_printaddr(tcp, arg, &res))
> +		return RVAL_IOCTL_DECODED;
> +
> +	tprints("{");
> +	if (entering(tcp)) {

> +		PRINT_FIELD_PTR("", res, plane_id_ptr);
Why not PRINT_FIELD_ADDR64?

"plane_id_ptr" is an array of count_planes elements of uint32_t type,
and likely has to be treated as such.

> +		PRINT_FIELD_U(", ", res, count_planes);
> +		tprints("}");
> +
> +		return 0;
> +	}
> +

> +	PRINT_FIELD_U("", res, count_planes);
This one seems to be R/W.

> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_obj_getproperties(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_mode_obj_get_properties prop;
> +
> +	if (entering(tcp)) {
> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &prop))
> +			return RVAL_IOCTL_DECODED;
> +
> +		PRINT_FIELD_PTR("{", prop, props_ptr);
Why not PRINT_FIELD_ADDR64?

"props_ptr" is an array of count_props elements of uint32_t type,
and likely has to be treated as such.

> +		PRINT_FIELD_PTR(", ", prop, prop_values_ptr);
Why not PRINT_FIELD_ADDR64?

"prop_values_ptr" is an array of count_props elements of uint64_t type,
and likely has to be treated as such.

> +		PRINT_FIELD_U(", ", prop, obj_id);
> +		PRINT_FIELD_U(", ", prop, obj_type);
> +
> +		return 0;
> +	}
> +
> +	if (!syserror(tcp) && !umove(tcp, arg, &prop)) {

> +		PRINT_FIELD_U(", ", prop, count_props);
This seems to be R/W.

> +	}
> +
> +	tprints("}");
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +static int
> +drm_mode_obj_setproperty(struct tcb *const tcp, const kernel_ulong_t arg)
> +{
> +	struct_drm_mode_obj_set_property prop;
> +
> +	tprints(", ");
> +	if (!umove_or_printaddr(tcp, arg, &prop)) {
> +		PRINT_FIELD_U("{", prop, value);
> +		PRINT_FIELD_U(", ", prop, prop_id);
> +		PRINT_FIELD_U(", ", prop, obj_id);
> +		PRINT_FIELD_U(", ", prop, obj_type);
> +		tprints("}");
> +	}
> +
> +	return RVAL_IOCTL_DECODED;
> +}
> +
> +MPERS_PRINTER_DECL(int, drm_ioctl_mpers, struct tcb *const tcp,
> +		   const unsigned int code, const kernel_ulong_t arg)
> +{
> +	switch (code) {
I'd rather perform dispatch against _IOC_NR(code) if there's nothing
preventing that.

> +	case DRM_IOCTL_VERSION: /* RW */
> +		return drm_version(tcp, arg);
> +	case DRM_IOCTL_GET_UNIQUE: /* RW */

> +	//case DRM_IOCTL_SET_UNIQUE: /* W */ /* invalid op */
Leftover comment.

> +		return drm_unique(tcp, arg);
> +	case DRM_IOCTL_GET_MAP: /* RW */
> +		return drm_get_map(tcp, arg);
> +	case DRM_IOCTL_GET_CLIENT: /* RW */
> +		return drm_get_client(tcp, arg);
> +	case DRM_IOCTL_GET_STATS: /* R */
> +		return drm_get_stats(tcp, arg);
> +	case DRM_IOCTL_ADD_MAP: /* RW */
> +		return drm_add_map(tcp, arg);
> +	case DRM_IOCTL_ADD_BUFS: /* RW */
> +		return drm_add_bufs(tcp, arg);
> +	case DRM_IOCTL_MARK_BUFS: /* W */
> +		return drm_mark_bufs(tcp, arg);
> +	case DRM_IOCTL_INFO_BUFS: /* RW */
> +		return drm_info_bufs(tcp, arg);
> +	case DRM_IOCTL_MAP_BUFS: /* RW */
> +		return drm_map_bufs(tcp, arg);
> +	case DRM_IOCTL_FREE_BUFS: /* W */
> +		return drm_free_bufs(tcp, arg);
> +	case DRM_IOCTL_RM_MAP: /* W */
> +		return drm_rm_map(tcp, arg);
> +	case DRM_IOCTL_SET_SAREA_CTX:
> +	case DRM_IOCTL_GET_SAREA_CTX:
> +		return drm_sarea_ctx(tcp, arg, code == DRM_IOCTL_GET_SAREA_CTX);
> +	case DRM_IOCTL_RES_CTX: /* RW */
> +		return drm_res_ctx(tcp, arg);
> +	case DRM_IOCTL_AGP_ENABLE: /* W */
> +		return drm_agp_enable(tcp, arg);
> +	case DRM_IOCTL_AGP_INFO: /* R */
> +		return drm_agp_info(tcp, arg);
> +	case DRM_IOCTL_AGP_ALLOC: /* RW */
> +		return drm_agp_alloc(tcp, arg);
> +	case DRM_IOCTL_AGP_FREE: /* W */
> +		return drm_agp_free(tcp, arg);
> +	case DRM_IOCTL_AGP_BIND: /* W */
> +	case DRM_IOCTL_AGP_UNBIND: /* W */
> +		return drm_agp_bind(tcp, arg, code == DRM_IOCTL_AGP_BIND);
> +	case DRM_IOCTL_SG_ALLOC: /* RW */
> +	case DRM_IOCTL_SG_FREE: /* W */
> +		return drm_scatter_gather(tcp, arg, code == DRM_IOCTL_SG_ALLOC);
> +	case DRM_IOCTL_WAIT_VBLANK: /* RW */
> +		return drm_wait_vblank(tcp, arg);
> +	case DRM_IOCTL_MODE_ADDFB2: /* RW */
> +		return drm_mode_add_fb2(tcp, arg);
> +	}
> +	/* variable length, so we can't use switch(code) to identify DRM_IOCTL_MODE_GETCONNECTOR */
> +	if (_IOC_NR(code) == _IOC_NR(DRM_IOCTL_MODE_GETCONNECTOR)) {
> +		return drm_mode_get_connector(tcp, arg);
> +	}
> +
> +	if (_IOC_NR(code) == _IOC_NR(DRM_IOCTL_MODE_GETPLANERESOURCES)) {
> +		return drm_mode_getplaneresources(tcp, arg);
> +	}
> +
> +	if (_IOC_NR(code) == _IOC_NR(DRM_IOCTL_MODE_OBJ_GETPROPERTIES)) {
> +		return drm_mode_obj_getproperties(tcp, arg);
> +	}
> +
> +	if (_IOC_NR(code) == _IOC_NR(DRM_IOCTL_MODE_OBJ_SETPROPERTY)) {
> +		return drm_mode_obj_setproperty(tcp, arg);
> +	}
> +
> +	return RVAL_DECODED;
> +}
> +
> +#endif /* HAVE_DRM_H || HAVE_DRM_DRM_H */
> diff --git a/ioctl.c b/ioctl.c
> index b80292cb..b9aa9833 100644
> --- a/ioctl.c
> +++ b/ioctl.c
> @@ -195,6 +195,10 @@ ioctl_decode_command_number(struct tcb *tcp)
>  				return 1;
>  			}
>  			return 0;
> +#if defined(HAVE_DRM_H) || defined(HAVE_DRM_DRM_H)
> +		case 'd':
> +			return drm_decode_number(tcp, code);
> +#endif
I'd prefer having this code unconditional here and put a stub drm_decode_number
in drm_ioctl.c, but it's more up to Dmitry.

>  		default:
>  			return 0;
>  	}
> @@ -264,6 +268,10 @@ ioctl_decode(struct tcb *tcp)
>  		return fs_x_ioctl(tcp, code, arg);
>  	case 0x22:
>  		return scsi_ioctl(tcp, code, arg);

> +#if defined(HAVE_DRM_H) || defined(HAVE_DRM_DRM_H)
> +	case 'd':
> +		return drm_ioctl(tcp, code, arg);
> +#endif
Same here regarding drm_ioctl.

>  	case 'L':
>  		return loop_ioctl(tcp, code, arg);
>  #ifdef HAVE_STRUCT_MTD_WRITE_REQ
> diff --git a/xlat/drm_buf_desc_flags.in b/xlat/drm_buf_desc_flags.in
> new file mode 100644
> index 00000000..64c41fa9
> --- /dev/null
> +++ b/xlat/drm_buf_desc_flags.in
> @@ -0,0 +1,5 @@
"#sorted" is (probably) missing here.

> +_DRM_PAGE_ALIGN		0x01
> +_DRM_AGP_BUFFER		0x02
> +_DRM_SG_BUFFER		0x04
> +_DRM_FB_BUFFER		0x08
> +_DRM_PCI_BUFFER_RO	0x10
> diff --git a/xlat/drm_capability.in b/xlat/drm_capability.in
> new file mode 100644
> index 00000000..ac30d02f
> --- /dev/null
> +++ b/xlat/drm_capability.in
> @@ -0,0 +1,14 @@
"#value_indexed" is missing here.

> +DRM_CAP_DUMB_BUFFER		0x1
> +DRM_CAP_VBLANK_HIGH_CRTC	0x2
> +DRM_CAP_DUMB_PREFERRED_DEPTH	0x3
> +DRM_CAP_DUMB_PREFER_SHADOW	0x4
> +DRM_CAP_PRIME			0x5
> +DRM_CAP_TIMESTAMP_MONOTONIC	0x6
> +DRM_CAP_ASYNC_PAGE_FLIP		0x7
> +DRM_CAP_CURSOR_WIDTH		0x8
> +DRM_CAP_CURSOR_HEIGHT		0x9
> +DRM_CAP_ADDFB2_MODIFIERS	0x10
> +DRM_CAP_PAGE_FLIP_TARGET	0x11
> +DRM_CAP_CRTC_IN_VBLANK_EVENT	0x12
> +DRM_CAP_SYNCOBJ			0x13
> +DRM_CAP_SYNCOBJ_TIMELINE	0x14
> diff --git a/xlat/drm_client_capability.in b/xlat/drm_client_capability.in
> new file mode 100644
> index 00000000..7c3d867c
> --- /dev/null
> +++ b/xlat/drm_client_capability.in
> @@ -0,0 +1,5 @@
"#value_indexed" is missing here.

> +DRM_CLIENT_CAP_STEREO_3D		1
> +DRM_CLIENT_CAP_UNIVERSAL_PLANES		2
> +DRM_CLIENT_CAP_ATOMIC			3
> +DRM_CLIENT_CAP_ASPECT_RATIO		4
> +DRM_CLIENT_CAP_WRITEBACK_CONNECTORS	5
> diff --git a/xlat/drm_control_func.in b/xlat/drm_control_func.in
> new file mode 100644
> index 00000000..c270bc62
> --- /dev/null
> +++ b/xlat/drm_control_func.in
> @@ -0,0 +1,4 @@
"#value_indexed" is missing here.

> +DRM_ADD_COMMAND		0
> +DRM_RM_COMMAND		1
> +DRM_INST_HANDLER	2
> +DRM_UNINST_HANDLER	3
> diff --git a/xlat/drm_crtc_sequence_flags.in b/xlat/drm_crtc_sequence_flags.in
> new file mode 100644
> index 00000000..8283287d
> --- /dev/null
> +++ b/xlat/drm_crtc_sequence_flags.in
> @@ -0,0 +1,2 @@
"#sorted" is (probably) missing here.

> +DRM_CRTC_SEQUENCE_RELATIVE	0x00000001
> +DRM_CRTC_SEQUENCE_NEXT_ON_MISS	0x00000002
> diff --git a/xlat/drm_ctx_flags.in b/xlat/drm_ctx_flags.in
> new file mode 100644
> index 00000000..4225a19b
> --- /dev/null
> +++ b/xlat/drm_ctx_flags.in
> @@ -0,0 +1,2 @@
"#sorted" is (probably) missing here.

> +_DRM_CONTEXT_PRESERVED	0x01
> +_DRM_CONTEXT_2DONLY	0x02
> diff --git a/xlat/drm_lock_flags.in b/xlat/drm_lock_flags.in
> new file mode 100644
> index 00000000..960d946f
> --- /dev/null
> +++ b/xlat/drm_lock_flags.in
> @@ -0,0 +1,6 @@
"#sorted" is (probably) missing here.

> +_DRM_LOCK_READY		0x01
> +_DRM_LOCK_QUIESCENT	0x02
> +_DRM_LOCK_FLUSH		0x04
> +_DRM_LOCK_FLUSH_ALL	0x08
> +_DRM_HALT_ALL_QUEUES	0x10
> +_DRM_HALT_CUR_QUEUES	0x20
> diff --git a/xlat/drm_map_flags.in b/xlat/drm_map_flags.in
> new file mode 100644
> index 00000000..9cd46901
> --- /dev/null
> +++ b/xlat/drm_map_flags.in
"#sorted" is (probably) missing here.

> @@ -0,0 +1,8 @@
> +_DRM_RESTRICTED		0x01
> +_DRM_READ_ONLY		0x02
> +_DRM_LOCKED		0x04
> +_DRM_KERNEL		0x08
> +_DRM_WRITE_COMBINING	0x10
> +_DRM_CONTAINS_LOCK	0x20
> +_DRM_REMOVABLE		0x40
> +_DRM_DRIVER		0x80
> diff --git a/xlat/drm_map_type.in b/xlat/drm_map_type.in
> new file mode 100644
> index 00000000..6a51a66a
> --- /dev/null
> +++ b/xlat/drm_map_type.in
> @@ -0,0 +1,6 @@
"#value_indexed" is missing here.

> +_DRM_FRAME_BUFFER	0
> +_DRM_REGISTERS		1
> +_DRM_SHM		2
> +_DRM_AGP		3
> +_DRM_SCATTER_GATHER	4
> +_DRM_CONSISTENT		5
> diff --git a/xlat/drm_mode_encoder_type.in b/xlat/drm_mode_encoder_type.in
> new file mode 100644
> index 00000000..cbd67918
> --- /dev/null
> +++ b/xlat/drm_mode_encoder_type.in
> @@ -0,0 +1,9 @@
"#value_indexed" is missing here.

> +DRM_MODE_ENCODER_NONE		0
> +DRM_MODE_ENCODER_DAC		1
> +DRM_MODE_ENCODER_TMDS		2
> +DRM_MODE_ENCODER_LVDS		3
> +DRM_MODE_ENCODER_TVDAC		4
> +DRM_MODE_ENCODER_VIRTUAL	5
> +DRM_MODE_ENCODER_DSI		6
> +DRM_MODE_ENCODER_DPMST		7
> +DRM_MODE_ENCODER_DPI		8
> diff --git a/xlat/drm_mode_flags.in b/xlat/drm_mode_flags.in
> new file mode 100644
> index 00000000..d862689c
> --- /dev/null
> +++ b/xlat/drm_mode_flags.in
> @@ -0,0 +1,6 @@
"#sorted" is (probably) missing here.

> +DRM_MODE_FLAG_PIC_AR_MASK	(0x0F << 19)
I doubt this one should be here.

> +DRM_MODE_FLAG_PIC_AR_NONE	(0 << 19)
> +DRM_MODE_FLAG_PIC_AR_4_3	(1 << 19)
> +DRM_MODE_FLAG_PIC_AR_16_9	(2 << 19)
> +DRM_MODE_FLAG_PIC_AR_64_27	(3 << 19)
> +DRM_MODE_FLAG_PIC_AR_256_135	(4 << 19)
> diff --git a/xlat/drm_mode_page_flip_flags.in b/xlat/drm_mode_page_flip_flags.in
> new file mode 100644
> index 00000000..b0006d84
> --- /dev/null
> +++ b/xlat/drm_mode_page_flip_flags.in
> @@ -0,0 +1,6 @@

> +DRM_MODE_PAGE_FLIP_EVENT
> +DRM_MODE_PAGE_FLIP_ASYNC
> +DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE
> +DRM_MODE_PAGE_FLIP_TARGET_RELATIVE
Those doesn't feel to be arch-specific, so their values can be provided
here.

> +DRM_MODE_PAGE_FLIP_TARGET
It should be before DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE and
DRM_MODE_PAGE_FLIP_TARGET_RELATIVE in order to be decoded by printflags.

> +DRM_MODE_PAGE_FLIP_FLAGS
It should go first in order to be decoded bt printflags. I'm not sure,
however, whether this is a part of kernel API.

> diff --git a/xlat/drm_mode_type.in b/xlat/drm_mode_type.in
> new file mode 100644
> index 00000000..3e9a9094
> --- /dev/null
> +++ b/xlat/drm_mode_type.in
> @@ -0,0 +1,8 @@
"#sorted" is (probably) missing here.

> +DRM_MODE_TYPE_BUILTIN		(1 << 0)
> +DRM_MODE_TYPE_CLOCK_C		(1 << 1 | DRM_MODE_TYPE_BUILTIN)
> +DRM_MODE_TYPE_CRTC_C		(1 << 2 | DRM_MODE_TYPE_BUILTIN)
> +DRM_MODE_TYPE_PREFERRED		(1 << 3)
> +DRM_MODE_TYPE_DEFAULT		(1 << 4)
> +DRM_MODE_TYPE_USERDEF		(1 << 5)
> +DRM_MODE_TYPE_DRIVER		(1 << 6)
> +DRM_MODE_TYPE_ALL		(DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_USERDEF | DRM_MODE_TYPE_DRIVER)
> diff --git a/xlat/drm_modeset_cmd.in b/xlat/drm_modeset_cmd.in
> new file mode 100644
> index 00000000..a58a9076
> --- /dev/null
> +++ b/xlat/drm_modeset_cmd.in
> @@ -0,0 +1,2 @@
"#value_indexed" is missing here.

> +_DRM_PRE_MODESET	1
> +_DRM_POST_MODESET	2

> diff --git a/xlat/drm_stat_type.in b/xlat/drm_stat_type.in
> new file mode 100644
> index 00000000..b15f18d5
> --- /dev/null
> +++ b/xlat/drm_stat_type.in
> @@ -0,0 +1,15 @@
"#value_indexed" is missing here.

> +_DRM_STAT_LOCK		0
> +_DRM_STAT_OPENS		1

> diff --git a/xlat/drm_syncobj_flags.in b/xlat/drm_syncobj_flags.in
> new file mode 100644
> index 00000000..b047f1c0
> --- /dev/null
> +++ b/xlat/drm_syncobj_flags.in
> @@ -0,0 +1 @@
"#sorted" is missing here.

> +DRM_SYNCOBJ_CREATE_SIGNALED	(1 << 0)
> diff --git a/xlat/drm_syncobj_wait_flags.in b/xlat/drm_syncobj_wait_flags.in
> new file mode 100644
> index 00000000..185f94b1
> --- /dev/null
> +++ b/xlat/drm_syncobj_wait_flags.in
> @@ -0,0 +1,3 @@
"#sorted" is missing here.

> +DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL		(1 << 0)
> +DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT	(1 << 1)
> +DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE	(1 << 2)


> diff --git a/xlat/drm_vblank_seq_type.in b/xlat/drm_vblank_seq_type.in
> new file mode 100644
> index 00000000..6460cf55
> --- /dev/null
> +++ b/xlat/drm_vblank_seq_type.in
> @@ -0,0 +1,8 @@
"#sorted" is missing here.

> +_DRM_VBLANK_ABSOLUTE		0x0
> +_DRM_VBLANK_RELATIVE		0x1

> +_DRM_VBLANK_HIGH_CRTC_MASK	0x0000003e
I'm not sure about leading zeroes here, it complicates reading.

> +_DRM_VBLANK_EVENT		0x4000000
> +_DRM_VBLANK_FLIP		0x8000000
> +_DRM_VBLANK_NEXTONMISS		0x10000000
> +_DRM_VBLANK_SECONDARY		0x20000000
> +_DRM_VBLANK_SIGNAL		0x40000000


It's also likely worth considering abbreviating output for some
informational ioctls.


More information about the Strace-devel mailing list