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