xref: /freebsd-src/sys/kern/kern_vnodedumper.c (revision 37cef00192ef1347d27ac64be419267a4ba604ac)
1c9114f9fSMitchell Horne /*-
2c9114f9fSMitchell Horne  * Copyright (c) 2021-2022 Juniper Networks
3c9114f9fSMitchell Horne  *
4c9114f9fSMitchell Horne  * This software was developed by Mitchell Horne <mhorne@FreeBSD.org>
5c9114f9fSMitchell Horne  * under sponsorship from Juniper Networks and Klara Systems.
6c9114f9fSMitchell Horne  *
7c9114f9fSMitchell Horne  * Redistribution and use in source and binary forms, with or without
8c9114f9fSMitchell Horne  * modification, are permitted provided that the following conditions
9c9114f9fSMitchell Horne  * are met:
10c9114f9fSMitchell Horne  *
11c9114f9fSMitchell Horne  * 1. Redistributions of source code must retain the above copyright
12c9114f9fSMitchell Horne  *    notice, this list of conditions and the following disclaimer.
13c9114f9fSMitchell Horne  * 2. Redistributions in binary form must reproduce the above copyright
14c9114f9fSMitchell Horne  *    notice, this list of conditions and the following disclaimer in the
15c9114f9fSMitchell Horne  *    documentation and/or other materials provided with the distribution.
16c9114f9fSMitchell Horne  *
17c9114f9fSMitchell Horne  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18c9114f9fSMitchell Horne  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19c9114f9fSMitchell Horne  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20c9114f9fSMitchell Horne  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21c9114f9fSMitchell Horne  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22c9114f9fSMitchell Horne  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23c9114f9fSMitchell Horne  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24c9114f9fSMitchell Horne  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25c9114f9fSMitchell Horne  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26c9114f9fSMitchell Horne  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27c9114f9fSMitchell Horne  */
28c9114f9fSMitchell Horne 
29c9114f9fSMitchell Horne #include <sys/param.h>
30c9114f9fSMitchell Horne #include <sys/systm.h>
31c9114f9fSMitchell Horne #include <sys/conf.h>
32c9114f9fSMitchell Horne #include <sys/caprights.h>
33c9114f9fSMitchell Horne #include <sys/disk.h>
34eb9d205fSMitchell Horne #include <sys/eventhandler.h>
35c9114f9fSMitchell Horne #include <sys/fcntl.h>
36c9114f9fSMitchell Horne #include <sys/file.h>
37c9114f9fSMitchell Horne #include <sys/kerneldump.h>
38c9114f9fSMitchell Horne #include <sys/limits.h>
39c9114f9fSMitchell Horne #include <sys/malloc.h>
40c9114f9fSMitchell Horne #include <sys/namei.h>
41c9114f9fSMitchell Horne #include <sys/priv.h>
42c9114f9fSMitchell Horne #include <sys/proc.h>
43c9114f9fSMitchell Horne #include <sys/stat.h>
44c9114f9fSMitchell Horne #include <sys/sysctl.h>
45c9114f9fSMitchell Horne #include <sys/vnode.h>
46c9114f9fSMitchell Horne 
47c3179891SMark Johnston #include <machine/pcb.h>
48c9114f9fSMitchell Horne #include <machine/vmparam.h>
49c9114f9fSMitchell Horne 
50c9114f9fSMitchell Horne static dumper_start_t vnode_dumper_start;
51c9114f9fSMitchell Horne static dumper_t vnode_dump;
52c9114f9fSMitchell Horne static dumper_hdr_t vnode_write_headers;
53c9114f9fSMitchell Horne 
54c9114f9fSMitchell Horne static struct sx livedump_sx;
55c9114f9fSMitchell Horne SX_SYSINIT(livedump, &livedump_sx, "Livedump sx");
56c9114f9fSMitchell Horne 
57c9114f9fSMitchell Horne /*
58c9114f9fSMitchell Horne  * Invoke a live minidump on the system.
59c9114f9fSMitchell Horne  */
60c9114f9fSMitchell Horne int
61c9114f9fSMitchell Horne livedump_start(int fd, int flags, uint8_t compression)
62c9114f9fSMitchell Horne {
63c9114f9fSMitchell Horne #if MINIDUMP_PAGE_TRACKING == 1
64c9114f9fSMitchell Horne 	struct file *fp;
6560bc9617SVijeyalakshumi Koteeswaran 	struct vnode *vp;
66c9114f9fSMitchell Horne 	int error;
67c9114f9fSMitchell Horne 
68c9114f9fSMitchell Horne 	error = priv_check(curthread, PRIV_KMEM_READ);
69c9114f9fSMitchell Horne 	if (error != 0)
70c9114f9fSMitchell Horne 		return (error);
71c9114f9fSMitchell Horne 
72c9114f9fSMitchell Horne 	if (flags != 0)
73c9114f9fSMitchell Horne 		return (EINVAL);
74c9114f9fSMitchell Horne 
75c9114f9fSMitchell Horne 	error = getvnode(curthread, fd, &cap_write_rights, &fp);
76c9114f9fSMitchell Horne 	if (error != 0)
77c9114f9fSMitchell Horne 		return (error);
78c9114f9fSMitchell Horne 	vp = fp->f_vnode;
79c9114f9fSMitchell Horne 
80c9114f9fSMitchell Horne 	if ((fp->f_flag & FWRITE) == 0) {
81c9114f9fSMitchell Horne 		error = EBADF;
82c9114f9fSMitchell Horne 		goto drop;
83c9114f9fSMitchell Horne 	}
8460bc9617SVijeyalakshumi Koteeswaran 	error = livedump_start_vnode(vp, flags, compression);
8560bc9617SVijeyalakshumi Koteeswaran 	if (error != 0)
8660bc9617SVijeyalakshumi Koteeswaran 		goto drop;
8760bc9617SVijeyalakshumi Koteeswaran drop:
8860bc9617SVijeyalakshumi Koteeswaran 	fdrop(fp, curthread);
8960bc9617SVijeyalakshumi Koteeswaran 	return (error);
9060bc9617SVijeyalakshumi Koteeswaran #else
9160bc9617SVijeyalakshumi Koteeswaran 	return (EOPNOTSUPP);
9260bc9617SVijeyalakshumi Koteeswaran #endif /* MINIDUMP_PAGE_TRACKING == 1 */
9360bc9617SVijeyalakshumi Koteeswaran }
9460bc9617SVijeyalakshumi Koteeswaran 
9560bc9617SVijeyalakshumi Koteeswaran int
9660bc9617SVijeyalakshumi Koteeswaran livedump_start_vnode(struct vnode *vp, int flags, uint8_t compression)
9760bc9617SVijeyalakshumi Koteeswaran {
9860bc9617SVijeyalakshumi Koteeswaran #if MINIDUMP_PAGE_TRACKING == 1
9960bc9617SVijeyalakshumi Koteeswaran 	struct dumperinfo di, *livedi;
10060bc9617SVijeyalakshumi Koteeswaran 	struct diocskerneldump_arg kda;
10160bc9617SVijeyalakshumi Koteeswaran 	void *rl_cookie;
10260bc9617SVijeyalakshumi Koteeswaran 	int error;
103c9114f9fSMitchell Horne 
104c9114f9fSMitchell Horne         /* Set up a new dumper. */
105c9114f9fSMitchell Horne 	bzero(&di, sizeof(di));
106c9114f9fSMitchell Horne 	di.dumper_start = vnode_dumper_start;
107c9114f9fSMitchell Horne 	di.dumper = vnode_dump;
108c9114f9fSMitchell Horne 	di.dumper_hdr = vnode_write_headers;
109c9114f9fSMitchell Horne 	di.blocksize = PAGE_SIZE; /* Arbitrary. */
110c9114f9fSMitchell Horne 	di.maxiosize = MAXDUMPPGS * PAGE_SIZE;
111c9114f9fSMitchell Horne 
112c9114f9fSMitchell Horne 	bzero(&kda, sizeof(kda));
113c9114f9fSMitchell Horne 	kda.kda_compression = compression;
114c9114f9fSMitchell Horne 	error = dumper_create(&di, "livedump", &kda, &livedi);
115c9114f9fSMitchell Horne 	if (error != 0)
11660bc9617SVijeyalakshumi Koteeswaran 		return (error);
117c9114f9fSMitchell Horne 
118c9114f9fSMitchell Horne 	/* Only allow one livedump to proceed at a time. */
119c9114f9fSMitchell Horne 	if (sx_try_xlock(&livedump_sx) == 0) {
120c9114f9fSMitchell Horne 		dumper_destroy(livedi);
121c9114f9fSMitchell Horne 		error = EBUSY;
12260bc9617SVijeyalakshumi Koteeswaran 		return (error);
123c9114f9fSMitchell Horne 	}
124c9114f9fSMitchell Horne 
125c9114f9fSMitchell Horne 	/* To be used by the callback functions. */
126c9114f9fSMitchell Horne 	livedi->priv = vp;
127c9114f9fSMitchell Horne 
128c9114f9fSMitchell Horne 	/* Lock the entire file range and vnode. */
129c9114f9fSMitchell Horne 	rl_cookie = vn_rangelock_wlock(vp, 0, OFF_MAX);
130c9114f9fSMitchell Horne 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
131c9114f9fSMitchell Horne 
132eb9d205fSMitchell Horne 	EVENTHANDLER_INVOKE(livedumper_start, &error);
133eb9d205fSMitchell Horne 	if (error != 0)
134eb9d205fSMitchell Horne 		goto out;
135eb9d205fSMitchell Horne 
136*37cef001SMark Johnston 	curthread->td_pflags2 |= TDP2_SAN_QUIET;
137c9114f9fSMitchell Horne 	dump_savectx();
138c9114f9fSMitchell Horne 	error = minidumpsys(livedi, true);
139*37cef001SMark Johnston 	curthread->td_pflags2 &= ~TDP2_SAN_QUIET;
140c9114f9fSMitchell Horne 
141eb9d205fSMitchell Horne 	EVENTHANDLER_INVOKE(livedumper_finish);
142eb9d205fSMitchell Horne out:
143c9114f9fSMitchell Horne 	VOP_UNLOCK(vp);
144c9114f9fSMitchell Horne 	vn_rangelock_unlock(vp, rl_cookie);
145c9114f9fSMitchell Horne 	sx_xunlock(&livedump_sx);
146c9114f9fSMitchell Horne 	dumper_destroy(livedi);
147c9114f9fSMitchell Horne 	return (error);
148c9114f9fSMitchell Horne #else
149c9114f9fSMitchell Horne 	return (EOPNOTSUPP);
150c9114f9fSMitchell Horne #endif /* MINIDUMP_PAGE_TRACKING == 1 */
151c9114f9fSMitchell Horne }
152c9114f9fSMitchell Horne 
153c9114f9fSMitchell Horne int
154c9114f9fSMitchell Horne vnode_dumper_start(struct dumperinfo *di, void *key, uint32_t keysize)
155c9114f9fSMitchell Horne {
156c9114f9fSMitchell Horne 
157c9114f9fSMitchell Horne 	/* Always begin with an offset of zero. */
158c9114f9fSMitchell Horne 	di->dumpoff = 0;
159c9114f9fSMitchell Horne 
160c9114f9fSMitchell Horne 	KASSERT(keysize == 0, ("encryption not supported for livedumps"));
161c9114f9fSMitchell Horne 	return (0);
162c9114f9fSMitchell Horne }
163c9114f9fSMitchell Horne 
164c9114f9fSMitchell Horne /*
165c9114f9fSMitchell Horne  * Callback from dumpsys() to dump a chunk of memory.
166c9114f9fSMitchell Horne  *
167c9114f9fSMitchell Horne  * Parameters:
168c9114f9fSMitchell Horne  *	arg	 Opaque private pointer to vnode
169c9114f9fSMitchell Horne  *	virtual  Virtual address (where to read the data from)
170c9114f9fSMitchell Horne  *	offset	 Offset from start of core file
171c9114f9fSMitchell Horne  *	length	 Data length
172c9114f9fSMitchell Horne  *
173c9114f9fSMitchell Horne  * Return value:
174c9114f9fSMitchell Horne  *	0 on success
175c9114f9fSMitchell Horne  *	errno on error
176c9114f9fSMitchell Horne  */
177c9114f9fSMitchell Horne int
178489ba222SMitchell Horne vnode_dump(void *arg, void *virtual, off_t offset, size_t length)
179c9114f9fSMitchell Horne {
180c9114f9fSMitchell Horne 	struct vnode *vp;
181c9114f9fSMitchell Horne 	int error = 0;
182c9114f9fSMitchell Horne 
183c9114f9fSMitchell Horne 	vp = arg;
184c9114f9fSMitchell Horne 	MPASS(vp != NULL);
185c9114f9fSMitchell Horne 	ASSERT_VOP_LOCKED(vp, __func__);
186c9114f9fSMitchell Horne 
187eb9d205fSMitchell Horne 	EVENTHANDLER_INVOKE(livedumper_dump, virtual, offset, length, &error);
188eb9d205fSMitchell Horne 	if (error != 0)
189eb9d205fSMitchell Horne 		return (error);
190eb9d205fSMitchell Horne 
191c9114f9fSMitchell Horne 	/* Done? */
192c9114f9fSMitchell Horne 	if (virtual == NULL)
193c9114f9fSMitchell Horne 		return (0);
194c9114f9fSMitchell Horne 
195c9114f9fSMitchell Horne 	error = vn_rdwr(UIO_WRITE, vp, virtual, length, offset, UIO_SYSSPACE,
196c9114f9fSMitchell Horne 	    IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL, curthread);
197c9114f9fSMitchell Horne 	if (error != 0)
198c9114f9fSMitchell Horne 		uprintf("%s: error writing livedump block at offset %jx: %d\n",
199c9114f9fSMitchell Horne 		    __func__, (uintmax_t)offset, error);
200c9114f9fSMitchell Horne 	return (error);
201c9114f9fSMitchell Horne }
202c9114f9fSMitchell Horne 
203c9114f9fSMitchell Horne /*
204c9114f9fSMitchell Horne  * Callback from dumpsys() to write out the dump header, placed at the end.
205c9114f9fSMitchell Horne  */
206c9114f9fSMitchell Horne int
207c9114f9fSMitchell Horne vnode_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh)
208c9114f9fSMitchell Horne {
209c9114f9fSMitchell Horne 	struct vnode *vp;
210c9114f9fSMitchell Horne 	int error;
211c9114f9fSMitchell Horne 	off_t offset;
212c9114f9fSMitchell Horne 
213c9114f9fSMitchell Horne 	vp = di->priv;
214c9114f9fSMitchell Horne 	MPASS(vp != NULL);
215c9114f9fSMitchell Horne 	ASSERT_VOP_LOCKED(vp, __func__);
216c9114f9fSMitchell Horne 
217c9114f9fSMitchell Horne 	/* Compensate for compression/encryption adjustment of dumpoff. */
218c9114f9fSMitchell Horne 	offset = roundup2(di->dumpoff, di->blocksize);
219c9114f9fSMitchell Horne 
220c9114f9fSMitchell Horne 	/* Write the kernel dump header to the end of the file. */
221c9114f9fSMitchell Horne 	error = vn_rdwr(UIO_WRITE, vp, kdh, sizeof(*kdh), offset,
222c9114f9fSMitchell Horne 	    UIO_SYSSPACE, IO_NODELOCKED, curthread->td_ucred, NOCRED, NULL,
223c9114f9fSMitchell Horne 	    curthread);
224c9114f9fSMitchell Horne 	if (error != 0)
225c9114f9fSMitchell Horne 		uprintf("%s: error writing livedump header: %d\n", __func__,
226c9114f9fSMitchell Horne 		    error);
227c9114f9fSMitchell Horne 	return (error);
228c9114f9fSMitchell Horne }
229