1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2000 Mark R. V. Murray & Jeroen C. van Gelderen
5 * Copyright (c) 2001-2004 Mark R. V. Murray
6 * Copyright (c) 2014 Eitan Adler
7 * Copyright (c) 2025 Pietro Cerutti
8 * All rights reserved.
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 * in this position and unchanged.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <sys/uio.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/disk.h>
41 #include <sys/bus.h>
42 #include <sys/filio.h>
43 #include <sys/event.h>
44
45 #include <machine/bus.h>
46 #include <machine/vmparam.h>
47
48 /* For use with destroy_dev(9). */
49 static struct cdev *full_dev;
50 static struct cdev *null_dev;
51 static struct cdev *zero_dev;
52
53 static d_write_t full_write;
54 static d_write_t null_write;
55 static d_ioctl_t null_ioctl;
56 static d_ioctl_t zero_ioctl;
57 static d_read_t zero_read;
58 static d_kqfilter_t kqfilter;
59 static int one_ev(struct knote *kn, long hint);
60 static int zero_ev(struct knote *kn, long hint);
61
62 static const struct filterops one_fop = {
63 .f_isfd = 1,
64 .f_event = one_ev,
65 .f_copy = knote_triv_copy,
66 };
67
68 static const struct filterops zero_fop = {
69 .f_isfd = 1,
70 .f_event = zero_ev,
71 .f_copy = knote_triv_copy,
72 };
73
74 static struct cdevsw full_cdevsw = {
75 .d_version = D_VERSION,
76 .d_read = zero_read,
77 .d_write = full_write,
78 .d_ioctl = zero_ioctl,
79 .d_kqfilter = kqfilter,
80 .d_name = "full",
81 };
82
83 static struct cdevsw null_cdevsw = {
84 .d_version = D_VERSION,
85 .d_read = (d_read_t *)nullop,
86 .d_write = null_write,
87 .d_ioctl = null_ioctl,
88 .d_kqfilter = kqfilter,
89 .d_name = "null",
90 };
91
92 static struct cdevsw zero_cdevsw = {
93 .d_version = D_VERSION,
94 .d_read = zero_read,
95 .d_write = null_write,
96 .d_ioctl = zero_ioctl,
97 .d_kqfilter = kqfilter,
98 .d_name = "zero",
99 .d_flags = D_MMAP_ANON,
100 };
101
102 /* ARGSUSED */
103 static int
full_write(struct cdev * dev __unused,struct uio * uio __unused,int flags __unused)104 full_write(struct cdev *dev __unused, struct uio *uio __unused, int flags __unused)
105 {
106
107 return (ENOSPC);
108 }
109
110 /* ARGSUSED */
111 static int
null_write(struct cdev * dev __unused,struct uio * uio,int flags __unused)112 null_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
113 {
114 uio->uio_resid = 0;
115
116 return (0);
117 }
118
119 /* ARGSUSED */
120 static int
null_ioctl(struct cdev * dev __unused,u_long cmd,caddr_t data __unused,int flags __unused,struct thread * td)121 null_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
122 int flags __unused, struct thread *td)
123 {
124 struct diocskerneldump_arg kda;
125 int error;
126
127 error = 0;
128 switch (cmd) {
129 case DIOCSKERNELDUMP:
130 bzero(&kda, sizeof(kda));
131 kda.kda_index = KDA_REMOVE_ALL;
132 error = dumper_remove(NULL, &kda);
133 break;
134 case FIONBIO:
135 break;
136 case FIOASYNC:
137 if (*(int *)data != 0)
138 error = EINVAL;
139 break;
140 default:
141 error = ENOIOCTL;
142 }
143 return (error);
144 }
145
146 /* ARGSUSED */
147 static int
zero_ioctl(struct cdev * dev __unused,u_long cmd,caddr_t data __unused,int flags __unused,struct thread * td)148 zero_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
149 int flags __unused, struct thread *td)
150 {
151 int error;
152 error = 0;
153
154 switch (cmd) {
155 case FIONBIO:
156 break;
157 case FIOASYNC:
158 if (*(int *)data != 0)
159 error = EINVAL;
160 break;
161 default:
162 error = ENOIOCTL;
163 }
164 return (error);
165 }
166
167 /* ARGSUSED */
168 static int
zero_read(struct cdev * dev __unused,struct uio * uio,int flags __unused)169 zero_read(struct cdev *dev __unused, struct uio *uio, int flags __unused)
170 {
171 void *zbuf;
172 ssize_t len;
173 int error = 0;
174
175 KASSERT(uio->uio_rw == UIO_READ,
176 ("Can't be in %s for write", __func__));
177 zbuf = __DECONST(void *, zero_region);
178 while (uio->uio_resid > 0 && error == 0) {
179 len = uio->uio_resid;
180 if (len > ZERO_REGION_SIZE)
181 len = ZERO_REGION_SIZE;
182 error = uiomove(zbuf, len, uio);
183 }
184
185 return (error);
186 }
187
188 /* ARGSUSED */
189 static int
null_modevent(module_t mod __unused,int type,void * data __unused)190 null_modevent(module_t mod __unused, int type, void *data __unused)
191 {
192 switch(type) {
193 case MOD_LOAD:
194 if (bootverbose)
195 printf("null: <full device, null device, zero device>\n");
196 full_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &full_cdevsw, 0,
197 NULL, UID_ROOT, GID_WHEEL, 0666, "full");
198 null_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &null_cdevsw, 0,
199 NULL, UID_ROOT, GID_WHEEL, 0666, "null");
200 zero_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &zero_cdevsw, 0,
201 NULL, UID_ROOT, GID_WHEEL, 0666, "zero");
202 break;
203
204 case MOD_UNLOAD:
205 destroy_dev(full_dev);
206 destroy_dev(null_dev);
207 destroy_dev(zero_dev);
208 break;
209
210 case MOD_SHUTDOWN:
211 break;
212
213 default:
214 return (EOPNOTSUPP);
215 }
216
217 return (0);
218 }
219
220 static int
one_ev(struct knote * kn,long hint)221 one_ev(struct knote *kn, long hint)
222 {
223
224 return (1);
225 }
226
227 static int
zero_ev(struct knote * kn,long hint)228 zero_ev(struct knote *kn, long hint)
229 {
230
231 return (0);
232 }
233
234 static int
kqfilter(struct cdev * dev,struct knote * kn)235 kqfilter(struct cdev *dev, struct knote *kn)
236 {
237
238 switch (kn->kn_filter) {
239 case EVFILT_READ:
240 kn->kn_fop = dev->si_devsw == &null_cdevsw ? &zero_fop : &one_fop;
241 return (0);
242 case EVFILT_WRITE:
243 kn->kn_fop = dev->si_devsw == &full_cdevsw ? &zero_fop : &one_fop;
244 return (0);
245 default:
246 return (EOPNOTSUPP);
247 }
248 }
249
250 DEV_MODULE(null, null_modevent, NULL);
251 MODULE_VERSION(null, 1);
252