code:
#
# Linux system call numbers and entry vectors
#
# The format is:
# <num>	<abi>	<name>			[<entry point>			[<oabi compat entry point>]]
#
# Where abi is:
#  common - for system calls shared between oabi and eabi (may have compat)
#  oabi   - for oabi-only system calls (may have compat)
#  eabi   - for eabi-only system calls
#
# For each syscall number, "common" is mutually exclusive with oabi and eabi
#
0	common	restart_syscall		sys_restart_syscall
1	common	exit			sys_exit
2	common	fork			sys_fork
3	common	read			sys_read
4	common	write			sys_write
5	common	open			sys_open
6	common	close			sys_close
# 7 was sys_waitpid
8	common	creat			sys_creat
9	common	link			sys_link
10	common	unlink			sys_unlink
11	common	execve			sys_execve
12	common	chdir			sys_chdir
13	oabi	time			sys_time32
14	common	mknod			sys_mknod
15	common	chmod			sys_chmod
16	common	lchown			sys_lchown16
# 17 was sys_break
# 18 was sys_stat
19	common	lseek			sys_lseek
20	common	getpid			sys_getpid
21	common	mount			sys_mount
22	oabi	umount			sys_oldumount
23	common	setuid			sys_setuid16
24	common	getuid			sys_getuid16
25	oabi	stime			sys_stime32
26	common	ptrace			sys_ptrace
27	oabi	alarm			sys_alarm
# 28 was sys_fstat
29	common	pause			sys_pause
30	oabi	utime			sys_utime32
# 31 was sys_stty
# 32 was sys_gtty
33	common	access			sys_access
34	common	nice			sys_nice
# 35 was sys_ftime
36	common	sync			sys_sync
37	common	kill			sys_kill
38	common	rename			sys_rename
39	common	mkdir			sys_mkdir
40	common	rmdir			sys_rmdir
41	common	dup			sys_dup
42	common	pipe			sys_pipe
43	common	times			sys_times
# 44 was sys_prof
45	common	brk			sys_brk
46	common	setgid			sys_setgid16
47	common	getgid			sys_getgid16
# 48 was sys_signal
49	common	geteuid			sys_geteuid16
50	common	getegid			sys_getegid16
51	common	acct			sys_acct
52	common	umount2			sys_umount
# 53 was sys_lock
54	common	ioctl			sys_ioctl
55	common	fcntl			sys_fcntl
# 56 was sys_mpx
57	common	setpgid			sys_setpgid
# 58 was sys_ulimit
# 59 was sys_olduname
60	common	umask			sys_umask
61	common	chroot			sys_chroot
62	common	ustat			sys_ustat
63	common	dup2			sys_dup2
64	common	getppid			sys_getppid
65	common	getpgrp			sys_getpgrp
66	common	setsid			sys_setsid
67	common	sigaction		sys_sigaction
# 68 was sys_sgetmask
# 69 was sys_ssetmask
70	common	setreuid		sys_setreuid16
71	common	setregid		sys_setregid16
72	common	sigsuspend		sys_sigsuspend
73	common	sigpending		sys_sigpending
74	common	sethostname		sys_sethostname
75	common	setrlimit		sys_setrlimit
# Back compat 2GB limited rlimit
76	oabi	getrlimit		sys_old_getrlimit
77	common	getrusage		sys_getrusage
78	common	gettimeofday		sys_gettimeofday
79	common	settimeofday		sys_settimeofday
80	common	getgroups		sys_getgroups16
81	common	setgroups		sys_setgroups16
82	oabi	select			sys_old_select
83	common	symlink			sys_symlink
# 84 was sys_lstat
85	common	readlink		sys_readlink
86	common	uselib			sys_uselib
87	common	swapon			sys_swapon
88	common	reboot			sys_reboot
89	oabi	readdir			sys_old_readdir
90	oabi	mmap			sys_old_mmap
91	common	munmap			sys_munmap
92	common	truncate		sys_truncate
93	common	ftruncate		sys_ftruncate
94	common	fchmod			sys_fchmod
95	common	fchown			sys_fchown16
96	common	getpriority		sys_getpriority
97	common	setpriority		sys_setpriority
# 98 was sys_profil
99	common	statfs			sys_statfs
100	common	fstatfs			sys_fstatfs
# 101 was sys_ioperm
102	oabi	socketcall		sys_socketcall		sys_oabi_socketcall
103	common	syslog			sys_syslog
104	common	setitimer		sys_setitimer
105	common	getitimer		sys_getitimer
106	common	stat			sys_newstat
107	common	lstat			sys_newlstat
108	common	fstat			sys_newfstat
# 109 was sys_uname
# 110 was sys_iopl
111	common	vhangup			sys_vhangup
# 112 was sys_idle
# syscall to call a syscall!
113	oabi	syscall			sys_syscall
114	common	wait4			sys_wait4
115	common	swapoff			sys_swapoff
116	common	sysinfo			sys_sysinfo
117	oabi	ipc			sys_ipc			sys_oabi_ipc
118	common	fsync			sys_fsync
119	common	sigreturn		sys_sigreturn_wrapper
120	common	clone			sys_clone
121	common	setdomainname		sys_setdomainname
122	common	uname			sys_newuname
# 123 was sys_modify_ldt
124	common	adjtimex		sys_adjtimex_time32
125	common	mprotect		sys_mprotect
126	common	sigprocmask		sys_sigprocmask
# 127 was sys_create_module
128	common	init_module		sys_init_module
129	common	delete_module		sys_delete_module
# 130 was sys_get_kernel_syms
131	common	quotactl		sys_quotactl
132	common	getpgid			sys_getpgid
133	common	fchdir			sys_fchdir
134	common	bdflush			sys_ni_syscall
135	common	sysfs			sys_sysfs
136	common	personality		sys_personality
# 137 was sys_afs_syscall
138	common	setfsuid		sys_setfsuid16
139	common	setfsgid		sys_setfsgid16
140	common	_llseek			sys_llseek
141	common	getdents		sys_getdents
142	common	_newselect		sys_select
143	common	flock			sys_flock
144	common	msync			sys_msync
145	common	readv			sys_readv
146	common	writev			sys_writev
147	common	getsid			sys_getsid
148	common	fdatasync		sys_fdatasync
149	common	_sysctl			sys_ni_syscall
150	common	mlock			sys_mlock
151	common	munlock			sys_munlock
152	common	mlockall		sys_mlockall
153	common	munlockall		sys_munlockall
154	common	sched_setparam		sys_sched_setparam
155	common	sched_getparam		sys_sched_getparam
156	common	sched_setscheduler	sys_sched_setscheduler
157	common	sched_getscheduler	sys_sched_getscheduler
158	common	sched_yield		sys_sched_yield
159	common	sched_get_priority_max	sys_sched_get_priority_max
160	common	sched_get_priority_min	sys_sched_get_priority_min
161	common	sched_rr_get_interval	sys_sched_rr_get_interval_time32
162	common	nanosleep		sys_nanosleep_time32
163	common	mremap			sys_mremap
164	common	setresuid		sys_setresuid16
165	common	getresuid		sys_getresuid16
# 166 was sys_vm86
# 167 was sys_query_module
168	common	poll			sys_poll
169	common	nfsservctl
170	common	setresgid		sys_setresgid16
171	common	getresgid		sys_getresgid16
172	common	prctl			sys_prctl
173	common	rt_sigreturn		sys_rt_sigreturn_wrapper
174	common	rt_sigaction		sys_rt_sigaction
175	common	rt_sigprocmask		sys_rt_sigprocmask
176	common	rt_sigpending		sys_rt_sigpending
177	common	rt_sigtimedwait		sys_rt_sigtimedwait_time32
178	common	rt_sigqueueinfo		sys_rt_sigqueueinfo
179	common	rt_sigsuspend		sys_rt_sigsuspend
180	common	pread64			sys_pread64		sys_oabi_pread64
181	common	pwrite64		sys_pwrite64		sys_oabi_pwrite64
182	common	chown			sys_chown16
183	common	getcwd			sys_getcwd
184	common	capget			sys_capget
185	common	capset			sys_capset
186	common	sigaltstack		sys_sigaltstack
187	common	sendfile		sys_sendfile
# 188 reserved
# 189 reserved
190	common	vfork			sys_vfork
# SuS compliant getrlimit
191	common	ugetrlimit		sys_getrlimit
192	common	mmap2			sys_mmap2
193	common	truncate64		sys_truncate64		sys_oabi_truncate64
194	common	ftruncate64		sys_ftruncate64		sys_oabi_ftruncate64
195	common	stat64			sys_stat64		sys_oabi_stat64
196	common	lstat64			sys_lstat64		sys_oabi_lstat64
197	common	fstat64			sys_fstat64		sys_oabi_fstat64
198	common	lchown32		sys_lchown
199	common	getuid32		sys_getuid
200	common	getgid32		sys_getgid
201	common	geteuid32		sys_geteuid
202	common	getegid32		sys_getegid
203	common	setreuid32		sys_setreuid
204	common	setregid32		sys_setregid
205	common	getgroups32		sys_getgroups
206	common	setgroups32		sys_setgroups
207	common	fchown32		sys_fchown
208	common	setresuid32		sys_setresuid
209	common	getresuid32		sys_getresuid
210	common	setresgid32		sys_setresgid
211	common	getresgid32		sys_getresgid
212	common	chown32			sys_chown
213	common	setuid32		sys_setuid
214	common	setgid32		sys_setgid
215	common	setfsuid32		sys_setfsuid
216	common	setfsgid32		sys_setfsgid
217	common	getdents64		sys_getdents64
218	common	pivot_root		sys_pivot_root
219	common	mincore			sys_mincore
220	common	madvise			sys_madvise
221	common	fcntl64			sys_fcntl64		sys_oabi_fcntl64
# 222 for tux
# 223 is unused
224	common	gettid			sys_gettid
225	common	readahead		sys_readahead		sys_oabi_readahead
226	common	setxattr		sys_setxattr
227	common	lsetxattr		sys_lsetxattr
228	common	fsetxattr		sys_fsetxattr
229	common	getxattr		sys_getxattr
230	common	lgetxattr		sys_lgetxattr
231	common	fgetxattr		sys_fgetxattr
232	common	listxattr		sys_listxattr
233	common	llistxattr		sys_llistxattr
234	common	flistxattr		sys_flistxattr
235	common	removexattr		sys_removexattr
236	common	lremovexattr		sys_lremovexattr
237	common	fremovexattr		sys_fremovexattr
238	common	tkill			sys_tkill
239	common	sendfile64		sys_sendfile64
240	common	futex			sys_futex_time32
241	common	sched_setaffinity	sys_sched_setaffinity
242	common	sched_getaffinity	sys_sched_getaffinity
243	common	io_setup		sys_io_setup
244	common	io_destroy		sys_io_destroy
245	common	io_getevents		sys_io_getevents_time32
246	common	io_submit		sys_io_submit
247	common	io_cancel		sys_io_cancel
248	common	exit_group		sys_exit_group
249	common	lookup_dcookie		sys_lookup_dcookie
250	common	epoll_create		sys_epoll_create
251	common	epoll_ctl		sys_epoll_ctl		sys_oabi_epoll_ctl
252	common	epoll_wait		sys_epoll_wait
253	common	remap_file_pages	sys_remap_file_pages
# 254 for set_thread_area
# 255 for get_thread_area
256	common	set_tid_address		sys_set_tid_address
257	common	timer_create		sys_timer_create
258	common	timer_settime		sys_timer_settime32
259	common	timer_gettime		sys_timer_gettime32
260	common	timer_getoverrun	sys_timer_getoverrun
261	common	timer_delete		sys_timer_delete
262	common	clock_settime		sys_clock_settime32
263	common	clock_gettime		sys_clock_gettime32
264	common	clock_getres		sys_clock_getres_time32
265	common	clock_nanosleep		sys_clock_nanosleep_time32
266	common	statfs64		sys_statfs64_wrapper
267	common	fstatfs64		sys_fstatfs64_wrapper
268	common	tgkill			sys_tgkill
269	common	utimes			sys_utimes_time32
270	common	arm_fadvise64_64	sys_arm_fadvise64_64
271	common	pciconfig_iobase	sys_pciconfig_iobase
272	common	pciconfig_read		sys_pciconfig_read
273	common	pciconfig_write		sys_pciconfig_write
274	common	mq_open			sys_mq_open
275	common	mq_unlink		sys_mq_unlink
276	common	mq_timedsend		sys_mq_timedsend_time32
277	common	mq_timedreceive		sys_mq_timedreceive_time32
278	common	mq_notify		sys_mq_notify
279	common	mq_getsetattr		sys_mq_getsetattr
280	common	waitid			sys_waitid
281	common	socket			sys_socket
282	common	bind			sys_bind		sys_oabi_bind
283	common	connect			sys_connect		sys_oabi_connect
284	common	listen			sys_listen
285	common	accept			sys_accept
286	common	getsockname		sys_getsockname
287	common	getpeername		sys_getpeername
288	common	socketpair		sys_socketpair
289	common	send			sys_send
290	common	sendto			sys_sendto		sys_oabi_sendto
291	common	recv			sys_recv
292	common	recvfrom		sys_recvfrom
293	common	shutdown		sys_shutdown
294	common	setsockopt		sys_setsockopt
295	common	getsockopt		sys_getsockopt
296	common	sendmsg			sys_sendmsg		sys_oabi_sendmsg
297	common	recvmsg			sys_recvmsg
298	common	semop			sys_semop		sys_oabi_semop
299	common	semget			sys_semget
300	common	semctl			sys_old_semctl
301	common	msgsnd			sys_msgsnd
302	common	msgrcv			sys_msgrcv
303	common	msgget			sys_msgget
304	common	msgctl			sys_old_msgctl
305	common	shmat			sys_shmat
306	common	shmdt			sys_shmdt
307	common	shmget			sys_shmget
308	common	shmctl			sys_old_shmctl
309	common	add_key			sys_add_key
310	common	request_key		sys_request_key
311	common	keyctl			sys_keyctl
312	common	semtimedop		sys_semtimedop_time32	sys_oabi_semtimedop
313	common	vserver
314	common	ioprio_set		sys_ioprio_set
315	common	ioprio_get		sys_ioprio_get
316	common	inotify_init		sys_inotify_init
317	common	inotify_add_watch	sys_inotify_add_watch
318	common	inotify_rm_watch	sys_inotify_rm_watch
319	common	mbind			sys_mbind
320	common	get_mempolicy		sys_get_mempolicy
321	common	set_mempolicy		sys_set_mempolicy
322	common	openat			sys_openat
323	common	mkdirat			sys_mkdirat
324	common	mknodat			sys_mknodat
325	common	fchownat		sys_fchownat
326	common	futimesat		sys_futimesat_time32
327	common	fstatat64		sys_fstatat64		sys_oabi_fstatat64
328	common	unlinkat		sys_unlinkat
329	common	renameat		sys_renameat
330	common	linkat			sys_linkat
331	common	symlinkat		sys_symlinkat
332	common	readlinkat		sys_readlinkat
333	common	fchmodat		sys_fchmodat
334	common	faccessat		sys_faccessat
335	common	pselect6		sys_pselect6_time32
336	common	ppoll			sys_ppoll_time32
337	common	unshare			sys_unshare
338	common	set_robust_list		sys_set_robust_list
339	common	get_robust_list		sys_get_robust_list
340	common	splice			sys_splice
341	common	arm_sync_file_range	sys_sync_file_range2
342	common	tee			sys_tee
343	common	vmsplice		sys_vmsplice
344	common	move_pages		sys_move_pages
345	common	getcpu			sys_getcpu
346	common	epoll_pwait		sys_epoll_pwait
347	common	kexec_load		sys_kexec_load
348	common	utimensat		sys_utimensat_time32
349	common	signalfd		sys_signalfd
350	common	timerfd_create		sys_timerfd_create
351	common	eventfd			sys_eventfd
352	common	fallocate		sys_fallocate
353	common	timerfd_settime		sys_timerfd_settime32
354	common	timerfd_gettime		sys_timerfd_gettime32
355	common	signalfd4		sys_signalfd4
356	common	eventfd2		sys_eventfd2
357	common	epoll_create1		sys_epoll_create1
358	common	dup3			sys_dup3
359	common	pipe2			sys_pipe2
360	common	inotify_init1		sys_inotify_init1
361	common	preadv			sys_preadv
362	common	pwritev			sys_pwritev
363	common	rt_tgsigqueueinfo	sys_rt_tgsigqueueinfo
364	common	perf_event_open		sys_perf_event_open
365	common	recvmmsg		sys_recvmmsg_time32
366	common	accept4			sys_accept4
367	common	fanotify_init		sys_fanotify_init
368	common	fanotify_mark		sys_fanotify_mark
369	common	prlimit64		sys_prlimit64
370	common	name_to_handle_at	sys_name_to_handle_at
371	common	open_by_handle_at	sys_open_by_handle_at
372	common	clock_adjtime		sys_clock_adjtime32
373	common	syncfs			sys_syncfs
374	common	sendmmsg		sys_sendmmsg
375	common	setns			sys_setns
376	common	process_vm_readv	sys_process_vm_readv
377	common	process_vm_writev	sys_process_vm_writev
378	common	kcmp			sys_kcmp
379	common	finit_module		sys_finit_module
380	common	sched_setattr		sys_sched_setattr
381	common	sched_getattr		sys_sched_getattr
382	common	renameat2		sys_renameat2
383	common	seccomp			sys_seccomp
384	common	getrandom		sys_getrandom
385	common	memfd_create		sys_memfd_create
386	common	bpf			sys_bpf
387	common	execveat		sys_execveat
388	common	userfaultfd		sys_userfaultfd
389	common	membarrier		sys_membarrier
390	common	mlock2			sys_mlock2
391	common	copy_file_range		sys_copy_file_range
392	common	preadv2			sys_preadv2
393	common	pwritev2		sys_pwritev2
394	common	pkey_mprotect		sys_pkey_mprotect
395	common	pkey_alloc		sys_pkey_alloc
396	common	pkey_free		sys_pkey_free
397	common	statx			sys_statx
398	common	rseq			sys_rseq
399	common	io_pgetevents		sys_io_pgetevents_time32
400	common	migrate_pages		sys_migrate_pages
401	common	kexec_file_load		sys_kexec_file_load
# 402 is unused
403	common	clock_gettime64			sys_clock_gettime
404	common	clock_settime64			sys_clock_settime
405	common	clock_adjtime64			sys_clock_adjtime
406	common	clock_getres_time64		sys_clock_getres
407	common	clock_nanosleep_time64		sys_clock_nanosleep
408	common	timer_gettime64			sys_timer_gettime
409	common	timer_settime64			sys_timer_settime
410	common	timerfd_gettime64		sys_timerfd_gettime
411	common	timerfd_settime64		sys_timerfd_settime
412	common	utimensat_time64		sys_utimensat
413	common	pselect6_time64			sys_pselect6
414	common	ppoll_time64			sys_ppoll
416	common	io_pgetevents_time64		sys_io_pgetevents
417	common	recvmmsg_time64			sys_recvmmsg
418	common	mq_timedsend_time64		sys_mq_timedsend
419	common	mq_timedreceive_time64		sys_mq_timedreceive
420	common	semtimedop_time64		sys_semtimedop
421	common	rt_sigtimedwait_time64		sys_rt_sigtimedwait
422	common	futex_time64			sys_futex
423	common	sched_rr_get_interval_time64	sys_sched_rr_get_interval
424	common	pidfd_send_signal		sys_pidfd_send_signal
425	common	io_uring_setup			sys_io_uring_setup
426	common	io_uring_enter			sys_io_uring_enter
427	common	io_uring_register		sys_io_uring_register
428	common	open_tree			sys_open_tree
429	common	move_mount			sys_move_mount
430	common	fsopen				sys_fsopen
431	common	fsconfig			sys_fsconfig
432	common	fsmount				sys_fsmount
433	common	fspick				sys_fspick
434	common	pidfd_open			sys_pidfd_open
435	common	clone3				sys_clone3
436	common	close_range			sys_close_range
437	common	openat2				sys_openat2
438	common	pidfd_getfd			sys_pidfd_getfd
439	common	faccessat2			sys_faccessat2
440	common	process_madvise			sys_process_madvise
441	common	epoll_pwait2			sys_epoll_pwait2
442	common	mount_setattr			sys_mount_setattr
443	common	quotactl_fd			sys_quotactl_fd
444	common	landlock_create_ruleset		sys_landlock_create_ruleset
445	common	landlock_add_rule		sys_landlock_add_rule
446	common	landlock_restrict_self		sys_landlock_restrict_self
# 447 reserved for memfd_secret
448	common	process_mrelease		sys_process_mrelease
449	common	futex_waitv			sys_futex_waitv
450	common	set_mempolicy_home_node		sys_set_mempolicy_home_node


code:
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
in="$1"
out="$2"
align=1

fileguard=_ASM_ARM_`basename "$out" | sed \
    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \
    -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'`

grep -E "^[0-9A-Fa-fXx]+[[:space:]]+" "$in" | sort -n | tail -n1 | (
    echo "#ifndef ${fileguard}
#define ${fileguard} 1

/*
 * This needs to be greater than __NR_last_syscall+1 in order to account
 * for the padding in the syscall table.
 */
"

    while read nr abi name entry; do
        nr=$(($nr + 1))
        while [ "$(($nr / (256 * $align) ))" -gt 0 ]; do
            align=$(( $align * 4 ))
        done
        nr=$(( ($nr + $align - 1) & ~($align - 1) ))
        echo "/* aligned to $align */"
        echo "#define __NR_syscalls $nr"
    done

    echo ""
    echo "#endif /* ${fileguard} */"
) > "$out"

repo_name: angery/linux


code:
# SPDX-License-Identifier: GPL-2.0-only
vdso.lds
vdso.so.raw
vdsomunge


code:
# SPDX-License-Identifier: GPL-2.0

# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before
# the inclusion of generic Makefile.
ARCH_REL_TYPE_ABS := R_ARM_JUMP_SLOT|R_ARM_GLOB_DAT|R_ARM_ABS32
include $(srctree)/lib/vdso/Makefile

hostprogs := vdsomunge

obj-vdso := vgettimeofday.o datapage.o note.o

# Build rules
targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.so.raw vdso.lds
obj-vdso := $(addprefix $(obj)/, $(obj-vdso))

ccflags-y := -fPIC -fno-common -fno-builtin -fno-stack-protector
ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO32

ldflags-$(CONFIG_CPU_ENDIAN_BE8) := --be8
ldflags-y := -Bsymbolic --no-undefined -soname=linux-vdso.so.1 \
	    -z max-page-size=4096 -shared $(ldflags-y) \
	    --hash-style=sysv --build-id=sha1 \
	    -T

obj-$(CONFIG_VDSO) += vdso.o
CPPFLAGS_vdso.lds += -P -C -U$(ARCH)

CFLAGS_REMOVE_vdso.o = -pg

# Force -O2 to avoid libgcc dependencies
CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS)
ifeq ($(c-gettimeofday-y),)
CFLAGS_vgettimeofday.o = -O2
else
CFLAGS_vgettimeofday.o = -O2 -include $(c-gettimeofday-y)
endif

# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
UBSAN_SANITIZE := n

# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in.
KCOV_INSTRUMENT := n

KASAN_SANITIZE := n

# Force dependency
$(obj)/vdso.o : $(obj)/vdso.so

# Link rule for the .so file
$(obj)/vdso.so.raw: $(obj)/vdso.lds $(obj-vdso) FORCE
	$(call if_changed,vdsold_and_vdso_check)

$(obj)/vdso.so.dbg: $(obj)/vdso.so.raw $(obj)/vdsomunge FORCE
	$(call if_changed,vdsomunge)

# Strip rule for the .so file
$(obj)/%.so: OBJCOPYFLAGS := -S
$(obj)/%.so: $(obj)/%.so.dbg FORCE
	$(call if_changed,objcopy)

# Actual build commands
quiet_cmd_vdsold_and_vdso_check = LD      $@
      cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check)

quiet_cmd_vdsomunge = MUNGE   $@
      cmd_vdsomunge = $(objtree)/$(obj)/vdsomunge $< $@

#
# Install the unstripped copy of vdso.so.dbg.  If our toolchain
# supports build-id, install .build-id links as well.
#
# Cribbed from arch/x86/vdso/Makefile.
#
quiet_cmd_vdso_install = INSTALL $<
define cmd_vdso_install
	cp $< "$(MODLIB)/vdso/vdso.so"; \
	if readelf -n $< | grep -q 'Build ID'; then \
	  buildid=`readelf -n $< |grep 'Build ID' |sed -e 's/^.*Build ID: \(.*\)$$/\1/'`; \
	  first=`echo $$buildid | cut -b-2`; \
	  last=`echo $$buildid | cut -b3-`; \
	  mkdir -p "$(MODLIB)/vdso/.build-id/$$first"; \
	  ln -sf "../../vdso.so" "$(MODLIB)/vdso/.build-id/$$first/$$last.debug"; \
	fi
endef

$(MODLIB)/vdso: FORCE
	@mkdir -p $(MODLIB)/vdso

PHONY += vdso_install
vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso
	$(call cmd,vdso_install)

path: arch/arm/vdso/Makefile


code:
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/linkage.h>
#include <asm/asm-offsets.h>

	.align 2
.L_vdso_data_ptr:
	.long	_start - . - VDSO_DATA_SIZE

ENTRY(__get_datapage)
	.fnstart
	adr	r0, .L_vdso_data_ptr
	ldr	r1, [r0]
	add	r0, r0, r1
	bx	lr
	.fnend
ENDPROC(__get_datapage)


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2012-2018 ARM Limited
 *
 * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
 * Here we can supply some information useful to userland.
 */

#include <linux/uts.h>
#include <linux/version.h>
#include <linux/elfnote.h>
#include <linux/build-salt.h>

ELFNOTE32("Linux", 0, LINUX_VERSION_CODE);
BUILD_SALT;


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Adapted from arm64 version.
 *
 * Copyright (C) 2012 ARM Limited
 *
 * Author: Will Deacon <will.deacon@arm.com>
 */

#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/const.h>
#include <asm/page.h>

	.globl vdso_start, vdso_end
	.section .data..ro_after_init
	.balign PAGE_SIZE
vdso_start:
	.incbin "arch/arm/vdso/vdso.so"
	.balign PAGE_SIZE
vdso_end:

	.previous


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Adapted from arm64 version.
 *
 * GNU linker script for the VDSO library.
 *
 * Copyright (C) 2012 ARM Limited
 *
 * Author: Will Deacon <will.deacon@arm.com>
 * Heavily based on the vDSO linker scripts for other archs.
 */

#include <linux/const.h>
#include <asm/page.h>
#include <asm/vdso.h>

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)

SECTIONS
{
	PROVIDE(_start = .);

	. = SIZEOF_HEADERS;

	.hash		: { *(.hash) }			:text
	.gnu.hash	: { *(.gnu.hash) }
	.dynsym		: { *(.dynsym) }
	.dynstr		: { *(.dynstr) }
	.gnu.version	: { *(.gnu.version) }
	.gnu.version_d	: { *(.gnu.version_d) }
	.gnu.version_r	: { *(.gnu.version_r) }

	.note		: { *(.note.*) }		:text	:note

	.eh_frame_hdr	: { *(.eh_frame_hdr) }		:text	:eh_frame_hdr
	.eh_frame	: { KEEP (*(.eh_frame)) }	:text

	.dynamic	: { *(.dynamic) }		:text	:dynamic

	.rodata		: { *(.rodata*) }		:text

	.text		: { *(.text*) }			:text	=0xe7f001f2

	.got		: { *(.got) }
	.rel.plt	: { *(.rel.plt) }

	/DISCARD/	: {
		*(.note.GNU-stack)
		*(.data .data.* .gnu.linkonce.d.* .sdata*)
		*(.bss .sbss .dynbss .dynsbss)
	}
}

/*
 * We must supply the ELF program headers explicitly to get just one
 * PT_LOAD segment, and set the flags explicitly to make segments read-only.
 */
PHDRS
{
	text		PT_LOAD		FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
	dynamic		PT_DYNAMIC	FLAGS(4);		/* PF_R */
	note		PT_NOTE		FLAGS(4);		/* PF_R */
	eh_frame_hdr	PT_GNU_EH_FRAME;
}

VERSION
{
	LINUX_2.6 {
	global:
		__vdso_clock_gettime;
		__vdso_gettimeofday;
		__vdso_clock_getres;
		__vdso_clock_gettime64;
	local: *;
	};
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2015 Mentor Graphics Corporation.
 *
 * vdsomunge - Host program which produces a shared object
 * architecturally specified to be usable by both soft- and hard-float
 * programs.
 *
 * The Procedure Call Standard for the ARM Architecture (ARM IHI
 * 0042E) says:
 *
 *	6.4.1 VFP and Base Standard Compatibility
 *
 *	Code compiled for the VFP calling standard is compatible with
 *	the base standard (and vice-versa) if no floating-point or
 *	containerized vector arguments or results are used.
 *
 * And ELF for the ARM Architecture (ARM IHI 0044E) (Table 4-2) says:
 *
 *	If both EF_ARM_ABI_FLOAT_XXXX bits are clear, conformance to the
 *	base procedure-call standard is implied.
 *
 * The VDSO is built with -msoft-float, as with the rest of the ARM
 * kernel, and uses no floating point arguments or results.  The build
 * process will produce a shared object that may or may not have the
 * EF_ARM_ABI_FLOAT_SOFT flag set (it seems to depend on the binutils
 * version; binutils starting with 2.24 appears to set it).  The
 * EF_ARM_ABI_FLOAT_HARD flag should definitely not be set, and this
 * program will error out if it is.
 *
 * If the soft-float flag is set, this program clears it.  That's all
 * it does.
 */

#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define swab16(x) \
	((((x) & 0x00ff) << 8) | \
	 (((x) & 0xff00) >> 8))

#define swab32(x) \
	((((x) & 0x000000ff) << 24) | \
	 (((x) & 0x0000ff00) <<  8) | \
	 (((x) & 0x00ff0000) >>  8) | \
	 (((x) & 0xff000000) >> 24))

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define HOST_ORDER ELFDATA2LSB
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define HOST_ORDER ELFDATA2MSB
#endif

/* Some of the ELF constants we'd like to use were added to <elf.h>
 * relatively recently.
 */
#ifndef EF_ARM_EABI_VER5
#define EF_ARM_EABI_VER5 0x05000000
#endif

#ifndef EF_ARM_ABI_FLOAT_SOFT
#define EF_ARM_ABI_FLOAT_SOFT 0x200
#endif

#ifndef EF_ARM_ABI_FLOAT_HARD
#define EF_ARM_ABI_FLOAT_HARD 0x400
#endif

static int failed;
static const char *argv0;
static const char *outfile;

static void fail(const char *fmt, ...)
{
	va_list ap;

	failed = 1;
	fprintf(stderr, "%s: ", argv0);
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	exit(EXIT_FAILURE);
}

static void cleanup(void)
{
	if (failed && outfile != NULL)
		unlink(outfile);
}

static Elf32_Word read_elf_word(Elf32_Word word, bool swap)
{
	return swap ? swab32(word) : word;
}

static Elf32_Half read_elf_half(Elf32_Half half, bool swap)
{
	return swap ? swab16(half) : half;
}

static void write_elf_word(Elf32_Word val, Elf32_Word *dst, bool swap)
{
	*dst = swap ? swab32(val) : val;
}

int main(int argc, char **argv)
{
	const Elf32_Ehdr *inhdr;
	bool clear_soft_float;
	const char *infile;
	Elf32_Word e_flags;
	const void *inbuf;
	struct stat stat;
	void *outbuf;
	bool swap;
	int outfd;
	int infd;

	atexit(cleanup);
	argv0 = argv[0];

	if (argc != 3)
		fail("Usage: %s [infile] [outfile]\n", argv[0]);

	infile = argv[1];
	outfile = argv[2];

	infd = open(infile, O_RDONLY);
	if (infd < 0)
		fail("Cannot open %s: %s\n", infile, strerror(errno));

	if (fstat(infd, &stat) != 0)
		fail("Failed stat for %s: %s\n", infile, strerror(errno));

	inbuf = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, infd, 0);
	if (inbuf == MAP_FAILED)
		fail("Failed to map %s: %s\n", infile, strerror(errno));

	close(infd);

	inhdr = inbuf;

	if (memcmp(&inhdr->e_ident, ELFMAG, SELFMAG) != 0)
		fail("Not an ELF file\n");

	if (inhdr->e_ident[EI_CLASS] != ELFCLASS32)
		fail("Unsupported ELF class\n");

	swap = inhdr->e_ident[EI_DATA] != HOST_ORDER;

	if (read_elf_half(inhdr->e_type, swap) != ET_DYN)
		fail("Not a shared object\n");

	if (read_elf_half(inhdr->e_machine, swap) != EM_ARM)
		fail("Unsupported architecture %#x\n", inhdr->e_machine);

	e_flags = read_elf_word(inhdr->e_flags, swap);

	if (EF_ARM_EABI_VERSION(e_flags) != EF_ARM_EABI_VER5) {
		fail("Unsupported EABI version %#x\n",
		     EF_ARM_EABI_VERSION(e_flags));
	}

	if (e_flags & EF_ARM_ABI_FLOAT_HARD)
		fail("Unexpected hard-float flag set in e_flags\n");

	clear_soft_float = !!(e_flags & EF_ARM_ABI_FLOAT_SOFT);

	outfd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
	if (outfd < 0)
		fail("Cannot open %s: %s\n", outfile, strerror(errno));

	if (ftruncate(outfd, stat.st_size) != 0)
		fail("Cannot truncate %s: %s\n", outfile, strerror(errno));

	outbuf = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
		      outfd, 0);
	if (outbuf == MAP_FAILED)
		fail("Failed to map %s: %s\n", outfile, strerror(errno));

	close(outfd);

	memcpy(outbuf, inbuf, stat.st_size);

	if (clear_soft_float) {
		Elf32_Ehdr *outhdr;

		outhdr = outbuf;
		e_flags &= ~EF_ARM_ABI_FLOAT_SOFT;
		write_elf_word(e_flags, &outhdr->e_flags, swap);
	}

	if (msync(outbuf, stat.st_size, MS_SYNC) != 0)
		fail("Failed to sync %s: %s\n", outfile, strerror(errno));

	return EXIT_SUCCESS;
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * ARM userspace implementations of gettimeofday() and similar.
 *
 * Copyright 2015 Mentor Graphics Corporation.
 */
#include <linux/time.h>
#include <linux/types.h>

int __vdso_clock_gettime(clockid_t clock,
			 struct old_timespec32 *ts)
{
	return __cvdso_clock_gettime32(clock, ts);
}

int __vdso_clock_gettime64(clockid_t clock,
			   struct __kernel_timespec *ts)
{
	return __cvdso_clock_gettime(clock, ts);
}

int __vdso_gettimeofday(struct __kernel_old_timeval *tv,
			struct timezone *tz)
{
	return __cvdso_gettimeofday(tv, tz);
}

int __vdso_clock_getres(clockid_t clock_id,
			struct old_timespec32 *res)
{
	return __cvdso_clock_getres_time32(clock_id, res);
}

/* Avoid unresolved references emitted by GCC */

void __aeabi_unwind_cpp_pr0(void)
{
}

void __aeabi_unwind_cpp_pr1(void)
{
}

void __aeabi_unwind_cpp_pr2(void)
{
}


code:
# SPDX-License-Identifier: GPL-2.0-only
#
# linux/arch/arm/vfp/Makefile
#
# Copyright (C) 2001 ARM Limited
#

# ccflags-y := -DDEBUG
# asflags-y := -DDEBUG

obj-y		+= vfpmodule.o entry.o vfphw.o vfpsingle.o vfpdouble.o

path: arch/arm/vfp/Makefile


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/vfp/entry.S
 *
 *  Copyright (C) 2004 ARM Limited.
 *  Written by Deep Blue Solutions Limited.
 */
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/vfpmacros.h>
#include <asm/assembler.h>
#include <asm/asm-offsets.h>

@ VFP entry point.
@
@  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
@  r2  = PC value to resume execution after successful emulation
@  r9  = normal "successful" return address
@  r10 = this threads thread_info structure
@  lr  = unrecognised instruction return address
@  IRQs enabled.
@
ENTRY(do_vfp)
	inc_preempt_count r10, r4
 	ldr	r4, .LCvfp
	ldr	r11, [r10, #TI_CPU]	@ CPU number
	add	r10, r10, #TI_VFPSTATE	@ r10 = workspace
	ldr	pc, [r4]		@ call VFP entry point
ENDPROC(do_vfp)

ENTRY(vfp_null_entry)
	dec_preempt_count_ti r10, r4
	ret	lr
ENDPROC(vfp_null_entry)

	.align	2
.LCvfp:
	.word	vfp_vector


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/vfp/vfp.h
 *
 *  Copyright (C) 2004 ARM Limited.
 *  Written by Deep Blue Solutions Limited.
 */

static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
{
	if (shift) {
		if (shift < 32)
			val = val >> shift | ((val << (32 - shift)) != 0);
		else
			val = val != 0;
	}
	return val;
}

static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
{
	if (shift) {
		if (shift < 64)
			val = val >> shift | ((val << (64 - shift)) != 0);
		else
			val = val != 0;
	}
	return val;
}

static inline u32 vfp_hi64to32jamming(u64 val)
{
	u32 v;

	asm(
	"cmp	%Q1, #1		@ vfp_hi64to32jamming\n\t"
	"movcc	%0, %R1\n\t"
	"orrcs	%0, %R1, #1"
	: "=r" (v) : "r" (val) : "cc");

	return v;
}

static inline void add128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml)
{
	asm(	"adds	%Q0, %Q2, %Q4\n\t"
		"adcs	%R0, %R2, %R4\n\t"
		"adcs	%Q1, %Q3, %Q5\n\t"
		"adc	%R1, %R3, %R5"
	    : "=r" (nl), "=r" (nh)
	    : "0" (nl), "1" (nh), "r" (ml), "r" (mh)
	    : "cc");
	*resh = nh;
	*resl = nl;
}

static inline void sub128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml)
{
	asm(	"subs	%Q0, %Q2, %Q4\n\t"
		"sbcs	%R0, %R2, %R4\n\t"
		"sbcs	%Q1, %Q3, %Q5\n\t"
		"sbc	%R1, %R3, %R5\n\t"
	    : "=r" (nl), "=r" (nh)
	    : "0" (nl), "1" (nh), "r" (ml), "r" (mh)
	    : "cc");
	*resh = nh;
	*resl = nl;
}

static inline void mul64to128(u64 *resh, u64 *resl, u64 n, u64 m)
{
	u32 nh, nl, mh, ml;
	u64 rh, rma, rmb, rl;

	nl = n;
	ml = m;
	rl = (u64)nl * ml;

	nh = n >> 32;
	rma = (u64)nh * ml;

	mh = m >> 32;
	rmb = (u64)nl * mh;
	rma += rmb;

	rh = (u64)nh * mh;
	rh += ((u64)(rma < rmb) << 32) + (rma >> 32);

	rma <<= 32;
	rl += rma;
	rh += (rl < rma);

	*resl = rl;
	*resh = rh;
}

static inline void shift64left(u64 *resh, u64 *resl, u64 n)
{
	*resh = n >> 63;
	*resl = n << 1;
}

static inline u64 vfp_hi64multiply64(u64 n, u64 m)
{
	u64 rh, rl;
	mul64to128(&rh, &rl, n, m);
	return rh | (rl != 0);
}

static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
{
	u64 mh, ml, remh, reml, termh, terml, z;

	if (nh >= m)
		return ~0ULL;
	mh = m >> 32;
	if (mh << 32 <= nh) {
		z = 0xffffffff00000000ULL;
	} else {
		z = nh;
		do_div(z, mh);
		z <<= 32;
	}
	mul64to128(&termh, &terml, m, z);
	sub128(&remh, &reml, nh, nl, termh, terml);
	ml = m << 32;
	while ((s64)remh < 0) {
		z -= 0x100000000ULL;
		add128(&remh, &reml, remh, reml, mh, ml);
	}
	remh = (remh << 32) | (reml >> 32);
	if (mh << 32 <= remh) {
		z |= 0xffffffff;
	} else {
		do_div(remh, mh);
		z |= remh;
	}
	return z;
}

/*
 * Operations on unpacked elements
 */
#define vfp_sign_negate(sign)	(sign ^ 0x8000)

/*
 * Single-precision
 */
struct vfp_single {
	s16	exponent;
	u16	sign;
	u32	significand;
};

asmlinkage s32 vfp_get_float(unsigned int reg);
asmlinkage void vfp_put_float(s32 val, unsigned int reg);

/*
 * VFP_SINGLE_MANTISSA_BITS - number of bits in the mantissa
 * VFP_SINGLE_EXPONENT_BITS - number of bits in the exponent
 * VFP_SINGLE_LOW_BITS - number of low bits in the unpacked significand
 *  which are not propagated to the float upon packing.
 */
#define VFP_SINGLE_MANTISSA_BITS	(23)
#define VFP_SINGLE_EXPONENT_BITS	(8)
#define VFP_SINGLE_LOW_BITS		(32 - VFP_SINGLE_MANTISSA_BITS - 2)
#define VFP_SINGLE_LOW_BITS_MASK	((1 << VFP_SINGLE_LOW_BITS) - 1)

/*
 * The bit in an unpacked float which indicates that it is a quiet NaN
 */
#define VFP_SINGLE_SIGNIFICAND_QNAN	(1 << (VFP_SINGLE_MANTISSA_BITS - 1 + VFP_SINGLE_LOW_BITS))

/*
 * Operations on packed single-precision numbers
 */
#define vfp_single_packed_sign(v)	((v) & 0x80000000)
#define vfp_single_packed_negate(v)	((v) ^ 0x80000000)
#define vfp_single_packed_abs(v)	((v) & ~0x80000000)
#define vfp_single_packed_exponent(v)	(((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1))
#define vfp_single_packed_mantissa(v)	((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1))

/*
 * Unpack a single-precision float.  Note that this returns the magnitude
 * of the single-precision float mantissa with the 1. if necessary,
 * aligned to bit 30.
 */
static inline void vfp_single_unpack(struct vfp_single *s, s32 val)
{
	u32 significand;

	s->sign = vfp_single_packed_sign(val) >> 16,
	s->exponent = vfp_single_packed_exponent(val);

	significand = (u32) val;
	significand = (significand << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2;
	if (s->exponent && s->exponent != 255)
		significand |= 0x40000000;
	s->significand = significand;
}

/*
 * Re-pack a single-precision float.  This assumes that the float is
 * already normalised such that the MSB is bit 30, _not_ bit 31.
 */
static inline s32 vfp_single_pack(struct vfp_single *s)
{
	u32 val;
	val = (s->sign << 16) +
	      (s->exponent << VFP_SINGLE_MANTISSA_BITS) +
	      (s->significand >> VFP_SINGLE_LOW_BITS);
	return (s32)val;
}

#define VFP_NUMBER		(1<<0)
#define VFP_ZERO		(1<<1)
#define VFP_DENORMAL		(1<<2)
#define VFP_INFINITY		(1<<3)
#define VFP_NAN			(1<<4)
#define VFP_NAN_SIGNAL		(1<<5)

#define VFP_QNAN		(VFP_NAN)
#define VFP_SNAN		(VFP_NAN|VFP_NAN_SIGNAL)

static inline int vfp_single_type(struct vfp_single *s)
{
	int type = VFP_NUMBER;
	if (s->exponent == 255) {
		if (s->significand == 0)
			type = VFP_INFINITY;
		else if (s->significand & VFP_SINGLE_SIGNIFICAND_QNAN)
			type = VFP_QNAN;
		else
			type = VFP_SNAN;
	} else if (s->exponent == 0) {
		if (s->significand == 0)
			type |= VFP_ZERO;
		else
			type |= VFP_DENORMAL;
	}
	return type;
}

#ifndef DEBUG
#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except)
u32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions);
#else
u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func);
#endif

/*
 * Double-precision
 */
struct vfp_double {
	s16	exponent;
	u16	sign;
	u64	significand;
};

/*
 * VFP_REG_ZERO is a special register number for vfp_get_double
 * which returns (double)0.0.  This is useful for the compare with
 * zero instructions.
 */
#ifdef CONFIG_VFPv3
#define VFP_REG_ZERO	32
#else
#define VFP_REG_ZERO	16
#endif
asmlinkage u64 vfp_get_double(unsigned int reg);
asmlinkage void vfp_put_double(u64 val, unsigned int reg);

#define VFP_DOUBLE_MANTISSA_BITS	(52)
#define VFP_DOUBLE_EXPONENT_BITS	(11)
#define VFP_DOUBLE_LOW_BITS		(64 - VFP_DOUBLE_MANTISSA_BITS - 2)
#define VFP_DOUBLE_LOW_BITS_MASK	((1 << VFP_DOUBLE_LOW_BITS) - 1)

/*
 * The bit in an unpacked double which indicates that it is a quiet NaN
 */
#define VFP_DOUBLE_SIGNIFICAND_QNAN	(1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1 + VFP_DOUBLE_LOW_BITS))

/*
 * Operations on packed single-precision numbers
 */
#define vfp_double_packed_sign(v)	((v) & (1ULL << 63))
#define vfp_double_packed_negate(v)	((v) ^ (1ULL << 63))
#define vfp_double_packed_abs(v)	((v) & ~(1ULL << 63))
#define vfp_double_packed_exponent(v)	(((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
#define vfp_double_packed_mantissa(v)	((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))

/*
 * Unpack a double-precision float.  Note that this returns the magnitude
 * of the double-precision float mantissa with the 1. if necessary,
 * aligned to bit 62.
 */
static inline void vfp_double_unpack(struct vfp_double *s, s64 val)
{
	u64 significand;

	s->sign = vfp_double_packed_sign(val) >> 48;
	s->exponent = vfp_double_packed_exponent(val);

	significand = (u64) val;
	significand = (significand << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2;
	if (s->exponent && s->exponent != 2047)
		significand |= (1ULL << 62);
	s->significand = significand;
}

/*
 * Re-pack a double-precision float.  This assumes that the float is
 * already normalised such that the MSB is bit 30, _not_ bit 31.
 */
static inline s64 vfp_double_pack(struct vfp_double *s)
{
	u64 val;
	val = ((u64)s->sign << 48) +
	      ((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
	      (s->significand >> VFP_DOUBLE_LOW_BITS);
	return (s64)val;
}

static inline int vfp_double_type(struct vfp_double *s)
{
	int type = VFP_NUMBER;
	if (s->exponent == 2047) {
		if (s->significand == 0)
			type = VFP_INFINITY;
		else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN)
			type = VFP_QNAN;
		else
			type = VFP_SNAN;
	} else if (s->exponent == 0) {
		if (s->significand == 0)
			type |= VFP_ZERO;
		else
			type |= VFP_DENORMAL;
	}
	return type;
}

u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func);

u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand);

/*
 * A special flag to tell the normalisation code not to normalise.
 */
#define VFP_NAN_FLAG	0x100

/*
 * A bit pattern used to indicate the initial (unset) value of the
 * exception mask, in case nothing handles an instruction.  This
 * doesn't include the NAN flag, which get masked out before
 * we check for an error.
 */
#define VFP_EXCEPTION_ERROR	((u32)-1 & ~VFP_NAN_FLAG)

/*
 * A flag to tell vfp instruction type.
 *  OP_SCALAR - this operation always operates in scalar mode
 *  OP_SD - the instruction exceptionally writes to a single precision result.
 *  OP_DD - the instruction exceptionally writes to a double precision result.
 *  OP_SM - the instruction exceptionally reads from a single precision operand.
 */
#define OP_SCALAR	(1 << 0)
#define OP_SD		(1 << 1)
#define OP_DD		(1 << 1)
#define OP_SM		(1 << 2)

struct op {
	u32 (* const fn)(int dd, int dn, int dm, u32 fpscr);
	u32 flags;
};

asmlinkage void vfp_save_state(void *location, u32 fpexc);


code:
/*
 *  linux/arch/arm/vfp/vfpdouble.c
 *
 * This code is derived in part from John R. Housers softfloat library, which
 * carries the following notice:
 *
 * ===========================================================================
 * This C source file is part of the SoftFloat IEC/IEEE Floating-point
 * Arithmetic Package, Release 2.
 *
 * Written by John R. Hauser.  This work was made possible in part by the
 * International Computer Science Institute, located at Suite 600, 1947 Center
 * Street, Berkeley, California 94704.  Funding was partially provided by the
 * National Science Foundation under grant MIP-9311980.  The original version
 * of this code was written as part of a project to build a fixed-point vector
 * processor in collaboration with the University of California at Berkeley,
 * overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
 * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
 * arithmetic/softfloat.html'.
 *
 * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
 * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
 * TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
 * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
 * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
 *
 * Derivative works are acceptable, even for commercial purposes, so long as
 * (1) they include prominent notice that the work is derivative, and (2) they
 * include prominent notice akin to these three paragraphs for those parts of
 * this code that are retained.
 * ===========================================================================
 */
#include <linux/kernel.h>
#include <linux/bitops.h>

#include <asm/div64.h>
#include <asm/vfp.h>

#include "vfpinstr.h"
#include "vfp.h"

static struct vfp_double vfp_double_default_qnan = {
	.exponent	= 2047,
	.sign		= 0,
	.significand	= VFP_DOUBLE_SIGNIFICAND_QNAN,
};

static void vfp_double_dump(const char *str, struct vfp_double *d)
{
	pr_debug("VFP: %s: sign=%d exponent=%d significand=%016llx\n",
		 str, d->sign != 0, d->exponent, d->significand);
}

static void vfp_double_normalise_denormal(struct vfp_double *vd)
{
	int bits = 31 - fls(vd->significand >> 32);
	if (bits == 31)
		bits = 63 - fls(vd->significand);

	vfp_double_dump("normalise_denormal: in", vd);

	if (bits) {
		vd->exponent -= bits - 1;
		vd->significand <<= bits;
	}

	vfp_double_dump("normalise_denormal: out", vd);
}

u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func)
{
	u64 significand, incr;
	int exponent, shift, underflow;
	u32 rmode;

	vfp_double_dump("pack: in", vd);

	/*
	 * Infinities and NaNs are a special case.
	 */
	if (vd->exponent == 2047 && (vd->significand == 0 || exceptions))
		goto pack;

	/*
	 * Special-case zero.
	 */
	if (vd->significand == 0) {
		vd->exponent = 0;
		goto pack;
	}

	exponent = vd->exponent;
	significand = vd->significand;

	shift = 32 - fls(significand >> 32);
	if (shift == 32)
		shift = 64 - fls(significand);
	if (shift) {
		exponent -= shift;
		significand <<= shift;
	}

#ifdef DEBUG
	vd->exponent = exponent;
	vd->significand = significand;
	vfp_double_dump("pack: normalised", vd);
#endif

	/*
	 * Tiny number?
	 */
	underflow = exponent < 0;
	if (underflow) {
		significand = vfp_shiftright64jamming(significand, -exponent);
		exponent = 0;
#ifdef DEBUG
		vd->exponent = exponent;
		vd->significand = significand;
		vfp_double_dump("pack: tiny number", vd);
#endif
		if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1)))
			underflow = 0;
	}

	/*
	 * Select rounding increment.
	 */
	incr = 0;
	rmode = fpscr & FPSCR_RMODE_MASK;

	if (rmode == FPSCR_ROUND_NEAREST) {
		incr = 1ULL << VFP_DOUBLE_LOW_BITS;
		if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0)
			incr -= 1;
	} else if (rmode == FPSCR_ROUND_TOZERO) {
		incr = 0;
	} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
		incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;

	pr_debug("VFP: rounding increment = 0x%08llx\n", incr);

	/*
	 * Is our rounding going to overflow?
	 */
	if ((significand + incr) < significand) {
		exponent += 1;
		significand = (significand >> 1) | (significand & 1);
		incr >>= 1;
#ifdef DEBUG
		vd->exponent = exponent;
		vd->significand = significand;
		vfp_double_dump("pack: overflow", vd);
#endif
	}

	/*
	 * If any of the low bits (which will be shifted out of the
	 * number) are non-zero, the result is inexact.
	 */
	if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1))
		exceptions |= FPSCR_IXC;

	/*
	 * Do our rounding.
	 */
	significand += incr;

	/*
	 * Infinity?
	 */
	if (exponent >= 2046) {
		exceptions |= FPSCR_OFC | FPSCR_IXC;
		if (incr == 0) {
			vd->exponent = 2045;
			vd->significand = 0x7fffffffffffffffULL;
		} else {
			vd->exponent = 2047;		/* infinity */
			vd->significand = 0;
		}
	} else {
		if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0)
			exponent = 0;
		if (exponent || significand > 0x8000000000000000ULL)
			underflow = 0;
		if (underflow)
			exceptions |= FPSCR_UFC;
		vd->exponent = exponent;
		vd->significand = significand >> 1;
	}

 pack:
	vfp_double_dump("pack: final", vd);
	{
		s64 d = vfp_double_pack(vd);
		pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
			 dd, d, exceptions);
		vfp_put_double(d, dd);
	}
	return exceptions;
}

/*
 * Propagate the NaN, setting exceptions if it is signalling.
 * 'n' is always a NaN.  'm' may be a number, NaN or infinity.
 */
static u32
vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
		  struct vfp_double *vdm, u32 fpscr)
{
	struct vfp_double *nan;
	int tn, tm = 0;

	tn = vfp_double_type(vdn);

	if (vdm)
		tm = vfp_double_type(vdm);

	if (fpscr & FPSCR_DEFAULT_NAN)
		/*
		 * Default NaN mode - always returns a quiet NaN
		 */
		nan = &vfp_double_default_qnan;
	else {
		/*
		 * Contemporary mode - select the first signalling
		 * NAN, or if neither are signalling, the first
		 * quiet NAN.
		 */
		if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
			nan = vdn;
		else
			nan = vdm;
		/*
		 * Make the NaN quiet.
		 */
		nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
	}

	*vdd = *nan;

	/*
	 * If one was a signalling NAN, raise invalid operation.
	 */
	return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
}

/*
 * Extended operations
 */
static u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr)
{
	vfp_put_double(vfp_double_packed_abs(vfp_get_double(dm)), dd);
	return 0;
}

static u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr)
{
	vfp_put_double(vfp_get_double(dm), dd);
	return 0;
}

static u32 vfp_double_fneg(int dd, int unused, int dm, u32 fpscr)
{
	vfp_put_double(vfp_double_packed_negate(vfp_get_double(dm)), dd);
	return 0;
}

static u32 vfp_double_fsqrt(int dd, int unused, int dm, u32 fpscr)
{
	struct vfp_double vdm, vdd;
	int ret, tm;

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	tm = vfp_double_type(&vdm);
	if (tm & (VFP_NAN|VFP_INFINITY)) {
		struct vfp_double *vdp = &vdd;

		if (tm & VFP_NAN)
			ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr);
		else if (vdm.sign == 0) {
 sqrt_copy:
			vdp = &vdm;
			ret = 0;
		} else {
 sqrt_invalid:
			vdp = &vfp_double_default_qnan;
			ret = FPSCR_IOC;
		}
		vfp_put_double(vfp_double_pack(vdp), dd);
		return ret;
	}

	/*
	 * sqrt(+/- 0) == +/- 0
	 */
	if (tm & VFP_ZERO)
		goto sqrt_copy;

	/*
	 * Normalise a denormalised number
	 */
	if (tm & VFP_DENORMAL)
		vfp_double_normalise_denormal(&vdm);

	/*
	 * sqrt(<0) = invalid
	 */
	if (vdm.sign)
		goto sqrt_invalid;

	vfp_double_dump("sqrt", &vdm);

	/*
	 * Estimate the square root.
	 */
	vdd.sign = 0;
	vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023;
	vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31;

	vfp_double_dump("sqrt estimate1", &vdd);

	vdm.significand >>= 1 + (vdm.exponent & 1);
	vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand);

	vfp_double_dump("sqrt estimate2", &vdd);

	/*
	 * And now adjust.
	 */
	if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) {
		if (vdd.significand < 2) {
			vdd.significand = ~0ULL;
		} else {
			u64 termh, terml, remh, reml;
			vdm.significand <<= 2;
			mul64to128(&termh, &terml, vdd.significand, vdd.significand);
			sub128(&remh, &reml, vdm.significand, 0, termh, terml);
			while ((s64)remh < 0) {
				vdd.significand -= 1;
				shift64left(&termh, &terml, vdd.significand);
				terml |= 1;
				add128(&remh, &reml, remh, reml, termh, terml);
			}
			vdd.significand |= (remh | reml) != 0;
		}
	}
	vdd.significand = vfp_shiftright64jamming(vdd.significand, 1);

	return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fsqrt");
}

/*
 * Equal	:= ZC
 * Less than	:= N
 * Greater than	:= C
 * Unordered	:= CV
 */
static u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr)
{
	s64 d, m;
	u32 ret = 0;

	m = vfp_get_double(dm);
	if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
		ret |= FPSCR_C | FPSCR_V;
		if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
			/*
			 * Signalling NaN, or signalling on quiet NaN
			 */
			ret |= FPSCR_IOC;
	}

	d = vfp_get_double(dd);
	if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) {
		ret |= FPSCR_C | FPSCR_V;
		if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
			/*
			 * Signalling NaN, or signalling on quiet NaN
			 */
			ret |= FPSCR_IOC;
	}

	if (ret == 0) {
		if (d == m || vfp_double_packed_abs(d | m) == 0) {
			/*
			 * equal
			 */
			ret |= FPSCR_Z | FPSCR_C;
		} else if (vfp_double_packed_sign(d ^ m)) {
			/*
			 * different signs
			 */
			if (vfp_double_packed_sign(d))
				/*
				 * d is negative, so d < m
				 */
				ret |= FPSCR_N;
			else
				/*
				 * d is positive, so d > m
				 */
				ret |= FPSCR_C;
		} else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) {
			/*
			 * d < m
			 */
			ret |= FPSCR_N;
		} else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) {
			/*
			 * d > m
			 */
			ret |= FPSCR_C;
		}
	}

	return ret;
}

static u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr)
{
	return vfp_compare(dd, 0, dm, fpscr);
}

static u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr)
{
	return vfp_compare(dd, 1, dm, fpscr);
}

static u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr)
{
	return vfp_compare(dd, 0, VFP_REG_ZERO, fpscr);
}

static u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr)
{
	return vfp_compare(dd, 1, VFP_REG_ZERO, fpscr);
}

static u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr)
{
	struct vfp_double vdm;
	struct vfp_single vsd;
	int tm;
	u32 exceptions = 0;

	vfp_double_unpack(&vdm, vfp_get_double(dm));

	tm = vfp_double_type(&vdm);

	/*
	 * If we have a signalling NaN, signal invalid operation.
	 */
	if (tm == VFP_SNAN)
		exceptions = FPSCR_IOC;

	if (tm & VFP_DENORMAL)
		vfp_double_normalise_denormal(&vdm);

	vsd.sign = vdm.sign;
	vsd.significand = vfp_hi64to32jamming(vdm.significand);

	/*
	 * If we have an infinity or a NaN, the exponent must be 255
	 */
	if (tm & (VFP_INFINITY|VFP_NAN)) {
		vsd.exponent = 255;
		if (tm == VFP_QNAN)
			vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
		goto pack_nan;
	} else if (tm & VFP_ZERO)
		vsd.exponent = 0;
	else
		vsd.exponent = vdm.exponent - (1023 - 127);

	return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fcvts");

 pack_nan:
	vfp_put_float(vfp_single_pack(&vsd), sd);
	return exceptions;
}

static u32 vfp_double_fuito(int dd, int unused, int dm, u32 fpscr)
{
	struct vfp_double vdm;
	u32 m = vfp_get_float(dm);

	vdm.sign = 0;
	vdm.exponent = 1023 + 63 - 1;
	vdm.significand = (u64)m;

	return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fuito");
}

static u32 vfp_double_fsito(int dd, int unused, int dm, u32 fpscr)
{
	struct vfp_double vdm;
	u32 m = vfp_get_float(dm);

	vdm.sign = (m & 0x80000000) >> 16;
	vdm.exponent = 1023 + 63 - 1;
	vdm.significand = vdm.sign ? -m : m;

	return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fsito");
}

static u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr)
{
	struct vfp_double vdm;
	u32 d, exceptions = 0;
	int rmode = fpscr & FPSCR_RMODE_MASK;
	int tm;

	vfp_double_unpack(&vdm, vfp_get_double(dm));

	/*
	 * Do we have a denormalised number?
	 */
	tm = vfp_double_type(&vdm);
	if (tm & VFP_DENORMAL)
		exceptions |= FPSCR_IDC;

	if (tm & VFP_NAN)
		vdm.sign = 0;

	if (vdm.exponent >= 1023 + 32) {
		d = vdm.sign ? 0 : 0xffffffff;
		exceptions = FPSCR_IOC;
	} else if (vdm.exponent >= 1023 - 1) {
		int shift = 1023 + 63 - vdm.exponent;
		u64 rem, incr = 0;

		/*
		 * 2^0 <= m < 2^32-2^8
		 */
		d = (vdm.significand << 1) >> shift;
		rem = vdm.significand << (65 - shift);

		if (rmode == FPSCR_ROUND_NEAREST) {
			incr = 0x8000000000000000ULL;
			if ((d & 1) == 0)
				incr -= 1;
		} else if (rmode == FPSCR_ROUND_TOZERO) {
			incr = 0;
		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
			incr = ~0ULL;
		}

		if ((rem + incr) < rem) {
			if (d < 0xffffffff)
				d += 1;
			else
				exceptions |= FPSCR_IOC;
		}

		if (d && vdm.sign) {
			d = 0;
			exceptions |= FPSCR_IOC;
		} else if (rem)
			exceptions |= FPSCR_IXC;
	} else {
		d = 0;
		if (vdm.exponent | vdm.significand) {
			exceptions |= FPSCR_IXC;
			if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
				d = 1;
			else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
				d = 0;
				exceptions |= FPSCR_IOC;
			}
		}
	}

	pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);

	vfp_put_float(d, sd);

	return exceptions;
}

static u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr)
{
	return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO);
}

static u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr)
{
	struct vfp_double vdm;
	u32 d, exceptions = 0;
	int rmode = fpscr & FPSCR_RMODE_MASK;
	int tm;

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	vfp_double_dump("VDM", &vdm);

	/*
	 * Do we have denormalised number?
	 */
	tm = vfp_double_type(&vdm);
	if (tm & VFP_DENORMAL)
		exceptions |= FPSCR_IDC;

	if (tm & VFP_NAN) {
		d = 0;
		exceptions |= FPSCR_IOC;
	} else if (vdm.exponent >= 1023 + 32) {
		d = 0x7fffffff;
		if (vdm.sign)
			d = ~d;
		exceptions |= FPSCR_IOC;
	} else if (vdm.exponent >= 1023 - 1) {
		int shift = 1023 + 63 - vdm.exponent;	/* 58 */
		u64 rem, incr = 0;

		d = (vdm.significand << 1) >> shift;
		rem = vdm.significand << (65 - shift);

		if (rmode == FPSCR_ROUND_NEAREST) {
			incr = 0x8000000000000000ULL;
			if ((d & 1) == 0)
				incr -= 1;
		} else if (rmode == FPSCR_ROUND_TOZERO) {
			incr = 0;
		} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
			incr = ~0ULL;
		}

		if ((rem + incr) < rem && d < 0xffffffff)
			d += 1;
		if (d > 0x7fffffff + (vdm.sign != 0)) {
			d = 0x7fffffff + (vdm.sign != 0);
			exceptions |= FPSCR_IOC;
		} else if (rem)
			exceptions |= FPSCR_IXC;

		if (vdm.sign)
			d = -d;
	} else {
		d = 0;
		if (vdm.exponent | vdm.significand) {
			exceptions |= FPSCR_IXC;
			if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
				d = 1;
			else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
				d = -1;
		}
	}

	pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);

	vfp_put_float((s32)d, sd);

	return exceptions;
}

static u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr)
{
	return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO);
}

static struct op fops_ext[32] = {
	[FEXT_TO_IDX(FEXT_FCPY)]	= { vfp_double_fcpy,   0 },
	[FEXT_TO_IDX(FEXT_FABS)]	= { vfp_double_fabs,   0 },
	[FEXT_TO_IDX(FEXT_FNEG)]	= { vfp_double_fneg,   0 },
	[FEXT_TO_IDX(FEXT_FSQRT)]	= { vfp_double_fsqrt,  0 },
	[FEXT_TO_IDX(FEXT_FCMP)]	= { vfp_double_fcmp,   OP_SCALAR },
	[FEXT_TO_IDX(FEXT_FCMPE)]	= { vfp_double_fcmpe,  OP_SCALAR },
	[FEXT_TO_IDX(FEXT_FCMPZ)]	= { vfp_double_fcmpz,  OP_SCALAR },
	[FEXT_TO_IDX(FEXT_FCMPEZ)]	= { vfp_double_fcmpez, OP_SCALAR },
	[FEXT_TO_IDX(FEXT_FCVT)]	= { vfp_double_fcvts,  OP_SCALAR|OP_SD },
	[FEXT_TO_IDX(FEXT_FUITO)]	= { vfp_double_fuito,  OP_SCALAR|OP_SM },
	[FEXT_TO_IDX(FEXT_FSITO)]	= { vfp_double_fsito,  OP_SCALAR|OP_SM },
	[FEXT_TO_IDX(FEXT_FTOUI)]	= { vfp_double_ftoui,  OP_SCALAR|OP_SD },
	[FEXT_TO_IDX(FEXT_FTOUIZ)]	= { vfp_double_ftouiz, OP_SCALAR|OP_SD },
	[FEXT_TO_IDX(FEXT_FTOSI)]	= { vfp_double_ftosi,  OP_SCALAR|OP_SD },
	[FEXT_TO_IDX(FEXT_FTOSIZ)]	= { vfp_double_ftosiz, OP_SCALAR|OP_SD },
};

static u32
vfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn,
			  struct vfp_double *vdm, u32 fpscr)
{
	struct vfp_double *vdp;
	u32 exceptions = 0;
	int tn, tm;

	tn = vfp_double_type(vdn);
	tm = vfp_double_type(vdm);

	if (tn & tm & VFP_INFINITY) {
		/*
		 * Two infinities.  Are they different signs?
		 */
		if (vdn->sign ^ vdm->sign) {
			/*
			 * different signs -> invalid
			 */
			exceptions = FPSCR_IOC;
			vdp = &vfp_double_default_qnan;
		} else {
			/*
			 * same signs -> valid
			 */
			vdp = vdn;
		}
	} else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
		/*
		 * One infinity and one number -> infinity
		 */
		vdp = vdn;
	} else {
		/*
		 * 'n' is a NaN of some type
		 */
		return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
	}
	*vdd = *vdp;
	return exceptions;
}

static u32
vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,
	       struct vfp_double *vdm, u32 fpscr)
{
	u32 exp_diff;
	u64 m_sig;

	if (vdn->significand & (1ULL << 63) ||
	    vdm->significand & (1ULL << 63)) {
		pr_info("VFP: bad FP values in %s\n", __func__);
		vfp_double_dump("VDN", vdn);
		vfp_double_dump("VDM", vdm);
	}

	/*
	 * Ensure that 'n' is the largest magnitude number.  Note that
	 * if 'n' and 'm' have equal exponents, we do not swap them.
	 * This ensures that NaN propagation works correctly.
	 */
	if (vdn->exponent < vdm->exponent) {
		struct vfp_double *t = vdn;
		vdn = vdm;
		vdm = t;
	}

	/*
	 * Is 'n' an infinity or a NaN?  Note that 'm' may be a number,
	 * infinity or a NaN here.
	 */
	if (vdn->exponent == 2047)
		return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr);

	/*
	 * We have two proper numbers, where 'vdn' is the larger magnitude.
	 *
	 * Copy 'n' to 'd' before doing the arithmetic.
	 */
	*vdd = *vdn;

	/*
	 * Align 'm' with the result.
	 */
	exp_diff = vdn->exponent - vdm->exponent;
	m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff);

	/*
	 * If the signs are different, we are really subtracting.
	 */
	if (vdn->sign ^ vdm->sign) {
		m_sig = vdn->significand - m_sig;
		if ((s64)m_sig < 0) {
			vdd->sign = vfp_sign_negate(vdd->sign);
			m_sig = -m_sig;
		} else if (m_sig == 0) {
			vdd->sign = (fpscr & FPSCR_RMODE_MASK) ==
				      FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
		}
	} else {
		m_sig += vdn->significand;
	}
	vdd->significand = m_sig;

	return 0;
}

static u32
vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
		    struct vfp_double *vdm, u32 fpscr)
{
	vfp_double_dump("VDN", vdn);
	vfp_double_dump("VDM", vdm);

	/*
	 * Ensure that 'n' is the largest magnitude number.  Note that
	 * if 'n' and 'm' have equal exponents, we do not swap them.
	 * This ensures that NaN propagation works correctly.
	 */
	if (vdn->exponent < vdm->exponent) {
		struct vfp_double *t = vdn;
		vdn = vdm;
		vdm = t;
		pr_debug("VFP: swapping M <-> N\n");
	}

	vdd->sign = vdn->sign ^ vdm->sign;

	/*
	 * If 'n' is an infinity or NaN, handle it.  'm' may be anything.
	 */
	if (vdn->exponent == 2047) {
		if (vdn->significand || (vdm->exponent == 2047 && vdm->significand))
			return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
		if ((vdm->exponent | vdm->significand) == 0) {
			*vdd = vfp_double_default_qnan;
			return FPSCR_IOC;
		}
		vdd->exponent = vdn->exponent;
		vdd->significand = 0;
		return 0;
	}

	/*
	 * If 'm' is zero, the result is always zero.  In this case,
	 * 'n' may be zero or a number, but it doesn't matter which.
	 */
	if ((vdm->exponent | vdm->significand) == 0) {
		vdd->exponent = 0;
		vdd->significand = 0;
		return 0;
	}

	/*
	 * We add 2 to the destination exponent for the same reason
	 * as the addition case - though this time we have +1 from
	 * each input operand.
	 */
	vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2;
	vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand);

	vfp_double_dump("VDD", vdd);
	return 0;
}

#define NEG_MULTIPLY	(1 << 0)
#define NEG_SUBTRACT	(1 << 1)

static u32
vfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func)
{
	struct vfp_double vdd, vdp, vdn, vdm;
	u32 exceptions;

	vfp_double_unpack(&vdn, vfp_get_double(dn));
	if (vdn.exponent == 0 && vdn.significand)
		vfp_double_normalise_denormal(&vdn);

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	if (vdm.exponent == 0 && vdm.significand)
		vfp_double_normalise_denormal(&vdm);

	exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
	if (negate & NEG_MULTIPLY)
		vdp.sign = vfp_sign_negate(vdp.sign);

	vfp_double_unpack(&vdn, vfp_get_double(dd));
	if (vdn.exponent == 0 && vdn.significand)
		vfp_double_normalise_denormal(&vdn);
	if (negate & NEG_SUBTRACT)
		vdn.sign = vfp_sign_negate(vdn.sign);

	exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);

	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func);
}

/*
 * Standard operations
 */

/*
 * sd = sd + (sn * sm)
 */
static u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr)
{
	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac");
}

/*
 * sd = sd - (sn * sm)
 */
static u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr)
{
	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
}

/*
 * sd = -sd + (sn * sm)
 */
static u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr)
{
	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
}

/*
 * sd = -sd - (sn * sm)
 */
static u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr)
{
	return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
}

/*
 * sd = sn * sm
 */
static u32 vfp_double_fmul(int dd, int dn, int dm, u32 fpscr)
{
	struct vfp_double vdd, vdn, vdm;
	u32 exceptions;

	vfp_double_unpack(&vdn, vfp_get_double(dn));
	if (vdn.exponent == 0 && vdn.significand)
		vfp_double_normalise_denormal(&vdn);

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	if (vdm.exponent == 0 && vdm.significand)
		vfp_double_normalise_denormal(&vdm);

	exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fmul");
}

/*
 * sd = -(sn * sm)
 */
static u32 vfp_double_fnmul(int dd, int dn, int dm, u32 fpscr)
{
	struct vfp_double vdd, vdn, vdm;
	u32 exceptions;

	vfp_double_unpack(&vdn, vfp_get_double(dn));
	if (vdn.exponent == 0 && vdn.significand)
		vfp_double_normalise_denormal(&vdn);

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	if (vdm.exponent == 0 && vdm.significand)
		vfp_double_normalise_denormal(&vdm);

	exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
	vdd.sign = vfp_sign_negate(vdd.sign);

	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fnmul");
}

/*
 * sd = sn + sm
 */
static u32 vfp_double_fadd(int dd, int dn, int dm, u32 fpscr)
{
	struct vfp_double vdd, vdn, vdm;
	u32 exceptions;

	vfp_double_unpack(&vdn, vfp_get_double(dn));
	if (vdn.exponent == 0 && vdn.significand)
		vfp_double_normalise_denormal(&vdn);

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	if (vdm.exponent == 0 && vdm.significand)
		vfp_double_normalise_denormal(&vdm);

	exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);

	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fadd");
}

/*
 * sd = sn - sm
 */
static u32 vfp_double_fsub(int dd, int dn, int dm, u32 fpscr)
{
	struct vfp_double vdd, vdn, vdm;
	u32 exceptions;

	vfp_double_unpack(&vdn, vfp_get_double(dn));
	if (vdn.exponent == 0 && vdn.significand)
		vfp_double_normalise_denormal(&vdn);

	vfp_double_unpack(&vdm, vfp_get_double(dm));
	if (vdm.exponent == 0 && vdm.significand)
		vfp_double_normalise_denormal(&vdm);

	/*
	 * Subtraction is like addition, but with a negated operand.
	 */
	vdm.sign = vfp_sign_negate(vdm.sign);

	exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);

	return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fsub");
}

/*
 * sd = sn / sm
 */
static u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr)
{
	struct vfp_double vdd, vdn, vdm;
	u32 exceptions = 0;
	int tm, tn;

	vfp_double_unpack(&vdn, vfp_get_double(dn));
	vfp_double_unpack(&vdm, vfp_get_double(dm));

	vdd.sign = vdn.sign ^ vdm.sign;

	tn = vfp_double_type(&vdn);
	tm = vfp_double_type(&vdm);

	/*
	 * Is n a NAN?
	 */
	if (tn & VFP_NAN)
		goto vdn_nan;

	/*
	 * Is m a NAN?
	 */
	if (tm & VFP_NAN)
		goto vdm_nan;

	/*
	 * If n and m are infinity, the result is invalid
	 * If n and m are zero, the result is invalid
	 */
	if (tm & tn & (VFP_INFINITY|VFP_ZERO))
		goto invalid;

	/*
	 * If n is infinity, the result is infinity
	 */
	if (tn & VFP_INFINITY)
		goto infinity;

	/*
	 * If m is zero, raise div0 exceptions
	 */
	if (tm & VFP_ZERO)
		goto divzero;

	/*
	 * If m is infinity, or n is zero, the result is zero
	 */
	if (tm & VFP_INFINITY || tn & VFP_ZERO)
		goto zero;

	if (tn & VFP_DENORMAL)
		vfp_double_normalise_denormal(&vdn);
	if (tm & VFP_DENORMAL)
		vfp_double_normalise_denormal(&vdm);

	/*
	 * Ok, we have two numbers, we can perform division.
	 */
	vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1;
	vdm.significand <<= 1;
	if (vdm.significand <= (2 * vdn.significand)) {
		vdn.significand >>= 1;
		vdd.exponent++;
	}
	vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand);
	if ((vdd.significand & 0x1ff) <= 2) {
		u64 termh, terml, remh, reml;
		mul64to128(&termh, &terml, vdm.significand, vdd.significand);
		sub128(&remh, &reml, vdn.significand, 0, termh, terml);
		while ((s64)remh < 0) {
			vdd.significand -= 1;
			add128(&remh, &reml, remh, reml, 0, vdm.significand);
		}
		vdd.significand |= (reml != 0);
	}
	return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv");

 vdn_nan:
	exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
 pack:
	vfp_put_double(vfp_double_pack(&vdd), dd);
	return exceptions;

 vdm_nan:
	exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
	goto pack;

 zero:
	vdd.exponent = 0;
	vdd.significand = 0;
	goto pack;

 divzero:
	exceptions = FPSCR_DZC;
 infinity:
	vdd.exponent = 2047;
	vdd.significand = 0;
	goto pack;

 invalid:
	vfp_put_double(vfp_double_pack(&vfp_double_default_qnan), dd);
	return FPSCR_IOC;
}

static struct op fops[16] = {
	[FOP_TO_IDX(FOP_FMAC)]	= { vfp_double_fmac,  0 },
	[FOP_TO_IDX(FOP_FNMAC)]	= { vfp_double_fnmac, 0 },
	[FOP_TO_IDX(FOP_FMSC)]	= { vfp_double_fmsc,  0 },
	[FOP_TO_IDX(FOP_FNMSC)]	= { vfp_double_fnmsc, 0 },
	[FOP_TO_IDX(FOP_FMUL)]	= { vfp_double_fmul,  0 },
	[FOP_TO_IDX(FOP_FNMUL)]	= { vfp_double_fnmul, 0 },
	[FOP_TO_IDX(FOP_FADD)]	= { vfp_double_fadd,  0 },
	[FOP_TO_IDX(FOP_FSUB)]	= { vfp_double_fsub,  0 },
	[FOP_TO_IDX(FOP_FDIV)]	= { vfp_double_fdiv,  0 },
};

#define FREG_BANK(x)	((x) & 0x0c)
#define FREG_IDX(x)	((x) & 3)

u32 vfp_double_cpdo(u32 inst, u32 fpscr)
{
	u32 op = inst & FOP_MASK;
	u32 exceptions = 0;
	unsigned int dest;
	unsigned int dn = vfp_get_dn(inst);
	unsigned int dm;
	unsigned int vecitr, veclen, vecstride;
	struct op *fop;

	vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK));

	fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];

	/*
	 * fcvtds takes an sN register number as destination, not dN.
	 * It also always operates on scalars.
	 */
	if (fop->flags & OP_SD)
		dest = vfp_get_sd(inst);
	else
		dest = vfp_get_dd(inst);

	/*
	 * f[us]ito takes a sN operand, not a dN operand.
	 */
	if (fop->flags & OP_SM)
		dm = vfp_get_sm(inst);
	else
		dm = vfp_get_dm(inst);

	/*
	 * If destination bank is zero, vector length is always '1'.
	 * ARM DDI0100F C5.1.3, C5.3.2.
	 */
	if ((fop->flags & OP_SCALAR) || (FREG_BANK(dest) == 0))
		veclen = 0;
	else
		veclen = fpscr & FPSCR_LENGTH_MASK;

	pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
		 (veclen >> FPSCR_LENGTH_BIT) + 1);

	if (!fop->fn)
		goto invalid;

	for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
		u32 except;
		char type;

		type = fop->flags & OP_SD ? 's' : 'd';
		if (op == FOP_EXT)
			pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n",
				 vecitr >> FPSCR_LENGTH_BIT,
				 type, dest, dn, dm);
		else
			pr_debug("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n",
				 vecitr >> FPSCR_LENGTH_BIT,
				 type, dest, dn, FOP_TO_IDX(op), dm);

		except = fop->fn(dest, dn, dm, fpscr);
		pr_debug("VFP: itr%d: exceptions=%08x\n",
			 vecitr >> FPSCR_LENGTH_BIT, except);

		exceptions |= except;

		/*
		 * CHECK: It appears to be undefined whether we stop when
		 * we encounter an exception.  We continue.
		 */
		dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 3);
		dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3);
		if (FREG_BANK(dm) != 0)
			dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3);
	}
	return exceptions;

 invalid:
	return ~0;
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/vfp/vfphw.S
 *
 *  Copyright (C) 2004 ARM Limited.
 *  Written by Deep Blue Solutions Limited.
 *
 * This code is called from the kernel's undefined instruction trap.
 * r9 holds the return address for successful handling.
 * lr holds the return address for unrecognised instructions.
 * r10 points at the start of the private FP workspace in the thread structure
 * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h)
 */
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/vfpmacros.h>
#include <linux/kern_levels.h>
#include <asm/assembler.h>
#include <asm/asm-offsets.h>

	.macro	DBGSTR, str
#ifdef DEBUG
	stmfd	sp!, {r0-r3, ip, lr}
	ldr	r0, =1f
	bl	_printk
	ldmfd	sp!, {r0-r3, ip, lr}

	.pushsection .rodata, "a"
1:	.ascii	KERN_DEBUG "VFP: \str\n"
	.byte	0
	.previous
#endif
	.endm

	.macro  DBGSTR1, str, arg
#ifdef DEBUG
	stmfd	sp!, {r0-r3, ip, lr}
	mov	r1, \arg
	ldr	r0, =1f
	bl	_printk
	ldmfd	sp!, {r0-r3, ip, lr}

	.pushsection .rodata, "a"
1:	.ascii	KERN_DEBUG "VFP: \str\n"
	.byte	0
	.previous
#endif
	.endm

	.macro  DBGSTR3, str, arg1, arg2, arg3
#ifdef DEBUG
	stmfd	sp!, {r0-r3, ip, lr}
	mov	r3, \arg3
	mov	r2, \arg2
	mov	r1, \arg1
	ldr	r0, =1f
	bl	_printk
	ldmfd	sp!, {r0-r3, ip, lr}

	.pushsection .rodata, "a"
1:	.ascii	KERN_DEBUG "VFP: \str\n"
	.byte	0
	.previous
#endif
	.endm

@ VFP hardware support entry point.
@
@  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
@  r2  = PC value to resume execution after successful emulation
@  r9  = normal "successful" return address
@  r10 = vfp_state union
@  r11 = CPU number
@  lr  = unrecognised instruction return address
@  IRQs enabled.
ENTRY(vfp_support_entry)
	DBGSTR3	"instr %08x pc %08x state %p", r0, r2, r10

	.fpu	vfpv2
	VFPFMRX	r1, FPEXC		@ Is the VFP enabled?
	DBGSTR1	"fpexc %08x", r1
	tst	r1, #FPEXC_EN
	bne	look_for_VFP_exceptions	@ VFP is already enabled

	DBGSTR1 "enable %x", r10
	ldr	r3, vfp_current_hw_state_address
	orr	r1, r1, #FPEXC_EN	@ user FPEXC has the enable bit set
	ldr	r4, [r3, r11, lsl #2]	@ vfp_current_hw_state pointer
	bic	r5, r1, #FPEXC_EX	@ make sure exceptions are disabled
	cmp	r4, r10			@ this thread owns the hw context?
#ifndef CONFIG_SMP
	@ For UP, checking that this thread owns the hw context is
	@ sufficient to determine that the hardware state is valid.
	beq	vfp_hw_state_valid

	@ On UP, we lazily save the VFP context.  As a different
	@ thread wants ownership of the VFP hardware, save the old
	@ state if there was a previous (valid) owner.

	VFPFMXR	FPEXC, r5		@ enable VFP, disable any pending
					@ exceptions, so we can get at the
					@ rest of it

	DBGSTR1	"save old state %p", r4
	cmp	r4, #0			@ if the vfp_current_hw_state is NULL
	beq	vfp_reload_hw		@ then the hw state needs reloading
	VFPFSTMIA r4, r5		@ save the working registers
	VFPFMRX	r5, FPSCR		@ current status
#ifndef CONFIG_CPU_FEROCEON
	tst	r1, #FPEXC_EX		@ is there additional state to save?
	beq	1f
	VFPFMRX	r6, FPINST		@ FPINST (only if FPEXC.EX is set)
	tst	r1, #FPEXC_FP2V		@ is there an FPINST2 to read?
	beq	1f
	VFPFMRX	r8, FPINST2		@ FPINST2 if needed (and present)
1:
#endif
	stmia	r4, {r1, r5, r6, r8}	@ save FPEXC, FPSCR, FPINST, FPINST2
vfp_reload_hw:

#else
	@ For SMP, if this thread does not own the hw context, then we
	@ need to reload it.  No need to save the old state as on SMP,
	@ we always save the state when we switch away from a thread.
	bne	vfp_reload_hw

	@ This thread has ownership of the current hardware context.
	@ However, it may have been migrated to another CPU, in which
	@ case the saved state is newer than the hardware context.
	@ Check this by looking at the CPU number which the state was
	@ last loaded onto.
	ldr	ip, [r10, #VFP_CPU]
	teq	ip, r11
	beq	vfp_hw_state_valid

vfp_reload_hw:
	@ We're loading this threads state into the VFP hardware. Update
	@ the CPU number which contains the most up to date VFP context.
	str	r11, [r10, #VFP_CPU]

	VFPFMXR	FPEXC, r5		@ enable VFP, disable any pending
					@ exceptions, so we can get at the
					@ rest of it
#endif

	DBGSTR1	"load state %p", r10
	str	r10, [r3, r11, lsl #2]	@ update the vfp_current_hw_state pointer
					@ Load the saved state back into the VFP
	VFPFLDMIA r10, r5		@ reload the working registers while
					@ FPEXC is in a safe state
	ldmia	r10, {r1, r5, r6, r8}	@ load FPEXC, FPSCR, FPINST, FPINST2
#ifndef CONFIG_CPU_FEROCEON
	tst	r1, #FPEXC_EX		@ is there additional state to restore?
	beq	1f
	VFPFMXR	FPINST, r6		@ restore FPINST (only if FPEXC.EX is set)
	tst	r1, #FPEXC_FP2V		@ is there an FPINST2 to write?
	beq	1f
	VFPFMXR	FPINST2, r8		@ FPINST2 if needed (and present)
1:
#endif
	VFPFMXR	FPSCR, r5		@ restore status

@ The context stored in the VFP hardware is up to date with this thread
vfp_hw_state_valid:
	tst	r1, #FPEXC_EX
	bne	process_exception	@ might as well handle the pending
					@ exception before retrying branch
					@ out before setting an FPEXC that
					@ stops us reading stuff
	VFPFMXR	FPEXC, r1		@ Restore FPEXC last
	sub	r2, r2, #4		@ Retry current instruction - if Thumb
	str	r2, [sp, #S_PC]		@ mode it's two 16-bit instructions,
					@ else it's one 32-bit instruction, so
					@ always subtract 4 from the following
					@ instruction address.
	dec_preempt_count_ti r10, r4
	ret	r9			@ we think we have handled things

look_for_VFP_exceptions:
	@ Check for synchronous or asynchronous exception
	tst	r1, #FPEXC_EX | FPEXC_DEX
	bne	process_exception
	@ On some implementations of the VFP subarch 1, setting FPSCR.IXE
	@ causes all the CDP instructions to be bounced synchronously without
	@ setting the FPEXC.EX bit
	VFPFMRX	r5, FPSCR
	tst	r5, #FPSCR_IXE
	bne	process_exception

	tst	r5, #FPSCR_LENGTH_MASK
	beq	skip
	orr	r1, r1, #FPEXC_DEX
	b	process_exception
skip:

	@ Fall into hand on to next handler - appropriate coproc instr
	@ not recognised by VFP

	DBGSTR	"not VFP"
	dec_preempt_count_ti r10, r4
	ret	lr

process_exception:
	DBGSTR	"bounce"
	mov	r2, sp			@ nothing stacked - regdump is at TOS
	mov	lr, r9			@ setup for a return to the user code.

	@ Now call the C code to package up the bounce to the support code
	@   r0 holds the trigger instruction
	@   r1 holds the FPEXC value
	@   r2 pointer to register dump
	b	VFP_bounce		@ we have handled this - the support
					@ code will raise an exception if
					@ required. If not, the user code will
					@ retry the faulted instruction
ENDPROC(vfp_support_entry)

ENTRY(vfp_save_state)
	@ Save the current VFP state
	@ r0 - save location
	@ r1 - FPEXC
	DBGSTR1	"save VFP state %p", r0
	VFPFSTMIA r0, r2		@ save the working registers
	VFPFMRX	r2, FPSCR		@ current status
	tst	r1, #FPEXC_EX		@ is there additional state to save?
	beq	1f
	VFPFMRX	r3, FPINST		@ FPINST (only if FPEXC.EX is set)
	tst	r1, #FPEXC_FP2V		@ is there an FPINST2 to read?
	beq	1f
	VFPFMRX	r12, FPINST2		@ FPINST2 if needed (and present)
1:
	stmia	r0, {r1, r2, r3, r12}	@ save FPEXC, FPSCR, FPINST, FPINST2
	ret	lr
ENDPROC(vfp_save_state)

	.align
vfp_current_hw_state_address:
	.word	vfp_current_hw_state

	.macro	tbl_branch, base, tmp, shift
#ifdef CONFIG_THUMB2_KERNEL
	adr	\tmp, 1f
	add	\tmp, \tmp, \base, lsl \shift
	ret	\tmp
#else
	add	pc, pc, \base, lsl \shift
	mov	r0, r0
#endif
1:
	.endm

ENTRY(vfp_get_float)
	tbl_branch r0, r3, #3
	.fpu	vfpv2
	.irp	dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:	vmov	r0, s\dr
	ret	lr
	.org	1b + 8
	.endr
	.irp	dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:	vmov	r0, s\dr
	ret	lr
	.org	1b + 8
	.endr
ENDPROC(vfp_get_float)

ENTRY(vfp_put_float)
	tbl_branch r1, r3, #3
	.fpu	vfpv2
	.irp	dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:	vmov	s\dr, r0
	ret	lr
	.org	1b + 8
	.endr
	.irp	dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:	vmov	s\dr, r0
	ret	lr
	.org	1b + 8
	.endr
ENDPROC(vfp_put_float)

ENTRY(vfp_get_double)
	tbl_branch r0, r3, #3
	.fpu	vfpv2
	.irp	dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:	vmov	r0, r1, d\dr
	ret	lr
	.org	1b + 8
	.endr
#ifdef CONFIG_VFPv3
	@ d16 - d31 registers
	.fpu	vfpv3
	.irp	dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:	vmov	r0, r1, d\dr
	ret	lr
	.org	1b + 8
	.endr
#endif

	@ virtual register 16 (or 32 if VFPv3) for compare with zero
	mov	r0, #0
	mov	r1, #0
	ret	lr
ENDPROC(vfp_get_double)

ENTRY(vfp_put_double)
	tbl_branch r2, r3, #3
	.fpu	vfpv2
	.irp	dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:	vmov	d\dr, r0, r1
	ret	lr
	.org	1b + 8
	.endr
#ifdef CONFIG_VFPv3
	.fpu	vfpv3
	@ d16 - d31 registers
	.irp	dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:	vmov	d\dr, r0, r1
	ret	lr
	.org	1b + 8
	.endr
#endif
ENDPROC(vfp_put_double)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/vfp/vfpinstr.h
 *
 *  Copyright (C) 2004 ARM Limited.
 *  Written by Deep Blue Solutions Limited.
 *
 * VFP instruction masks.
 */
#define INST_CPRTDO(inst)	(((inst) & 0x0f000000) == 0x0e000000)
#define INST_CPRT(inst)		((inst) & (1 << 4))
#define INST_CPRT_L(inst)	((inst) & (1 << 20))
#define INST_CPRT_Rd(inst)	(((inst) & (15 << 12)) >> 12)
#define INST_CPRT_OP(inst)	(((inst) >> 21) & 7)
#define INST_CPNUM(inst)	((inst) & 0xf00)
#define CPNUM(cp)		((cp) << 8)

#define FOP_MASK	(0x00b00040)
#define FOP_FMAC	(0x00000000)
#define FOP_FNMAC	(0x00000040)
#define FOP_FMSC	(0x00100000)
#define FOP_FNMSC	(0x00100040)
#define FOP_FMUL	(0x00200000)
#define FOP_FNMUL	(0x00200040)
#define FOP_FADD	(0x00300000)
#define FOP_FSUB	(0x00300040)
#define FOP_FDIV	(0x00800000)
#define FOP_EXT		(0x00b00040)

#define FOP_TO_IDX(inst)	((inst & 0x00b00000) >> 20 | (inst & (1 << 6)) >> 4)

#define FEXT_MASK	(0x000f0080)
#define FEXT_FCPY	(0x00000000)
#define FEXT_FABS	(0x00000080)
#define FEXT_FNEG	(0x00010000)
#define FEXT_FSQRT	(0x00010080)
#define FEXT_FCMP	(0x00040000)
#define FEXT_FCMPE	(0x00040080)
#define FEXT_FCMPZ	(0x00050000)
#define FEXT_FCMPEZ	(0x00050080)
#define FEXT_FCVT	(0x00070080)
#define FEXT_FUITO	(0x00080000)
#define FEXT_FSITO	(0x00080080)
#define FEXT_FTOUI	(0x000c0000)
#define FEXT_FTOUIZ	(0x000c0080)
#define FEXT_FTOSI	(0x000d0000)
#define FEXT_FTOSIZ	(0x000d0080)

#define FEXT_TO_IDX(inst)	((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)

#define vfp_get_sd(inst)	((inst & 0x0000f000) >> 11 | (inst & (1 << 22)) >> 22)
#define vfp_get_dd(inst)	((inst & 0x0000f000) >> 12 | (inst & (1 << 22)) >> 18)
#define vfp_get_sm(inst)	((inst & 0x0000000f) << 1 | (inst & (1 << 5)) >> 5)
#define vfp_get_dm(inst)	((inst & 0x0000000f) | (inst & (1 << 5)) >> 1)
#define vfp_get_sn(inst)	((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
#define vfp_get_dn(inst)	((inst & 0x000f0000) >> 16 | (inst & (1 << 7)) >> 3)

#define vfp_single(inst)	(((inst) & 0x0000f00) == 0xa00)

#define FPSCR_N	(1 << 31)
#define FPSCR_Z	(1 << 30)
#define FPSCR_C (1 << 29)
#define FPSCR_V	(1 << 28)

#ifdef CONFIG_AS_VFP_VMRS_FPINST

#define fmrx(_vfp_) ({			\
	u32 __v;			\
	asm(".fpu	vfpv2\n"	\
	    "vmrs	%0, " #_vfp_	\
	    : "=r" (__v) : : "cc");	\
	__v;				\
 })

#define fmxr(_vfp_,_var_)		\
	asm(".fpu	vfpv2\n"	\
	    "vmsr	" #_vfp_ ", %0"	\
	   : : "r" (_var_) : "cc")

#else

#define vfpreg(_vfp_) #_vfp_

#define fmrx(_vfp_) ({			\
	u32 __v;			\
	asm("mrc p10, 7, %0, " vfpreg(_vfp_) ", cr0, 0 @ fmrx	%0, " #_vfp_	\
	    : "=r" (__v) : : "cc");	\
	__v;				\
 })

#define fmxr(_vfp_,_var_)		\
	asm("mcr p10, 7, %0, " vfpreg(_vfp_) ", cr0, 0 @ fmxr	" #_vfp_ ", %0"	\
	   : : "r" (_var_) : "cc")

#endif

u32 vfp_single_cpdo(u32 inst, u32 fpscr);
u32 vfp_single_cprt(u32 inst, u32 fpscr, struct pt_regs *regs);

u32 vfp_double_cpdo(u32 inst, u32 fpscr);