1#!/bin/sh
2# Copyright (c) 2015 Dmitry V. Levin <ldv@altlinux.org>
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8# 1. Redistributions of source code must retain the above copyright
9#    notice, this list of conditions and the following disclaimer.
10# 2. Redistributions in binary form must reproduce the above copyright
11#    notice, this list of conditions and the following disclaimer in the
12#    documentation and/or other materials provided with the distribution.
13# 3. The name of the author may not be used to endorse or promote products
14#    derived from this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27set -efu
28
29# This script processes header files containing ioctl command definitions in
30# symbolic form, assuming that these definitions match the following regular
31# expressions:
32
33r_define='^[[:space:]]*#[[:space:]]*define[[:space:]]\+'
34r_cmd_name='[A-Z][A-Z0-9_]*'
35r_io='\([A-Z]\+\)\?_S\?\(IO\|IOW\|IOR\|IOWR\|IOC\)'
36r_value='[[:space:]]\+'"$r_io"'[[:space:]]*([^)]'
37regexp="${r_define}${r_cmd_name}${r_value}"
38
39uname_m="$(uname -m)"
40me="${0##*/}"
41msg()
42{
43	printf >&2 '%s\n' "$me: $*"
44}
45
46prefix=
47case $# in
48	1)	inc_dir="$1"; shift
49		;;
50	2)	inc_dir="$1"; shift
51		prefix="$1"; shift
52		;;
53	*)	echo >&2 "usage: $me include-directory [prefix]"
54		exit 1
55		;;
56esac
57
58[ -z "$prefix" ] ||
59	prefix="${prefix%%/}/"
60
61tmpdir=
62cleanup()
63{
64	trap - EXIT
65	[ -z "$tmpdir" ] ||
66		rm -rf -- "$tmpdir"
67	exit "$@"
68}
69
70trap 'cleanup $?' EXIT
71trap 'cleanup 1' HUP PIPE INT QUIT TERM
72tmpdir="$(mktemp -dt "$me.XXXXXX")"
73
74# list interesting files in $inc_dir.
75cd "$inc_dir"
76inc_dir="$(pwd -P)"
77find . -type f -name '*.h' -print0 |
78	xargs -r0 grep -l "$r_value" -- > "$tmpdir"/headers1.list ||
79		exit 0
80cd - > /dev/null
81sed 's|^\./\(uapi/\)\?||' < "$tmpdir"/headers1.list > "$tmpdir"/headers.list
82LC_COLLATE=C sort -u -o "$tmpdir"/headers.list "$tmpdir"/headers.list
83
84msg "processing $(wc -l < "$tmpdir"/headers.list) header files from $inc_dir"
85failed=0
86
87CC="${CC:-gcc}"
88CPP="${CPP:-cpp}"
89CPPFLAGS="${CPPFLAGS-} -D__EXPORTED_HEADERS__"
90CFLAGS="${CFLAGS:--Wall -O2} -D__EXPORTED_HEADERS__"
91LDFLAGS="${LDFLAGS-}"
92INCLUDES="-I$inc_dir/uapi -I$inc_dir ${INCLUDES-}"
93
94$CC $INCLUDES $CFLAGS -c -o "$tmpdir"/print_ioctlent.o "${0%/*}"/print_ioctlent.c
95
96# Hook onto <asm-generic/ioctl.h> and <asm/ioctl.h>
97for d in asm-generic asm; do
98	mkdir "$tmpdir/$d"
99	cat > "$tmpdir/$d"/ioctl.h <<__EOF__
100#include_next <$d/ioctl.h>
101#undef _IOC
102#define _IOC(dir,type,nr,size) dir, type, nr, size
103__EOF__
104done
105
106INCLUDES="-I$tmpdir $INCLUDES"
107
108process_file()
109{
110	local f="$1"; shift
111
112	# Common code for every processed file.
113	cat > "$tmpdir"/printents.c <<__EOF__
114#include <asm/termbits.h>
115#include <asm/ioctl.h>
116#include <linux/types.h>
117#include <linux/limits.h>
118#include <linux/major.h>
119
120#include <sys/types.h>
121#include <sys/socket.h>
122#include <stdint.h>
123#include <stdbool.h>
124
125#ifndef NULL
126# define NULL ((void*)0)
127#endif
128#ifndef __user
129# define __user
130#endif
131#ifndef __iomem
132# define __iomem
133#endif
134#ifndef __noreturn
135# define __noreturn __attribute__((noreturn))
136#endif
137#ifndef __packed
138# define __packed __attribute__((packed))
139#endif
140
141typedef signed char s8;
142typedef unsigned char u8;
143typedef signed short s16;
144typedef unsigned short u16;
145typedef signed int s32;
146typedef unsigned int u32;
147typedef signed long long s64;
148typedef unsigned long long u64;
149
150#include "fixes.h"
151
152#include <asm/bitsperlong.h>
153#ifndef BITS_PER_LONG
154# define BITS_PER_LONG __BITS_PER_LONG
155#endif
156
157#include "$f"
158
159void print_ioctlent(const char *, const char *, unsigned short, unsigned short, unsigned short, unsigned short);
160
161int main(void)
162{
163
164#include "defs.h"
165
166return 0;
167}
168__EOF__
169
170	# Soft pre-include workarounds for some processed files.  Fragile.
171	case "$f" in
172		*asm/amigayle.h)
173			return 0 # false positive
174			;;
175		*asm/cmb.h)
176			echo '#include <asm/dasd.h>'
177			;;
178		*asm/core_*.h)
179			return 0 # false positives
180			;;
181		*asm/ioctls.h)
182			cat <<'__EOF__'
183#include <asm/termios.h>
184#include <linux/serial.h>
185__EOF__
186			;;
187		drm/sis_drm.h)
188			echo '#include <drm/drm.h>'
189			;;
190		*drm/*_drm.h)
191			echo '#include <drm/drm.h>' > "$tmpdir/drm.h"
192			;;
193		fbio.h|*/fbio.h)
194			cat <<'__EOF__'
195#include <linux/fb.h>
196#undef FBIOGETCMAP
197#undef FBIOPUTCMAP
198__EOF__
199			;;
200		*linux/atm_zatm.h)
201			cat <<'__EOF__'
202#include <linux/atm.h>
203#ifndef _LINUX_TIME_H
204# define _LINUX_TIME_H
205#endif
206#ifndef _UAPI_LINUX_TIME_H
207# define _UAPI_LINUX_TIME_H
208#endif
209__EOF__
210			;;
211		*linux/atm?*.h)
212			echo '#include <linux/atm.h>'
213			;;
214		*linux/auto_fs*.h)
215			echo 'typedef u32 compat_ulong_t;'
216			;;
217		*linux/coda.h|*android_alarm.h)
218			cat <<'__EOF__'
219#ifndef _LINUX_TIME_H
220# define _LINUX_TIME_H
221#endif
222#ifndef _UAPI_LINUX_TIME_H
223# define _UAPI_LINUX_TIME_H
224#endif
225__EOF__
226			;;
227		*linux/fs.h|*linux/ncp_fs.h)
228			cat <<'__EOF__'
229#include <linux/blktrace_api.h>
230#include <linux/fiemap.h>
231__EOF__
232			;;
233		*linux/if_pppox.h)
234			echo '#include <netinet/in.h>'
235			;;
236		*linux/if_tun.h|*linux/ppp-ioctl.h)
237			echo '#include <linux/filter.h>'
238			;;
239		*linux/isdn_ppp.h|*linux/gsmmux.h)
240			echo '#include <linux/if.h>'
241			;;
242		*media*/saa6588.h)
243			echo 'typedef struct poll_table_struct poll_table;'
244			;;
245		*linux/ivtvfb.h|*linux/meye.h|*media/*.h)
246			echo '#include <linux/videodev2.h>'
247			;;
248		*linux/kvm.h)
249			case "$uname_m" in
250				i?86|x86_64|arm*|ppc*|s390*) ;;
251				*) return 0 ;; # not applicable
252			esac
253			;;
254		*linux/sonet.h)
255			echo '#include <linux/atmioc.h>'
256			;;
257		*linux/usbdevice_fs.h)
258			cat <<'__EOF__'
259struct usbdevfs_ctrltransfer32 { __u32 unused[4]; };
260struct usbdevfs_bulktransfer32 { __u32 unused[4]; };
261struct usbdevfs_disconnectsignal32 { __u32 unused[2]; };
262struct usbdevfs_urb32 { __u8 unused[42]; };
263struct usbdevfs_ioctl32 { __u32 unused[3]; };
264__EOF__
265			;;
266		logger.h|*/logger.h)
267			echo 'typedef __u32 kuid_t;'
268			;;
269		*sound/asequencer.h)
270			cat <<'__EOF__'
271#include <sound/asound.h>
272struct snd_seq_queue_owner { __u32 unused[0]; };
273__EOF__
274			;;
275		*sound/emu10k1.h)
276			cat <<'__EOF__'
277#include <sound/asound.h>
278#ifndef DECLARE_BITMAP
279# define DIV_ROUND_UP(x,y) (((x) + ((y) - 1)) / (y))
280# define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, 8 * sizeof(long))
281# define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)]
282#endif
283__EOF__
284			;;
285		*video/sstfb.h)
286			echo 'struct fb_info;'
287			;;
288		*xen/evtchn.h|*xen/gntdev.h)
289			cat <<'__EOF__'
290typedef uint32_t grant_ref_t;
291typedef uint16_t domid_t;
292__EOF__
293			;;
294		*xen/interface/*.h)
295			return 0 # false positives
296			;;
297		*xen/privcmd.h)
298			return 0 # too much work to make it compileable
299			;;
300	esac > "$tmpdir"/fixes.h
301
302	cat > "$tmpdir"/header.in <<__EOF__
303#include <asm/bitsperlong.h>
304#ifndef BITS_PER_LONG
305# define BITS_PER_LONG __BITS_PER_LONG
306#endif
307#include "$f"
308__EOF__
309
310	if [ -f "$inc_dir/uapi/$f" ]; then
311		s="$inc_dir/uapi/$f"
312	elif [ -f "$inc_dir/$f" ]; then
313		s="$inc_dir/$f"
314	else
315		msg "$f: file not found"
316		return 1
317	fi
318
319	[ -n "${f##*/*}" ] ||
320		mkdir -p "$tmpdir/${f%/*}"
321	# Hard workarounds for some processed files.  Very fragile.
322	case "$f" in
323		*asm-generic/ioctls.h)
324			# Filter out macros defined using unavailable types.
325			case "$uname_m" in
326				alpha*|ppc*)
327					grep -Fv 'struct termios2' < "$s" > "$tmpdir/$f"
328					;;
329			esac
330			;;
331		*acpi/*|*linux/i2o.h|*media*/exynos-fimc.h|*media/v4l2-subdev.h|*net/bluetooth/*|net/nfc/nci_core.h)
332			# Fetch macros only.
333			grep "${r_define}${r_cmd_name}" < "$s" > "$tmpdir/$f"
334			;;
335		binder.h|*/binder.h)
336			# Convert enums to macros.
337			sed '/^enum binder/,/^};/d' < "$s" > "$tmpdir/$f"
338			sed -n '/^enum binder/,/^};/ s/^[[:space:]].*/&/p' < "$s" |
339			sed -e '
340s/^[[:space:]]*\([A-Z][A-Z_0-9]*\)[[:space:]]*=[[:space:]]*_\(IO\|IOW\|IOR\|IOWR\|IOC\)[[:space:]]*(/#define \1 _\2(/
341s/^\(#define .*)\),$/\1/
342s/^\(#define .*,\)$/\1 \\/
343s/^\([[:space:]]\+[^),]\+)\),$/\1/' >> "$tmpdir/$f"
344			;;
345		*drm/r128_drm.h)
346			# Filter out the code that references unknown types.
347			sed '/drm_r128_clear2_t/d' < "$s" > "$tmpdir/$f"
348			;;
349		*drm/sis_drm.h)
350			# Filter out the code that references unknown types.
351			sed '/^struct sis_file_private/,/^}/d' < "$s" > "$tmpdir/$f"
352			;;
353		*drm/via_drm.h)
354			# Create the file it attempts to include.
355			touch "$tmpdir/via_drmclient.h"
356			# Filter out the code that references unknown types.
357			sed '/^struct via_file_private/,/^}/d' < "$s" > "$tmpdir/$f"
358			;;
359		*linux/nilfs2_fs.h)
360			# Create the file it attempts to include.
361			touch "$tmpdir/asm/bug.h"
362			;;
363		*linux/vmw_vmci_defs.h)
364			# Fetch ioctl macros only.
365			grep "${r_define}I" < "$s" > "$tmpdir/$f"
366			;;
367		*media/v4l2-common.h)
368			# Fetch one piece of code containing ioctls definitions.
369			sed -n '/ remaining ioctls/,/ ---/p' < "$s" > "$tmpdir/$f"
370			;;
371		openpromio.h|*/openpromio.h|fbio.h|*/fbio.h)
372			# Create the file it attempts to include.
373			mkdir -p "$tmpdir/linux"
374			touch "$tmpdir/linux/compiler.h"
375	esac
376	if [ -f "$tmpdir/$f" ]; then
377		s="$tmpdir/$f"
378	fi
379
380	# This may fail if the file includes unavailable headers.
381	# In case of success it outputs both the #define directives
382	# and the result of preprocessing.
383	$CPP $CPPFLAGS -dD $INCLUDES < "$tmpdir"/header.in > "$tmpdir"/header.out
384
385	# Soft post-preprocess workarounds.  Fragile.
386	case "$f" in
387		*linux/kvm.h)
388			arm_list='KVM_ARM_PREFERRED_TARGET|KVM_ARM_VCPU_INIT'
389			ppc_list='KVM_ALLOCATE_RMA|KVM_CREATE_SPAPR_TCE|KVM_CREATE_SPAPR_TCE_64|KVM_PPC_GET_HTAB_FD|KVM_PPC_RTAS_DEFINE_TOKEN'
390			x86_list='KVM_GET_CPUID2|KVM_GET_DEBUGREGS|KVM_GET_EMULATED_CPUID|KVM_GET_LAPIC|KVM_GET_MSRS|KVM_GET_MSR_INDEX_LIST|KVM_GET_PIT|KVM_GET_PIT2|KVM_GET_SUPPORTED_CPUID|KVM_GET_VCPU_EVENTS|KVM_GET_XCRS|KVM_GET_XSAVE|KVM_SET_CPUID|KVM_SET_CPUID2|KVM_SET_DEBUGREGS|KVM_SET_LAPIC|KVM_SET_MEMORY_ALIAS|KVM_SET_MSRS|KVM_SET_PIT|KVM_SET_PIT2|KVM_SET_VCPU_EVENTS|KVM_SET_XCRS|KVM_SET_XSAVE|KVM_X86_SET_MCE|KVM_XEN_HVM_CONFIG'
391			case "$uname_m" in
392				arm*) list="$ppc_list|$x86_list" ;;
393				ppc*) list="$arm_list|$x86_list" ;;
394				i?86|x86_64*) list="$arm_list|$ppc_list" ;;
395				*) list="$arm_list|$ppc_list|$x86_list" ;;
396			esac
397			sed -r -i "/[[:space:]]($list)[[:space:]]/d" "$tmpdir"/header.out
398			;;
399	esac
400
401	# Need to exclude ioctl commands defined elsewhere.
402	local_defines='^[[:space:]]*#[[:space:]]*define[[:space:]]\+\('"$r_cmd_name"'\)[[:space:]]'
403	sed -n 's/'"$local_defines"'.*/\1\\/p' "$s" > "$tmpdir"/local_names
404	r_local_names="$(tr '\n' '|' < "$tmpdir"/local_names)"
405	r_local_names="${r_local_names%%|}"
406	r_local_names="${r_local_names%%\\}"
407
408	# Keep this in sync with $regexp by replacing $r_cmd_name with $r_local_names.
409	defs_regexp="${r_define}\($r_local_names\)${r_value}"
410
411	qf="$(echo "$prefix$f" | sed 's/[&\/]/\\&/g')"
412	# This outputs lines in the following format:
413	# print_ioctlent("filename.h", "IOCTL_CMD_NAME", IOCTL_CMD_NAME);
414	sed -n 's/'"$defs_regexp"'.*/print_ioctlent("'"$qf"'", "\1", \1);/p' \
415		< "$tmpdir"/header.out > "$tmpdir"/defs.h
416
417	# If something is wrong with the file, this will fail.
418	$CC $INCLUDES $CFLAGS -c -o "$tmpdir"/printents.o "$tmpdir"/printents.c
419	$CC $LDFLAGS -o "$tmpdir"/print_ioctlents \
420		"$tmpdir"/printents.o "$tmpdir"/print_ioctlent.o
421	"$tmpdir"/print_ioctlents > "$tmpdir"/ioctlents
422	cat "$tmpdir"/ioctlents
423	msg "$f: fetched $(grep -c '^{' "$tmpdir"/ioctlents) ioctl entries"
424}
425
426while read f; do
427	(process_file "$f" < /dev/null)
428	[ $? -eq 0 ] || {
429		msg "$f: failed to process"
430		failed=$((1 + $failed))
431	}
432done < "$tmpdir"/headers.list
433
434[ $failed -eq 0 ] ||
435	msg "failed to process $failed file(s)"
436