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 };
66
67 static const struct filterops zero_fop = {
68 .f_isfd = 1,
69 .f_event = zero_ev
70 };
71
72 static struct cdevsw full_cdevsw = {
73 .d_version = D_VERSION,
74 .d_read = zero_read,
75 .d_write = full_write,
76 .d_ioctl = zero_ioctl,
77 .d_kqfilter = kqfilter,
78 .d_name = "full",
79 };
80
81 static struct cdevsw null_cdevsw = {
82 .d_version = D_VERSION,
83 .d_read = (d_read_t *)nullop,
84 .d_write = null_write,
85 .d_ioctl = null_ioctl,
86 .d_kqfilter = kqfilter,
87 .d_name = "null",
88 };
89
90 static struct cdevsw zero_cdevsw = {
91 .d_version = D_VERSION,
92 .d_read = zero_read,
93 .d_write = null_write,
94 .d_ioctl = zero_ioctl,
95 .d_kqfilter = kqfilter,
96 .d_name = "zero",
97 .d_flags = D_MMAP_ANON,
98 };
99
100 /* ARGSUSED */
101 static int
full_write(struct cdev * dev __unused,struct uio * uio __unused,int flags __unused)102 full_write(struct cdev *dev __unused, struct uio *uio __unused, int flags __unused)
103 {
104
105 return (ENOSPC);
106 }
107
108 /* ARGSUSED */
109 static int
null_write(struct cdev * dev __unused,struct uio * uio,int flags __unused)110 null_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
111 {
112 uio->uio_resid = 0;
113
114 return (0);
115 }
116
117 /* ARGSUSED */
118 static int
null_ioctl(struct cdev * dev __unused,u_long cmd,caddr_t data __unused,int flags __unused,struct thread * td)119 null_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
120 int flags __unused, struct thread *td)
121 {
122 struct diocskerneldump_arg kda;
123 int error;
124
125 error = 0;
126 switch (cmd) {
127 case DIOCSKERNELDUMP:
128 bzero(&kda, sizeof(kda));
129 kda.kda_index = KDA_REMOVE_ALL;
130 error = dumper_remove(NULL, &kda);
131 break;
132 case FIONBIO:
133 break;
134 case FIOASYNC:
135 if (*(int *)data != 0)
136 error = EINVAL;
137 break;
138 default:
139 error = ENOIOCTL;
140 }
141 return (error);
142 }
143
144 /* ARGSUSED */
145 static int
zero_ioctl(struct cdev * dev __unused,u_long cmd,caddr_t data __unused,int flags __unused,struct thread * td)146 zero_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data __unused,
147 int flags __unused, struct thread *td)
148 {
149 int error;
150 error = 0;
151
152 switch (cmd) {
153 case FIONBIO:
154 break;
155 case FIOASYNC:
156 if (*(int *)data != 0)
157 error = EINVAL;
158 break;
159 default:
160 error = ENOIOCTL;
161 }
162 return (error);
163 }
164
165 /* ARGSUSED */
166 static int
zero_read(struct cdev * dev __unused,struct uio * uio,int flags __unused)167 zero_read(struct cdev *dev __unused, struct uio *uio, int flags __unused)
168 {
169 void *zbuf;
170 ssize_t len;
171 int error = 0;
172
173 KASSERT(uio->uio_rw == UIO_READ,
174 ("Can't be in %s for write", __func__));
175 zbuf = __DECONST(void *, zero_region);
176 while (uio->uio_resid > 0 && error == 0) {
177 len = uio->uio_resid;
178 if (len > ZERO_REGION_SIZE)
179 len = ZERO_REGION_SIZE;
180 error = uiomove(zbuf, len, uio);
181 }
182
183 return (error);
184 }
185
186 /* ARGSUSED */
187 static int
null_modevent(module_t mod __unused,int type,void * data __unused)188 null_modevent(module_t mod __unused, int type, void *data __unused)
189 {
190 switch(type) {
191 case MOD_LOAD:
192 if (bootverbose)
193 printf("null: <full device, null device, zero device>\n");
194 full_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &full_cdevsw, 0,
195 NULL, UID_ROOT, GID_WHEEL, 0666, "full");
196 null_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &null_cdevsw, 0,
197 NULL, UID_ROOT, GID_WHEEL, 0666, "null");
198 zero_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &zero_cdevsw, 0,
199 NULL, UID_ROOT, GID_WHEEL, 0666, "zero");
200 break;
201
202 case MOD_UNLOAD:
203 destroy_dev(full_dev);
204 destroy_dev(null_dev);
205 destroy_dev(zero_dev);
206 break;
207
208 case MOD_SHUTDOWN:
209 break;
210
211 default:
212 return (EOPNOTSUPP);
213 }
214
215 return (0);
216 }
217
218 static int
one_ev(struct knote * kn,long hint)219 one_ev(struct knote *kn, long hint)
220 {
221
222 return (1);
223 }
224
225 static int
zero_ev(struct knote * kn,long hint)226 zero_ev(struct knote *kn, long hint)
227 {
228
229 return (0);
230 }
231
232 static int
kqfilter(struct cdev * dev,struct knote * kn)233 kqfilter(struct cdev *dev, struct knote *kn)
234 {
235
236 switch (kn->kn_filter) {
237 case EVFILT_READ:
238 kn->kn_fop = dev->si_devsw == &null_cdevsw ? &zero_fop : &one_fop;
239 return (0);
240 case EVFILT_WRITE:
241 kn->kn_fop = dev->si_devsw == &full_cdevsw ? &zero_fop : &one_fop;
242 return (0);
243 default:
244 return (EOPNOTSUPP);
245 }
246 }
247
248 DEV_MODULE(null, null_modevent, NULL);
249 MODULE_VERSION(null, 1);
250