1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2018 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
8 * under sponsorship from the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include "opt_compat.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/imgact.h>
40#include <sys/lock.h>
41#include <sys/sx.h>
42#include <vm/vm.h>
43#include <vm/vm_param.h>
44#include <vm/vm_extern.h>
45#include <vm/pmap.h>
46
47#include <machine/atomic.h>
48#include <machine/md_var.h>
49
50#include <compat/linux/linux_emul.h>
51#include <compat/linux/linux_futex.h>
52
53struct futex_st0 {
54	int oparg;
55	int *oldval;
56};
57
58static void
59futex_xchgl_slow0(vm_offset_t kva, void *arg)
60{
61	struct futex_st0 *st;
62
63	st = arg;
64	*st->oldval = atomic_swap_int((int *)kva, st->oparg);
65}
66
67int
68futex_xchgl(int oparg, uint32_t *uaddr, int *oldval)
69{
70	struct futex_st0 st;
71
72	st.oparg = oparg;
73	st.oldval = oldval;
74	if (cp_slow0((vm_offset_t)uaddr, sizeof(uint32_t), true,
75	    futex_xchgl_slow0, &st) != 0)
76		return (-EFAULT);
77	return (0);
78}
79
80static void
81futex_addl_slow0(vm_offset_t kva, void *arg)
82{
83	struct futex_st0 *st;
84
85	st = arg;
86	*st->oldval = atomic_fetchadd_int((int *)kva, st->oparg);
87}
88
89int
90futex_addl(int oparg, uint32_t *uaddr, int *oldval)
91{
92	struct futex_st0 st;
93
94	st.oparg = oparg;
95	st.oldval = oldval;
96	if (cp_slow0((vm_offset_t)uaddr, sizeof(uint32_t), true,
97	    futex_addl_slow0, &st) != 0)
98		return (-EFAULT);
99	return (0);
100}
101
102static void
103futex_orl_slow0(vm_offset_t kva, void *arg)
104{
105	struct futex_st0 *st;
106	int old;
107
108	st = arg;
109	old = *(int *)kva;
110	while (!atomic_fcmpset_int((int *)kva, &old, old | st->oparg))
111		;
112	*st->oldval = old;
113}
114
115int
116futex_orl(int oparg, uint32_t *uaddr, int *oldval)
117{
118	struct futex_st0 st;
119
120	st.oparg = oparg;
121	st.oldval = oldval;
122	if (cp_slow0((vm_offset_t)uaddr, sizeof(uint32_t), true,
123	    futex_orl_slow0, &st) != 0)
124		return (-EFAULT);
125	return (0);
126}
127
128static void
129futex_andl_slow0(vm_offset_t kva, void *arg)
130{
131	struct futex_st0 *st;
132	int old;
133
134	st = arg;
135	old = *(int *)kva;
136	while (!atomic_fcmpset_int((int *)kva, &old, old & st->oparg))
137		;
138	*st->oldval = old;
139}
140
141int
142futex_andl(int oparg, uint32_t *uaddr, int *oldval)
143{
144	struct futex_st0 st;
145
146	st.oparg = oparg;
147	st.oldval = oldval;
148	if (cp_slow0((vm_offset_t)uaddr, sizeof(uint32_t), true,
149	    futex_andl_slow0, &st) != 0)
150		return (-EFAULT);
151	return (0);
152}
153
154static void
155futex_xorl_slow0(vm_offset_t kva, void *arg)
156{
157	struct futex_st0 *st;
158	int old;
159
160	st = arg;
161	old = *(int *)kva;
162	while (!atomic_fcmpset_int((int *)kva, &old, old ^ st->oparg))
163		;
164	*st->oldval = old;
165}
166
167int
168futex_xorl(int oparg, uint32_t *uaddr, int *oldval)
169{
170	struct futex_st0 st;
171
172	st.oparg = oparg;
173	st.oldval = oldval;
174	if (cp_slow0((vm_offset_t)uaddr, sizeof(uint32_t), true,
175	    futex_xorl_slow0, &st) != 0)
176		return (-EFAULT);
177	return (0);
178}
179