diff --git a/sys/kern/kern_dump.c b/sys/kern/kern_dump.c index 278863e19a65..17ac4e418645 100644 --- a/sys/kern/kern_dump.c +++ b/sys/kern/kern_dump.c @@ -1,476 +1,535 @@ /*- * Copyright (c) 2002 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(sizeof(struct kerneldumpheader) == 512); #define MD_ALIGN(x) roundup2((off_t)(x), PAGE_SIZE) /* Handle buffered writes. */ static size_t fragsz; struct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; #if !defined(__powerpc__) void dumpsys_gen_pa_init(void) { int n, idx; bzero(dump_map, sizeof(dump_map)); for (n = 0; n < nitems(dump_map); n++) { idx = n * 2; if (dump_avail[idx] == 0 && dump_avail[idx + 1] == 0) break; dump_map[n].pa_start = dump_avail[idx]; dump_map[n].pa_size = dump_avail[idx + 1] - dump_avail[idx]; } } #endif struct dump_pa * dumpsys_gen_pa_next(struct dump_pa *mdp) { if (mdp == NULL) return (&dump_map[0]); mdp++; if (mdp->pa_size == 0) mdp = NULL; return (mdp); } void dumpsys_gen_wbinv_all(void) { } void dumpsys_gen_unmap_chunk(vm_paddr_t pa __unused, size_t chunk __unused, void *va __unused) { } int dumpsys_gen_write_aux_headers(struct dumperinfo *di) { return (0); } int dumpsys_buf_seek(struct dumperinfo *di, size_t sz) { static uint8_t buf[DEV_BSIZE]; size_t nbytes; int error; bzero(buf, sizeof(buf)); while (sz > 0) { nbytes = MIN(sz, sizeof(buf)); error = dump_append(di, buf, 0, nbytes); if (error) return (error); sz -= nbytes; } return (0); } int dumpsys_buf_write(struct dumperinfo *di, char *ptr, size_t sz) { size_t len; int error; while (sz) { len = di->blocksize - fragsz; if (len > sz) len = sz; memcpy((char *)di->blockbuf + fragsz, ptr, len); fragsz += len; ptr += len; sz -= len; if (fragsz == di->blocksize) { error = dump_append(di, di->blockbuf, 0, di->blocksize); if (error) return (error); fragsz = 0; } } return (0); } int dumpsys_buf_flush(struct dumperinfo *di) { int error; if (fragsz == 0) return (0); error = dump_append(di, di->blockbuf, 0, di->blocksize); fragsz = 0; return (error); } CTASSERT(PAGE_SHIFT < 20); #define PG2MB(pgs) ((pgs + (1 << (20 - PAGE_SHIFT)) - 1) >> (20 - PAGE_SHIFT)) int dumpsys_cb_dumpdata(struct dump_pa *mdp, int seqnr, void *arg) { struct dumperinfo *di = (struct dumperinfo*)arg; vm_paddr_t pa; void *va; uint64_t pgs; size_t counter, sz, chunk; int c, error; u_int maxdumppgs; error = 0; /* catch case in which chunk size is 0 */ counter = 0; /* Update twiddle every 16MB */ va = NULL; pgs = mdp->pa_size / PAGE_SIZE; pa = mdp->pa_start; maxdumppgs = min(di->maxiosize / PAGE_SIZE, MAXDUMPPGS); if (maxdumppgs == 0) /* seatbelt */ maxdumppgs = 1; printf(" chunk %d: %juMB (%ju pages)", seqnr, (uintmax_t)PG2MB(pgs), (uintmax_t)pgs); dumpsys_wbinv_all(); while (pgs) { chunk = pgs; if (chunk > maxdumppgs) chunk = maxdumppgs; sz = chunk << PAGE_SHIFT; counter += sz; if (counter >> 24) { printf(" %ju", (uintmax_t)PG2MB(pgs)); counter &= (1 << 24) - 1; } dumpsys_map_chunk(pa, chunk, &va); wdog_kern_pat(WD_LASTVAL); error = dump_append(di, va, 0, sz); dumpsys_unmap_chunk(pa, chunk, va); if (error) break; pgs -= chunk; pa += sz; /* Check for user abort. */ c = cncheckc(); if (c == 0x03) return (ECANCELED); if (c != -1) printf(" (CTRL-C to abort) "); } printf(" ... %s\n", (error) ? "fail" : "ok"); return (error); } int dumpsys_foreach_chunk(dumpsys_callback_t cb, void *arg) { struct dump_pa *mdp; int error, seqnr; seqnr = 0; mdp = dumpsys_pa_next(NULL); while (mdp != NULL) { error = (*cb)(mdp, seqnr++, arg); if (error) return (-error); mdp = dumpsys_pa_next(mdp); } return (seqnr); } static off_t fileofs; static int cb_dumphdr(struct dump_pa *mdp, int seqnr, void *arg) { struct dumperinfo *di = (struct dumperinfo*)arg; Elf_Phdr phdr; uint64_t size; int error; size = mdp->pa_size; bzero(&phdr, sizeof(phdr)); phdr.p_type = PT_LOAD; phdr.p_flags = PF_R; /* XXX */ phdr.p_offset = fileofs; #ifdef __powerpc__ phdr.p_vaddr = (do_minidump? mdp->pa_start : ~0L); phdr.p_paddr = (do_minidump? ~0L : mdp->pa_start); #else phdr.p_vaddr = mdp->pa_start; phdr.p_paddr = mdp->pa_start; #endif phdr.p_filesz = size; phdr.p_memsz = size; phdr.p_align = PAGE_SIZE; error = dumpsys_buf_write(di, (char*)&phdr, sizeof(phdr)); fileofs += phdr.p_filesz; return (error); } static int cb_size(struct dump_pa *mdp, int seqnr, void *arg) { uint64_t *sz; sz = (uint64_t *)arg; *sz += (uint64_t)mdp->pa_size; return (0); } int dumpsys_generic(struct dumperinfo *di) { static struct kerneldumpheader kdh; Elf_Ehdr ehdr; uint64_t dumpsize; off_t hdrgap; size_t hdrsz; int error; #if MINIDUMP_PAGE_TRACKING == 1 if (do_minidump) - return (minidumpsys(di)); + return (minidumpsys(di, false)); #endif bzero(&ehdr, sizeof(ehdr)); ehdr.e_ident[EI_MAG0] = ELFMAG0; ehdr.e_ident[EI_MAG1] = ELFMAG1; ehdr.e_ident[EI_MAG2] = ELFMAG2; ehdr.e_ident[EI_MAG3] = ELFMAG3; ehdr.e_ident[EI_CLASS] = ELF_CLASS; #if BYTE_ORDER == LITTLE_ENDIAN ehdr.e_ident[EI_DATA] = ELFDATA2LSB; #else ehdr.e_ident[EI_DATA] = ELFDATA2MSB; #endif ehdr.e_ident[EI_VERSION] = EV_CURRENT; ehdr.e_ident[EI_OSABI] = ELFOSABI_STANDALONE; /* XXX big picture? */ ehdr.e_type = ET_CORE; ehdr.e_machine = EM_VALUE; ehdr.e_phoff = sizeof(ehdr); ehdr.e_flags = 0; ehdr.e_ehsize = sizeof(ehdr); ehdr.e_phentsize = sizeof(Elf_Phdr); ehdr.e_shentsize = sizeof(Elf_Shdr); dumpsys_pa_init(); /* Calculate dump size. */ dumpsize = 0L; ehdr.e_phnum = dumpsys_foreach_chunk(cb_size, &dumpsize) + DUMPSYS_NUM_AUX_HDRS; hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; fileofs = MD_ALIGN(hdrsz); dumpsize += fileofs; hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize); dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize); error = dump_start(di, &kdh); if (error != 0) goto fail; printf("Dumping %ju MB (%d chunks)\n", (uintmax_t)dumpsize >> 20, ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS); /* Dump ELF header */ error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); if (error) goto fail; /* Dump program headers */ error = dumpsys_foreach_chunk(cb_dumphdr, di); if (error < 0) goto fail; error = dumpsys_write_aux_headers(di); if (error < 0) goto fail; dumpsys_buf_flush(di); /* * All headers are written using blocked I/O, so we know the * current offset is (still) block aligned. Skip the alignement * in the file to have the segment contents aligned at page * boundary. */ error = dumpsys_buf_seek(di, (size_t)hdrgap); if (error) goto fail; /* Dump memory chunks. */ error = dumpsys_foreach_chunk(dumpsys_cb_dumpdata, di); if (error < 0) goto fail; error = dump_finish(di, &kdh); if (error != 0) goto fail; printf("\nDump complete\n"); return (0); fail: if (error < 0) error = -error; if (error == ECANCELED) printf("\nDump aborted\n"); else if (error == E2BIG || error == ENOSPC) printf("\nDump failed. Partition too small.\n"); else printf("\n** DUMP FAILED (ERROR %d) **\n", error); return (error); } #if MINIDUMP_PAGE_TRACKING == 1 /* Minidump progress bar */ static struct { const int min_per; const int max_per; bool visited; } progress_track[10] = { { 0, 10, false}, { 10, 20, false}, { 20, 30, false}, { 30, 40, false}, { 40, 50, false}, { 50, 60, false}, { 60, 70, false}, { 70, 80, false}, { 80, 90, false}, { 90, 100, false} }; static uint64_t dumpsys_pb_size; static uint64_t dumpsys_pb_remaining; static uint64_t dumpsys_pb_check; /* Reset the progress bar for a dump of dumpsize. */ void dumpsys_pb_init(uint64_t dumpsize) { int i; dumpsys_pb_size = dumpsys_pb_remaining = dumpsize; dumpsys_pb_check = 0; for (i = 0; i < nitems(progress_track); i++) progress_track[i].visited = false; } /* * Update the progress according to the delta bytes that were written out. * Check and print the progress percentage. */ void dumpsys_pb_progress(size_t delta) { int sofar, i; dumpsys_pb_remaining -= delta; dumpsys_pb_check += delta; /* * To save time while dumping, only loop through progress_track * occasionally. */ if ((dumpsys_pb_check >> DUMPSYS_PB_CHECK_BITS) == 0) return; else dumpsys_pb_check &= (1 << DUMPSYS_PB_CHECK_BITS) - 1; sofar = 100 - ((dumpsys_pb_remaining * 100) / dumpsys_pb_size); for (i = 0; i < nitems(progress_track); i++) { if (sofar < progress_track[i].min_per || sofar > progress_track[i].max_per) continue; if (!progress_track[i].visited) { progress_track[i].visited = true; printf("..%d%%", sofar); } break; } } int -minidumpsys(struct dumperinfo *di) +minidumpsys(struct dumperinfo *di, bool livedump) { struct minidumpstate state; + struct msgbuf mb_copy; + char *msg_ptr; + size_t sz; int error; - state.msgbufp = msgbufp; - state.dump_bitset = vm_page_dump; + if (livedump) { + KASSERT(!dumping, ("live dump invoked from incorrect context")); + + /* + * Before invoking cpu_minidumpsys() on the live system, we + * must snapshot some required global state: the message + * buffer, and the page dump bitset. They may be modified at + * any moment, so for the sake of the live dump it is best to + * have an unchanging snapshot to work with. Both are included + * as part of the dump and consumed by userspace tools. + * + * Other global state important to the minidump code is the + * dump_avail array and the kernel's page tables, but snapshots + * are not taken of these. For one, dump_avail[] is expected + * not to change after boot. Snapshotting the kernel page + * tables would involve an additional walk, so this is avoided + * too. + * + * This means live dumps are best effort, and the result may or + * may not be usable; there are no guarantees about the + * consistency of the dump's contents. Any of the following + * (and likely more) may affect the live dump: + * + * - Data may be modified, freed, or remapped during the + * course of the dump, such that the contents written out + * are partially or entirely unrecognizable. This means + * valid references may point to destroyed/mangled objects, + * and vice versa. + * + * - The dumped context of any threads that ran during the + * dump process may be unreliable. + * + * - The set of kernel page tables included in the dump likely + * won't correspond exactly to the copy of the dump bitset. + * This means some pages will be dumped without any way to + * locate them, and some pages may not have been dumped + * despite appearing as if they should. + */ + msg_ptr = malloc(msgbufsize, M_TEMP, M_WAITOK); + msgbuf_duplicate(msgbufp, &mb_copy, msg_ptr); + state.msgbufp = &mb_copy; + + sz = BITSET_SIZE(vm_page_dump_pages); + state.dump_bitset = malloc(sz, M_TEMP, M_WAITOK); + BIT_COPY_STORE_REL(sz, vm_page_dump, state.dump_bitset); + } else { + KASSERT(dumping, ("minidump invoked outside of doadump()")); + + /* Use the globals. */ + state.msgbufp = msgbufp; + state.dump_bitset = vm_page_dump; + } error = cpu_minidumpsys(di, &state); + if (livedump) { + free(msg_ptr, M_TEMP); + free(state.dump_bitset, M_TEMP); + } return (error); } #endif /* MINIDUMP_PAGE_TRACKING == 1 */ diff --git a/sys/kern/subr_msgbuf.c b/sys/kern/subr_msgbuf.c index 980d37df205b..8af013d52a2d 100644 --- a/sys/kern/subr_msgbuf.c +++ b/sys/kern/subr_msgbuf.c @@ -1,416 +1,431 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Ian Dowse. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Generic message buffer support routines. */ #include #include #include #include #include #include #include /* * Maximum number conversion buffer length: uintmax_t in base 2, plus <> * around the priority, and a terminating NUL. */ #define MAXPRIBUF (sizeof(intmax_t) * NBBY + 3) /* Read/write sequence numbers are modulo a multiple of the buffer size. */ #define SEQMOD(size) ((size) * 16) static u_int msgbuf_cksum(struct msgbuf *mbp); /* * Timestamps in msgbuf are useful when trying to diagnose when core dumps * or other actions occurred. */ static int msgbuf_show_timestamp = 0; SYSCTL_INT(_kern, OID_AUTO, msgbuf_show_timestamp, CTLFLAG_RWTUN, &msgbuf_show_timestamp, 0, "Show timestamp in msgbuf"); /* * Initialize a message buffer of the specified size at the specified * location. This also zeros the buffer area. */ void msgbuf_init(struct msgbuf *mbp, void *ptr, int size) { mbp->msg_ptr = ptr; mbp->msg_size = size; mbp->msg_seqmod = SEQMOD(size); msgbuf_clear(mbp); mbp->msg_magic = MSG_MAGIC; mbp->msg_lastpri = -1; mbp->msg_flags = 0; bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); } /* * Reinitialize a message buffer, retaining its previous contents if * the size and checksum are correct. If the old contents cannot be * recovered, the message buffer is cleared. */ void msgbuf_reinit(struct msgbuf *mbp, void *ptr, int size) { u_int cksum; if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) { msgbuf_init(mbp, ptr, size); return; } mbp->msg_seqmod = SEQMOD(size); mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq); mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq); mbp->msg_ptr = ptr; cksum = msgbuf_cksum(mbp); if (cksum != mbp->msg_cksum) { if (bootverbose) { printf("msgbuf cksum mismatch (read %x, calc %x)\n", mbp->msg_cksum, cksum); printf("Old msgbuf not recovered\n"); } msgbuf_clear(mbp); } mbp->msg_lastpri = -1; /* Assume that the old message buffer didn't end in a newline. */ mbp->msg_flags |= MSGBUF_NEEDNL; bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); } /* * Clear the message buffer. */ void msgbuf_clear(struct msgbuf *mbp) { bzero(mbp->msg_ptr, mbp->msg_size); mbp->msg_wseq = 0; mbp->msg_rseq = 0; mbp->msg_cksum = 0; } /* * Get a count of the number of unread characters in the message buffer. */ int msgbuf_getcount(struct msgbuf *mbp) { u_int len; len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq); if (len > mbp->msg_size) len = mbp->msg_size; return (len); } /* * Add a character into the message buffer, and update the checksum and * sequence number. * * The caller should hold the message buffer spinlock. */ static void msgbuf_do_addchar(struct msgbuf * const mbp, u_int * const seq, const int c) { u_int pos; /* Make sure we properly wrap the sequence number. */ pos = MSGBUF_SEQ_TO_POS(mbp, *seq); mbp->msg_cksum += (u_int)(u_char)c - (u_int)(u_char)mbp->msg_ptr[pos]; mbp->msg_ptr[pos] = c; *seq = MSGBUF_SEQNORM(mbp, *seq + 1); } /* * Append a character to a message buffer. */ void msgbuf_addchar(struct msgbuf *mbp, int c) { mtx_lock_spin(&mbp->msg_lock); msgbuf_do_addchar(mbp, &mbp->msg_wseq, c); mtx_unlock_spin(&mbp->msg_lock); } /* * Append a NUL-terminated string with a priority to a message buffer. * Filter carriage returns if the caller requests it. * * XXX The carriage return filtering behavior is present in the * msglogchar() API, however testing has shown that we don't seem to send * carriage returns down this path. So do we still need it? */ void msgbuf_addstr(struct msgbuf *mbp, int pri, const char *str, int filter_cr) { u_int seq; size_t len, prefix_len; char prefix[MAXPRIBUF]; char buf[32]; int i, j, needtime; len = strlen(str); prefix_len = 0; /* If we have a zero-length string, no need to do anything. */ if (len == 0) return; mtx_lock_spin(&mbp->msg_lock); /* * If this is true, we may need to insert a new priority sequence, * so prepare the prefix. */ if (pri != -1) prefix_len = sprintf(prefix, "<%d>", pri); /* * Starting write sequence number. */ seq = mbp->msg_wseq; /* * Whenever there is a change in priority, we have to insert a * newline, and a priority prefix if the priority is not -1. Here * we detect whether there was a priority change, and whether we * did not end with a newline. If that is the case, we need to * insert a newline before this string. */ if (mbp->msg_lastpri != pri && (mbp->msg_flags & MSGBUF_NEEDNL) != 0) { msgbuf_do_addchar(mbp, &seq, '\n'); mbp->msg_flags &= ~MSGBUF_NEEDNL; } needtime = 1; for (i = 0; i < len; i++) { /* * If we just had a newline, and the priority is not -1 * (and therefore prefix_len != 0), then we need a priority * prefix for this line. */ if ((mbp->msg_flags & MSGBUF_NEEDNL) == 0 && prefix_len != 0) { int j; for (j = 0; j < prefix_len; j++) msgbuf_do_addchar(mbp, &seq, prefix[j]); } if (msgbuf_show_timestamp && needtime == 1 && (mbp->msg_flags & MSGBUF_NEEDNL) == 0) { snprintf(buf, sizeof(buf), "[%jd] ", (intmax_t)time_uptime); for (j = 0; buf[j] != '\0'; j++) msgbuf_do_addchar(mbp, &seq, buf[j]); needtime = 0; } /* * Don't copy carriage returns if the caller requested * filtering. * * XXX This matches the behavior of msglogchar(), but is it * necessary? Testing has shown that we don't seem to get * carriage returns here. */ if ((filter_cr != 0) && (str[i] == '\r')) continue; /* * Clear this flag if we see a newline. This affects whether * we need to insert a new prefix or insert a newline later. */ if (str[i] == '\n') mbp->msg_flags &= ~MSGBUF_NEEDNL; else mbp->msg_flags |= MSGBUF_NEEDNL; msgbuf_do_addchar(mbp, &seq, str[i]); } /* * Update the write sequence number for the actual number of * characters we put in the message buffer. (Depends on whether * carriage returns are filtered.) */ mbp->msg_wseq = seq; /* * Set the last priority. */ mbp->msg_lastpri = pri; mtx_unlock_spin(&mbp->msg_lock); } /* * Read and mark as read a character from a message buffer. * Returns the character, or -1 if no characters are available. */ int msgbuf_getchar(struct msgbuf *mbp) { u_int len, wseq; int c; mtx_lock_spin(&mbp->msg_lock); wseq = mbp->msg_wseq; len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); if (len == 0) { mtx_unlock_spin(&mbp->msg_lock); return (-1); } if (len > mbp->msg_size) mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)]; mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1); mtx_unlock_spin(&mbp->msg_lock); return (c); } /* * Read and mark as read a number of characters from a message buffer. * Returns the number of characters that were placed in `buf'. */ int msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen) { u_int len, pos, wseq; mtx_lock_spin(&mbp->msg_lock); wseq = mbp->msg_wseq; len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); if (len == 0) { mtx_unlock_spin(&mbp->msg_lock); return (0); } if (len > mbp->msg_size) { mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); len = mbp->msg_size; } pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq); len = min(len, mbp->msg_size - pos); len = min(len, (u_int)buflen); bcopy(&mbp->msg_ptr[pos], buf, len); mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len); mtx_unlock_spin(&mbp->msg_lock); return (len); } /* * Peek at the full contents of a message buffer without marking any * data as read. `seqp' should point to an unsigned integer that * msgbuf_peekbytes() can use to retain state between calls so that * the whole message buffer can be read in multiple short reads. * To initialise this variable to the start of the message buffer, * call msgbuf_peekbytes() with a NULL `buf' parameter. * * Returns the number of characters that were placed in `buf'. */ int msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp) { u_int len, pos, wseq; mtx_lock_spin(&mbp->msg_lock); if (buf == NULL) { /* Just initialise *seqp. */ *seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size); mtx_unlock_spin(&mbp->msg_lock); return (0); } wseq = mbp->msg_wseq; len = MSGBUF_SEQSUB(mbp, wseq, *seqp); if (len == 0) { mtx_unlock_spin(&mbp->msg_lock); return (0); } if (len > mbp->msg_size) { *seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); len = mbp->msg_size; } pos = MSGBUF_SEQ_TO_POS(mbp, *seqp); len = min(len, mbp->msg_size - pos); len = min(len, (u_int)buflen); bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len); *seqp = MSGBUF_SEQNORM(mbp, *seqp + len); mtx_unlock_spin(&mbp->msg_lock); return (len); } /* * Compute the checksum for the complete message buffer contents. */ static u_int msgbuf_cksum(struct msgbuf *mbp) { u_int i, sum; sum = 0; for (i = 0; i < mbp->msg_size; i++) sum += (u_char)mbp->msg_ptr[i]; return (sum); } /* * Copy from one message buffer to another. */ void msgbuf_copy(struct msgbuf *src, struct msgbuf *dst) { int c; while ((c = msgbuf_getchar(src)) >= 0) msgbuf_addchar(dst, c); } + +/* + * Get a snapshot of the message buffer, without modifying its internal state + * (i.e. don't mark any new characters as read). + */ +void +msgbuf_duplicate(struct msgbuf *src, struct msgbuf *dst, char *dst_msgptr) +{ + + mtx_lock_spin(&src->msg_lock); + bcopy(src, dst, sizeof(struct msgbuf)); + dst->msg_ptr = dst_msgptr; + bcopy(src->msg_ptr, dst->msg_ptr, src->msg_size); + mtx_unlock_spin(&src->msg_lock); +} diff --git a/sys/mips/mips/minidump_machdep.c b/sys/mips/mips/minidump_machdep.c index abe45e999f13..cbf9a83395a6 100644 --- a/sys/mips/mips/minidump_machdep.c +++ b/sys/mips/mips/minidump_machdep.c @@ -1,281 +1,285 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010 Oleksandr Tymoshenko * Copyright (c) 2008 Semihalf, Grzegorz Bernacki * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: FreeBSD: src/sys/arm/arm/minidump_machdep.c v214223 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(sizeof(struct kerneldumpheader) == 512); static struct kerneldumpheader kdh; /* Handle chunked writes. */ static uint64_t dumpsize; /* Just auxiliary bufffer */ static char tmpbuffer[PAGE_SIZE] __aligned(sizeof(uint64_t)); extern pd_entry_t *kernel_segmap; static int write_buffer(struct dumperinfo *di, char *ptr, size_t sz) { size_t len; int error, c; u_int maxdumpsz; maxdumpsz = di->maxiosize; if (maxdumpsz == 0) /* seatbelt */ maxdumpsz = PAGE_SIZE; error = 0; while (sz) { len = min(maxdumpsz, sz); dumpsys_pb_progress(len); wdog_kern_pat(WD_LASTVAL); if (ptr) { error = dump_append(di, ptr, 0, len); if (error) return (error); ptr += len; sz -= len; } else { panic("pa is not supported"); } /* Check for user abort. */ c = cncheckc(); if (c == 0x03) return (ECANCELED); if (c != -1) printf(" (CTRL-C to abort) "); } return (0); } int cpu_minidumpsys(struct dumperinfo *di, const struct minidumpstate *state) { struct minidumphdr mdhdr; struct msgbuf *mbp; uint64_t *dump_avail_buf; uint32_t ptesize; vm_paddr_t pa; vm_offset_t prev_pte = 0; uint32_t count = 0; vm_offset_t va; pt_entry_t *pte; int i, error; void *dump_va; + /* Live dumps are untested. */ + if (!dumping) + return (EOPNOTSUPP); + /* Flush cache */ mips_dcache_wbinv_all(); /* Walk page table pages, set bits in vm_page_dump */ ptesize = 0; for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { ptesize += PAGE_SIZE; pte = pmap_pte(kernel_pmap, va); KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); for (i = 0; i < NPTEPG; i++) { if (pte_test(&pte[i], PTE_V)) { pa = TLBLO_PTE_TO_PA(pte[i]); if (vm_phys_is_dumpable(pa)) vm_page_dump_add(state->dump_bitset, pa); } } } /* * Now mark pages from 0 to phys_avail[0], that's where kernel * and pages allocated by pmap_steal reside */ for (pa = 0; pa < phys_avail[0]; pa += PAGE_SIZE) { if (vm_phys_is_dumpable(pa)) vm_page_dump_add(state->dump_bitset, pa); } /* Calculate dump size. */ mbp = state->msgbufp; dumpsize = ptesize; dumpsize += round_page(mbp->msg_size); dumpsize += round_page(nitems(dump_avail) * sizeof(uint64_t)); dumpsize += round_page(BITSET_SIZE(vm_page_dump_pages)); VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) { /* Clear out undumpable pages now if needed */ if (vm_phys_is_dumpable(pa)) dumpsize += PAGE_SIZE; else vm_page_dump_drop(state->dump_bitset, pa); } dumpsize += PAGE_SIZE; dumpsys_pb_init(dumpsize); /* Initialize mdhdr */ bzero(&mdhdr, sizeof(mdhdr)); strcpy(mdhdr.magic, MINIDUMP_MAGIC); mdhdr.version = MINIDUMP_VERSION; mdhdr.msgbufsize = mbp->msg_size; mdhdr.bitmapsize = round_page(BITSET_SIZE(vm_page_dump_pages)); mdhdr.ptesize = ptesize; mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; mdhdr.dumpavailsize = round_page(nitems(dump_avail) * sizeof(uint64_t)); dump_init_header(di, &kdh, KERNELDUMPMAGIC, KERNELDUMP_MIPS_VERSION, dumpsize); error = dump_start(di, &kdh); if (error != 0) goto fail; printf("Dumping %llu out of %ju MB:", (long long)dumpsize >> 20, ptoa((uintmax_t)physmem) / 1048576); /* Dump my header */ bzero(tmpbuffer, sizeof(tmpbuffer)); bcopy(&mdhdr, tmpbuffer, sizeof(mdhdr)); error = write_buffer(di, tmpbuffer, PAGE_SIZE); if (error) goto fail; /* Dump msgbuf up front */ error = write_buffer(di, mbp->msg_ptr, round_page(mbp->msg_size)); if (error) goto fail; /* Dump dump_avail. Make a copy using 64-bit physical addresses. */ _Static_assert(nitems(dump_avail) * sizeof(uint64_t) <= sizeof(tmpbuffer), "Large dump_avail not handled"); bzero(tmpbuffer, sizeof(tmpbuffer)); if (sizeof(dump_avail[0]) != sizeof(uint64_t)) { dump_avail_buf = (uint64_t *)tmpbuffer; for (i = 0; dump_avail[i] != 0 || dump_avail[i + 1] != 0; i++) { dump_avail_buf[i] = dump_avail[i]; dump_avail_buf[i + 1] = dump_avail[i + 1]; } } else { memcpy(tmpbuffer, dump_avail, sizeof(dump_avail)); } error = write_buffer(di, tmpbuffer, PAGE_SIZE); if (error) goto fail; /* Dump bitmap */ error = write_buffer(di, (char *)vm_page_dump, round_page(BITSET_SIZE(vm_page_dump_pages))); if (error) goto fail; /* Dump kernel page table pages */ for (va = VM_MIN_KERNEL_ADDRESS; va < kernel_vm_end; va += NBPDR) { pte = pmap_pte(kernel_pmap, va); KASSERT(pte != NULL, ("pte for %jx is NULL", (uintmax_t)va)); if (!count) { prev_pte = (vm_offset_t)pte; count++; } else { if ((vm_offset_t)pte == (prev_pte + count * PAGE_SIZE)) count++; else { error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE); if (error) goto fail; count = 1; prev_pte = (vm_offset_t)pte; } } } if (count) { error = write_buffer(di, (char*)prev_pte, count * PAGE_SIZE); if (error) goto fail; count = 0; prev_pte = 0; } /* Dump memory chunks page by page */ VM_PAGE_DUMP_FOREACH(state->dump_bitset, pa) { dump_va = pmap_kenter_temporary(pa, 0); error = write_buffer(di, dump_va, PAGE_SIZE); if (error) goto fail; pmap_kenter_temporary_free(pa); } error = dump_finish(di, &kdh); if (error != 0) goto fail; printf("\nDump complete\n"); return (0); fail: if (error < 0) error = -error; if (error == ECANCELED) printf("\nDump aborted\n"); else if (error == E2BIG || error == ENOSPC) { printf("\nDump failed. Partition too small (about %lluMB were " "needed this time).\n", (long long)dumpsize >> 20); } else printf("\n** DUMP FAILED (ERROR %d) **\n", error); return (error); } diff --git a/sys/sys/kerneldump.h b/sys/sys/kerneldump.h index 54662d9cff39..c293491eadc9 100644 --- a/sys/sys/kerneldump.h +++ b/sys/sys/kerneldump.h @@ -1,167 +1,167 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Poul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Poul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_KERNELDUMP_H #define _SYS_KERNELDUMP_H #include #include #include #if BYTE_ORDER == LITTLE_ENDIAN #define dtoh32(x) __bswap32(x) #define dtoh64(x) __bswap64(x) #define htod32(x) __bswap32(x) #define htod64(x) __bswap64(x) #elif BYTE_ORDER == BIG_ENDIAN #define dtoh32(x) (x) #define dtoh64(x) (x) #define htod32(x) (x) #define htod64(x) (x) #endif #define KERNELDUMP_COMP_NONE 0 #define KERNELDUMP_COMP_GZIP 1 #define KERNELDUMP_COMP_ZSTD 2 #define KERNELDUMP_ENC_NONE 0 #define KERNELDUMP_ENC_AES_256_CBC 1 #define KERNELDUMP_ENC_CHACHA20 2 #define KERNELDUMP_BUFFER_SIZE 4096 #define KERNELDUMP_IV_MAX_SIZE 32 #define KERNELDUMP_KEY_MAX_SIZE 64 #define KERNELDUMP_ENCKEY_MAX_SIZE (16384 / 8) /* * All uintX_t fields are in dump byte order, which is the same as * network byte order. Use the macros defined above to read or * write the fields. */ struct kerneldumpheader { char magic[20]; #define KERNELDUMPMAGIC "FreeBSD Kernel Dump" #define TEXTDUMPMAGIC "FreeBSD Text Dump" #define KERNELDUMPMAGIC_CLEARED "Cleared Kernel Dump" char architecture[12]; uint32_t version; #define KERNELDUMPVERSION 4 #define KERNELDUMP_TEXT_VERSION 4 uint32_t architectureversion; #define KERNELDUMP_AARCH64_VERSION 1 #define KERNELDUMP_AMD64_VERSION 2 #define KERNELDUMP_ARM_VERSION 1 #define KERNELDUMP_I386_VERSION 2 #define KERNELDUMP_MIPS_VERSION 1 #define KERNELDUMP_POWERPC_VERSION 1 #define KERNELDUMP_RISCV_VERSION 1 #define KERNELDUMP_SPARC64_VERSION 1 uint64_t dumplength; /* excl headers */ uint64_t dumptime; uint32_t dumpkeysize; uint32_t blocksize; char hostname[64]; char versionstring[192]; char panicstring[175]; uint8_t compression; uint64_t dumpextent; char unused[4]; uint32_t parity; }; struct kerneldumpkey { uint8_t kdk_encryption; uint8_t kdk_iv[KERNELDUMP_IV_MAX_SIZE]; uint32_t kdk_encryptedkeysize; uint8_t kdk_encryptedkey[]; } __packed; /* * Parity calculation is endian insensitive. */ static __inline u_int32_t kerneldump_parity(struct kerneldumpheader *kdhp) { uint32_t *up, parity; u_int i; up = (uint32_t *)kdhp; parity = 0; for (i = 0; i < sizeof *kdhp; i += sizeof *up) parity ^= *up++; return (parity); } #ifdef _KERNEL struct dump_pa { vm_paddr_t pa_start; vm_paddr_t pa_size; }; struct minidumpstate { struct msgbuf *msgbufp; struct bitset *dump_bitset; }; -int minidumpsys(struct dumperinfo *); +int minidumpsys(struct dumperinfo *, bool); int dumpsys_generic(struct dumperinfo *); void dumpsys_map_chunk(vm_paddr_t, size_t, void **); typedef int dumpsys_callback_t(struct dump_pa *, int, void *); int dumpsys_foreach_chunk(dumpsys_callback_t, void *); int dumpsys_cb_dumpdata(struct dump_pa *, int, void *); int dumpsys_buf_seek(struct dumperinfo *, size_t); int dumpsys_buf_write(struct dumperinfo *, char *, size_t); int dumpsys_buf_flush(struct dumperinfo *); void dumpsys_gen_pa_init(void); struct dump_pa *dumpsys_gen_pa_next(struct dump_pa *); void dumpsys_gen_wbinv_all(void); void dumpsys_gen_unmap_chunk(vm_paddr_t, size_t, void *); int dumpsys_gen_write_aux_headers(struct dumperinfo *); void dumpsys_pb_init(uint64_t); void dumpsys_pb_progress(size_t); extern int do_minidump; #endif #endif /* _SYS_KERNELDUMP_H */ diff --git a/sys/sys/msgbuf.h b/sys/sys/msgbuf.h index df61f130e46f..27aba1a8e0ed 100644 --- a/sys/sys/msgbuf.h +++ b/sys/sys/msgbuf.h @@ -1,87 +1,88 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1981, 1984, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)msgbuf.h 8.1 (Berkeley) 6/2/93 * $FreeBSD$ */ #ifndef _SYS_MSGBUF_H_ #define _SYS_MSGBUF_H_ #include #include struct msgbuf { char *msg_ptr; /* pointer to buffer */ #define MSG_MAGIC 0x063062 u_int msg_magic; u_int msg_size; /* size of buffer area */ u_int msg_wseq; /* write sequence number */ u_int msg_rseq; /* read sequence number */ u_int msg_cksum; /* checksum of contents */ u_int msg_seqmod; /* range for sequence numbers */ int msg_lastpri; /* saved priority value */ u_int msg_flags; #define MSGBUF_NEEDNL 0x01 /* set when newline needed */ struct mtx msg_lock; /* mutex to protect the buffer */ }; /* Normalise a sequence number or a difference between sequence numbers. */ #define MSGBUF_SEQNORM(mbp, seq) (((seq) + (mbp)->msg_seqmod) % \ (mbp)->msg_seqmod) #define MSGBUF_SEQ_TO_POS(mbp, seq) ((seq) % (mbp)->msg_size) /* Subtract sequence numbers. Note that only positive values result. */ #define MSGBUF_SEQSUB(mbp, seq1, seq2) (MSGBUF_SEQNORM((mbp), (seq1) - (seq2))) #ifdef _KERNEL extern int msgbufsize; extern int msgbuftrigger; extern struct msgbuf *msgbufp; extern struct mtx msgbuf_lock; void msgbufinit(void *ptr, int size); void msgbuf_addchar(struct msgbuf *mbp, int c); void msgbuf_addstr(struct msgbuf *mbp, int pri, const char *str, int filter_cr); void msgbuf_clear(struct msgbuf *mbp); void msgbuf_copy(struct msgbuf *src, struct msgbuf *dst); int msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen); int msgbuf_getchar(struct msgbuf *mbp); int msgbuf_getcount(struct msgbuf *mbp); void msgbuf_init(struct msgbuf *mbp, void *ptr, int size); int msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp); void msgbuf_reinit(struct msgbuf *mbp, void *ptr, int size); +void msgbuf_duplicate(struct msgbuf *src, struct msgbuf *dst, char *msgptr); #ifndef MSGBUF_SIZE #define MSGBUF_SIZE (32768 * 3) #endif #endif /* KERNEL */ #endif /* !_SYS_MSGBUF_H_ */