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