1/*-
2 * Copyright (c) 2008 David E. O'Brien
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/11/sys/compat/freebsd32/freebsd32_ioctl.c 350217 2019-07-22 18:14:34Z emaste $");
32
33#include "opt_compat.h"
34
35#include <sys/param.h>
36#include <sys/capsicum.h>
37#include <sys/cdio.h>
38#include <sys/fcntl.h>
39#include <sys/filio.h>
40#include <sys/file.h>
41#include <sys/ioccom.h>
42#include <sys/malloc.h>
43#include <sys/mdioctl.h>
44#include <sys/memrange.h>
45#include <sys/pciio.h>
46#include <sys/proc.h>
47#include <sys/syscall.h>
48#include <sys/syscallsubr.h>
49#include <sys/sysctl.h>
50#include <sys/sysent.h>
51#include <sys/sysproto.h>
52#include <sys/systm.h>
53
54#include <compat/freebsd32/freebsd32.h>
55#include <compat/freebsd32/freebsd32_ioctl.h>
56#include <compat/freebsd32/freebsd32_proto.h>
57
58/* Cannot get exact size in 64-bit due to alignment issue of entire struct. */
59CTASSERT((sizeof(struct md_ioctl32)+4) == 436);
60CTASSERT(sizeof(struct ioc_read_toc_entry32) == 8);
61CTASSERT(sizeof(struct ioc_toc_header32) == 4);
62CTASSERT(sizeof(struct mem_range_op32) == 12);
63CTASSERT(sizeof(struct pci_conf_io32) == 36);
64CTASSERT(sizeof(struct pci_match_conf32) == 44);
65CTASSERT(sizeof(struct pci_conf32) == 44);
66
67
68static int
69freebsd32_ioctl_md(struct thread *td, struct freebsd32_ioctl_args *uap,
70    struct file *fp)
71{
72	struct md_ioctl mdv;
73	struct md_ioctl32 md32;
74	u_long com = 0;
75	int i, error;
76
77	if (uap->com & IOC_IN) {
78		if ((error = copyin(uap->data, &md32, sizeof(md32)))) {
79			return (error);
80		}
81		CP(md32, mdv, md_version);
82		CP(md32, mdv, md_unit);
83		CP(md32, mdv, md_type);
84		PTRIN_CP(md32, mdv, md_file);
85		CP(md32, mdv, md_mediasize);
86		CP(md32, mdv, md_sectorsize);
87		CP(md32, mdv, md_options);
88		CP(md32, mdv, md_base);
89		CP(md32, mdv, md_fwheads);
90		CP(md32, mdv, md_fwsectors);
91	} else if (uap->com & IOC_OUT) {
92		/*
93		 * Zero the buffer so the user always
94		 * gets back something deterministic.
95		 */
96		bzero(&mdv, sizeof mdv);
97	}
98
99	switch (uap->com) {
100	case MDIOCATTACH_32:
101		com = MDIOCATTACH;
102		break;
103	case MDIOCDETACH_32:
104		com = MDIOCDETACH;
105		break;
106	case MDIOCQUERY_32:
107		com = MDIOCQUERY;
108		break;
109	case MDIOCLIST_32:
110		com = MDIOCLIST;
111		break;
112	default:
113		panic("%s: unknown MDIOC %#x", __func__, uap->com);
114	}
115	error = fo_ioctl(fp, com, (caddr_t)&mdv, td->td_ucred, td);
116	if (error == 0 && (com & IOC_OUT)) {
117		CP(mdv, md32, md_version);
118		CP(mdv, md32, md_unit);
119		CP(mdv, md32, md_type);
120		PTROUT_CP(mdv, md32, md_file);
121		CP(mdv, md32, md_mediasize);
122		CP(mdv, md32, md_sectorsize);
123		CP(mdv, md32, md_options);
124		CP(mdv, md32, md_base);
125		CP(mdv, md32, md_fwheads);
126		CP(mdv, md32, md_fwsectors);
127		if (com == MDIOCLIST) {
128			/*
129			 * Use MDNPAD, and not MDNPAD32.  Padding is
130			 * allocated and used by compat32 ABI.
131			 */
132			for (i = 0; i < MDNPAD; i++)
133				CP(mdv, md32, md_pad[i]);
134		}
135		error = copyout(&md32, uap->data, sizeof(md32));
136	}
137	return error;
138}
139
140
141static int
142freebsd32_ioctl_ioc_toc_header(struct thread *td,
143    struct freebsd32_ioctl_args *uap, struct file *fp)
144{
145	struct ioc_toc_header toch;
146	struct ioc_toc_header32 toch32;
147	int error;
148
149	if ((error = copyin(uap->data, &toch32, sizeof(toch32))))
150		return (error);
151	CP(toch32, toch, len);
152	CP(toch32, toch, starting_track);
153	CP(toch32, toch, ending_track);
154	error = fo_ioctl(fp, CDIOREADTOCHEADER, (caddr_t)&toch,
155	    td->td_ucred, td);
156	return (error);
157}
158
159
160static int
161freebsd32_ioctl_ioc_read_toc(struct thread *td,
162    struct freebsd32_ioctl_args *uap, struct file *fp)
163{
164	struct ioc_read_toc_entry toce;
165	struct ioc_read_toc_entry32 toce32;
166	int error;
167
168	if ((error = copyin(uap->data, &toce32, sizeof(toce32))))
169		return (error);
170	CP(toce32, toce, address_format);
171	CP(toce32, toce, starting_track);
172	CP(toce32, toce, data_len);
173	PTRIN_CP(toce32, toce, data);
174
175	if ((error = fo_ioctl(fp, CDIOREADTOCENTRYS, (caddr_t)&toce,
176	    td->td_ucred, td))) {
177		CP(toce, toce32, address_format);
178		CP(toce, toce32, starting_track);
179		CP(toce, toce32, data_len);
180		PTROUT_CP(toce, toce32, data);
181		error = copyout(&toce32, uap->data, sizeof(toce32));
182	}
183	return error;
184}
185
186static int
187freebsd32_ioctl_fiodgname(struct thread *td,
188    struct freebsd32_ioctl_args *uap, struct file *fp)
189{
190	struct fiodgname_arg fgn;
191	struct fiodgname_arg32 fgn32;
192	int error;
193
194	if ((error = copyin(uap->data, &fgn32, sizeof fgn32)) != 0)
195		return (error);
196	CP(fgn32, fgn, len);
197	PTRIN_CP(fgn32, fgn, buf);
198	error = fo_ioctl(fp, FIODGNAME, (caddr_t)&fgn, td->td_ucred, td);
199	return (error);
200}
201
202static int
203freebsd32_ioctl_memrange(struct thread *td,
204    struct freebsd32_ioctl_args *uap, struct file *fp)
205{
206	struct mem_range_op mro;
207	struct mem_range_op32 mro32;
208	int error;
209	u_long com;
210
211	if ((error = copyin(uap->data, &mro32, sizeof(mro32))) != 0)
212		return (error);
213
214	PTRIN_CP(mro32, mro, mo_desc);
215	CP(mro32, mro, mo_arg[0]);
216	CP(mro32, mro, mo_arg[1]);
217
218	com = 0;
219	switch (uap->com) {
220	case MEMRANGE_GET32:
221		com = MEMRANGE_GET;
222		break;
223
224	case MEMRANGE_SET32:
225		com = MEMRANGE_SET;
226		break;
227
228	default:
229		panic("%s: unknown MEMRANGE %#x", __func__, uap->com);
230	}
231
232	if ((error = fo_ioctl(fp, com, (caddr_t)&mro, td->td_ucred, td)) != 0)
233		return (error);
234
235	if ( (com & IOC_OUT) ) {
236		CP(mro, mro32, mo_arg[0]);
237		CP(mro, mro32, mo_arg[1]);
238
239		error = copyout(&mro32, uap->data, sizeof(mro32));
240	}
241
242	return (error);
243}
244
245static int
246freebsd32_ioctl_pciocgetconf(struct thread *td,
247    struct freebsd32_ioctl_args *uap, struct file *fp)
248{
249	struct pci_conf_io pci;
250	struct pci_conf_io32 pci32;
251	struct pci_match_conf32 pmc32;
252	struct pci_match_conf32 *pmc32p;
253	struct pci_match_conf pmc;
254	struct pci_match_conf *pmcp;
255	struct pci_conf32 pc32;
256	struct pci_conf32 *pc32p;
257	struct pci_conf pc;
258	struct pci_conf *pcp;
259	u_int32_t i;
260	u_int32_t npat_to_convert;
261	u_int32_t nmatch_to_convert;
262	vm_offset_t addr;
263	int error;
264
265	memset(&pmc, 0, sizeof(pmc));
266	memset(&pc32, 0, sizeof(pc32));
267	if ((error = copyin(uap->data, &pci32, sizeof(pci32))) != 0)
268		return (error);
269
270	CP(pci32, pci, num_patterns);
271	CP(pci32, pci, offset);
272	CP(pci32, pci, generation);
273
274	npat_to_convert = pci32.pat_buf_len / sizeof(struct pci_match_conf32);
275	pci.pat_buf_len = npat_to_convert * sizeof(struct pci_match_conf);
276	pci.patterns = NULL;
277	nmatch_to_convert = pci32.match_buf_len / sizeof(struct pci_conf32);
278	pci.match_buf_len = nmatch_to_convert * sizeof(struct pci_conf);
279	pci.matches = NULL;
280
281	if ((error = copyout_map(td, &addr, pci.pat_buf_len)) != 0)
282		goto cleanup;
283	pci.patterns = (struct pci_match_conf *)addr;
284	if ((error = copyout_map(td, &addr, pci.match_buf_len)) != 0)
285		goto cleanup;
286	pci.matches = (struct pci_conf *)addr;
287
288	npat_to_convert = min(npat_to_convert, pci.num_patterns);
289
290	for (i = 0, pmc32p = (struct pci_match_conf32 *)PTRIN(pci32.patterns),
291	     pmcp = pci.patterns;
292	     i < npat_to_convert; i++, pmc32p++, pmcp++) {
293		if ((error = copyin(pmc32p, &pmc32, sizeof(pmc32))) != 0)
294			goto cleanup;
295		CP(pmc32,pmc,pc_sel);
296		strlcpy(pmc.pd_name, pmc32.pd_name, sizeof(pmc.pd_name));
297		CP(pmc32,pmc,pd_unit);
298		CP(pmc32,pmc,pc_vendor);
299		CP(pmc32,pmc,pc_device);
300		CP(pmc32,pmc,pc_class);
301		CP(pmc32,pmc,flags);
302		if ((error = copyout(&pmc, pmcp, sizeof(pmc))) != 0)
303			goto cleanup;
304	}
305
306	if ((error = fo_ioctl(fp, PCIOCGETCONF, (caddr_t)&pci,
307			      td->td_ucred, td)) != 0)
308		goto cleanup;
309
310	nmatch_to_convert = min(nmatch_to_convert, pci.num_matches);
311
312	for (i = 0, pcp = pci.matches,
313	     pc32p = (struct pci_conf32 *)PTRIN(pci32.matches);
314	     i < nmatch_to_convert; i++, pcp++, pc32p++) {
315		if ((error = copyin(pcp, &pc, sizeof(pc))) != 0)
316			goto cleanup;
317		CP(pc,pc32,pc_sel);
318		CP(pc,pc32,pc_hdr);
319		CP(pc,pc32,pc_subvendor);
320		CP(pc,pc32,pc_subdevice);
321		CP(pc,pc32,pc_vendor);
322		CP(pc,pc32,pc_device);
323		CP(pc,pc32,pc_class);
324		CP(pc,pc32,pc_subclass);
325		CP(pc,pc32,pc_progif);
326		CP(pc,pc32,pc_revid);
327		strlcpy(pc32.pd_name, pc.pd_name, sizeof(pc32.pd_name));
328		CP(pc,pc32,pd_unit);
329		if ((error = copyout(&pc32, pc32p, sizeof(pc32))) != 0)
330			goto cleanup;
331	}
332
333	CP(pci, pci32, num_matches);
334	CP(pci, pci32, offset);
335	CP(pci, pci32, generation);
336	CP(pci, pci32, status);
337
338	error = copyout(&pci32, uap->data, sizeof(pci32));
339
340cleanup:
341	if (pci.patterns)
342		copyout_unmap(td, (vm_offset_t)pci.patterns, pci.pat_buf_len);
343	if (pci.matches)
344		copyout_unmap(td, (vm_offset_t)pci.matches, pci.match_buf_len);
345
346	return (error);
347}
348
349static int
350freebsd32_ioctl_sg(struct thread *td,
351    struct freebsd32_ioctl_args *uap, struct file *fp)
352{
353	struct sg_io_hdr io;
354	struct sg_io_hdr32 io32;
355	int error;
356
357	if ((error = copyin(uap->data, &io32, sizeof(io32))) != 0)
358		return (error);
359
360	CP(io32, io, interface_id);
361	CP(io32, io, dxfer_direction);
362	CP(io32, io, cmd_len);
363	CP(io32, io, mx_sb_len);
364	CP(io32, io, iovec_count);
365	CP(io32, io, dxfer_len);
366	PTRIN_CP(io32, io, dxferp);
367	PTRIN_CP(io32, io, cmdp);
368	PTRIN_CP(io32, io, sbp);
369	CP(io32, io, timeout);
370	CP(io32, io, flags);
371	CP(io32, io, pack_id);
372	PTRIN_CP(io32, io, usr_ptr);
373	CP(io32, io, status);
374	CP(io32, io, masked_status);
375	CP(io32, io, msg_status);
376	CP(io32, io, sb_len_wr);
377	CP(io32, io, host_status);
378	CP(io32, io, driver_status);
379	CP(io32, io, resid);
380	CP(io32, io, duration);
381	CP(io32, io, info);
382
383	if ((error = fo_ioctl(fp, SG_IO, (caddr_t)&io, td->td_ucred, td)) != 0)
384		return (error);
385
386	CP(io, io32, interface_id);
387	CP(io, io32, dxfer_direction);
388	CP(io, io32, cmd_len);
389	CP(io, io32, mx_sb_len);
390	CP(io, io32, iovec_count);
391	CP(io, io32, dxfer_len);
392	PTROUT_CP(io, io32, dxferp);
393	PTROUT_CP(io, io32, cmdp);
394	PTROUT_CP(io, io32, sbp);
395	CP(io, io32, timeout);
396	CP(io, io32, flags);
397	CP(io, io32, pack_id);
398	PTROUT_CP(io, io32, usr_ptr);
399	CP(io, io32, status);
400	CP(io, io32, masked_status);
401	CP(io, io32, msg_status);
402	CP(io, io32, sb_len_wr);
403	CP(io, io32, host_status);
404	CP(io, io32, driver_status);
405	CP(io, io32, resid);
406	CP(io, io32, duration);
407	CP(io, io32, info);
408
409	error = copyout(&io32, uap->data, sizeof(io32));
410
411	return (error);
412}
413
414int
415freebsd32_ioctl(struct thread *td, struct freebsd32_ioctl_args *uap)
416{
417	struct ioctl_args ap /*{
418		int	fd;
419		u_long	com;
420		caddr_t	data;
421	}*/ ;
422	struct file *fp;
423	cap_rights_t rights;
424	int error;
425
426	error = fget(td, uap->fd, cap_rights_init(&rights, CAP_IOCTL), &fp);
427	if (error != 0)
428		return (error);
429	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
430		fdrop(fp, td);
431		return (EBADF);
432	}
433
434	switch (uap->com) {
435	case MDIOCATTACH_32:	/* FALLTHROUGH */
436	case MDIOCDETACH_32:	/* FALLTHROUGH */
437	case MDIOCQUERY_32:	/* FALLTHROUGH */
438	case MDIOCLIST_32:
439		error = freebsd32_ioctl_md(td, uap, fp);
440		break;
441
442	case CDIOREADTOCENTRYS_32:
443		error = freebsd32_ioctl_ioc_read_toc(td, uap, fp);
444		break;
445
446	case CDIOREADTOCHEADER_32:
447		error = freebsd32_ioctl_ioc_toc_header(td, uap, fp);
448		break;
449
450	case FIODGNAME_32:
451		error = freebsd32_ioctl_fiodgname(td, uap, fp);
452		break;
453
454	case MEMRANGE_GET32:	/* FALLTHROUGH */
455	case MEMRANGE_SET32:
456		error = freebsd32_ioctl_memrange(td, uap, fp);
457		break;
458
459	case PCIOCGETCONF_32:
460		error = freebsd32_ioctl_pciocgetconf(td, uap, fp);
461		break;
462
463	case SG_IO_32:
464		error = freebsd32_ioctl_sg(td, uap, fp);
465		break;
466
467	default:
468		fdrop(fp, td);
469		ap.fd = uap->fd;
470		ap.com = uap->com;
471		PTRIN_CP(*uap, ap, data);
472		return sys_ioctl(td, &ap);
473	}
474
475	fdrop(fp, td);
476	return error;
477}
478