1618c7db3SRobert Watson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3753c4e83SPedro F. Giffuni *
4618c7db3SRobert Watson * Copyright (c) 2007 Robert N. M. Watson
5618c7db3SRobert Watson * All rights reserved.
6618c7db3SRobert Watson *
7618c7db3SRobert Watson * Redistribution and use in source and binary forms, with or without
8618c7db3SRobert Watson * modification, are permitted provided that the following conditions
9618c7db3SRobert Watson * are met:
10618c7db3SRobert Watson * 1. Redistributions of source code must retain the above copyright
11618c7db3SRobert Watson * notice, this list of conditions and the following disclaimer.
12618c7db3SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
13618c7db3SRobert Watson * notice, this list of conditions and the following disclaimer in the
14618c7db3SRobert Watson * documentation and/or other materials provided with the distribution.
15618c7db3SRobert Watson *
16618c7db3SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17618c7db3SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18618c7db3SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19618c7db3SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20618c7db3SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21618c7db3SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22618c7db3SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23618c7db3SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24618c7db3SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25618c7db3SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26618c7db3SRobert Watson * SUCH DAMAGE.
27618c7db3SRobert Watson */
28618c7db3SRobert Watson
29618c7db3SRobert Watson /*-
309b0fce60SRobert Watson * Kernel text-dump support: write a series of text files to the dump
319b0fce60SRobert Watson * partition for later recovery, including captured DDB output, kernel
329b0fce60SRobert Watson * configuration, message buffer, and panic message. This allows for a more
339b0fce60SRobert Watson * compact representation of critical debugging information than traditional
349b0fce60SRobert Watson * binary dumps, as well as allowing dump information to be used without
359b0fce60SRobert Watson * access to kernel symbols, source code, etc.
36618c7db3SRobert Watson *
37618c7db3SRobert Watson * Storage Layout
38618c7db3SRobert Watson * --------------
39618c7db3SRobert Watson *
40618c7db3SRobert Watson * Crash dumps are aligned to the end of the dump or swap partition in order
41618c7db3SRobert Watson * to minimize the chances of swap duing fsck eating into the dump. However,
42618c7db3SRobert Watson * unlike a memory dump, we don't know the size of the textdump a priori, so
43618c7db3SRobert Watson * can't just write it out sequentially in order from a known starting point
44618c7db3SRobert Watson * calculated with respect to the end of the partition. In order to address
45618c7db3SRobert Watson * this, we actually write out the textdump in reverse block order, allowing
46618c7db3SRobert Watson * us to directly align it to the end of the partition and then write out the
47618c7db3SRobert Watson * dump header and trailer before and after it once done. savecore(8) must
48618c7db3SRobert Watson * know to reverse the order of the blocks in order to produce a readable
49618c7db3SRobert Watson * file.
50618c7db3SRobert Watson *
519b0fce60SRobert Watson * Data is written out in the ustar file format so that we can write data
529b0fce60SRobert Watson * incrementally as a stream without reference to previous files.
53618c7db3SRobert Watson *
54618c7db3SRobert Watson * TODO
55618c7db3SRobert Watson * ----
56618c7db3SRobert Watson *
57ad29e12eSPedro F. Giffuni * - Allow subsystems to register to submit files for inclusion in the text
58618c7db3SRobert Watson * dump in a generic way.
59618c7db3SRobert Watson */
60618c7db3SRobert Watson
61618c7db3SRobert Watson #include <sys/cdefs.h>
62618c7db3SRobert Watson #include "opt_config.h"
63618c7db3SRobert Watson
6421d748a9SAlfred Perlstein #include "opt_ddb.h"
6521d748a9SAlfred Perlstein
66618c7db3SRobert Watson #include <sys/param.h>
67618c7db3SRobert Watson #include <sys/conf.h>
68618c7db3SRobert Watson #include <sys/kernel.h>
69618c7db3SRobert Watson #include <sys/kerneldump.h>
70618c7db3SRobert Watson #include <sys/msgbuf.h>
71618c7db3SRobert Watson #include <sys/sysctl.h>
72618c7db3SRobert Watson #include <sys/systm.h>
73618c7db3SRobert Watson
74618c7db3SRobert Watson #include <ddb/ddb.h>
75618c7db3SRobert Watson #include <ddb/db_lex.h>
76618c7db3SRobert Watson
777029da5cSPawel Biernacki static SYSCTL_NODE(_debug_ddb, OID_AUTO, textdump,
787029da5cSPawel Biernacki CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
79618c7db3SRobert Watson "DDB textdump options");
80618c7db3SRobert Watson
81618c7db3SRobert Watson /*
82618c7db3SRobert Watson * Don't touch the first SIZEOF_METADATA bytes on the dump device. This is
83618c7db3SRobert Watson * to protect us from metadata and metadata from us.
84618c7db3SRobert Watson */
85618c7db3SRobert Watson #define SIZEOF_METADATA (64*1024)
86618c7db3SRobert Watson
87618c7db3SRobert Watson /*
88618c7db3SRobert Watson * Data is written out as a series of files in the ustar tar format. ustar
89618c7db3SRobert Watson * is a simple streamed format consiting of a series of files prefixed with
90618c7db3SRobert Watson * headers, and all padded to 512-byte block boundaries, which maps
91618c7db3SRobert Watson * conveniently to our requirements.
92618c7db3SRobert Watson */
93618c7db3SRobert Watson struct ustar_header {
94618c7db3SRobert Watson char uh_filename[100];
95618c7db3SRobert Watson char uh_mode[8];
96618c7db3SRobert Watson char uh_tar_owner[8];
97618c7db3SRobert Watson char uh_tar_group[8];
98618c7db3SRobert Watson char uh_size[12];
99618c7db3SRobert Watson char uh_mtime[12];
100618c7db3SRobert Watson char uh_sum[8];
101618c7db3SRobert Watson char uh_type;
102618c7db3SRobert Watson char uh_linkfile[100];
103618c7db3SRobert Watson char uh_ustar[6];
104618c7db3SRobert Watson char uh_version[2];
105618c7db3SRobert Watson char uh_owner[32];
106618c7db3SRobert Watson char uh_group[32];
107618c7db3SRobert Watson char uh_major[8];
108618c7db3SRobert Watson char uh_minor[8];
109618c7db3SRobert Watson char uh_filenameprefix[155];
110618c7db3SRobert Watson char uh_zeropad[12];
111618c7db3SRobert Watson } __packed;
112618c7db3SRobert Watson
113618c7db3SRobert Watson /*
114618c7db3SRobert Watson * Various size assertions -- pretty much everything must be one block in
115618c7db3SRobert Watson * size.
116618c7db3SRobert Watson */
117618c7db3SRobert Watson CTASSERT(sizeof(struct kerneldumpheader) == TEXTDUMP_BLOCKSIZE);
118618c7db3SRobert Watson CTASSERT(sizeof(struct ustar_header) == TEXTDUMP_BLOCKSIZE);
119618c7db3SRobert Watson
120618c7db3SRobert Watson /*
121618c7db3SRobert Watson * Is a textdump scheduled? If so, the shutdown code will invoke our dumpsys
122618c7db3SRobert Watson * routine instead of the machine-dependent kernel dump routine.
123618c7db3SRobert Watson */
12421d748a9SAlfred Perlstein #ifdef TEXTDUMP_PREFERRED
12521d748a9SAlfred Perlstein int textdump_pending = 1;
12621d748a9SAlfred Perlstein #else
12721d748a9SAlfred Perlstein int textdump_pending = 0;
12821d748a9SAlfred Perlstein #endif
129618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, pending, CTLFLAG_RW,
130618c7db3SRobert Watson &textdump_pending, 0,
131618c7db3SRobert Watson "Perform textdump instead of regular kernel dump.");
132618c7db3SRobert Watson
133618c7db3SRobert Watson /*
134618c7db3SRobert Watson * Various constants for tar headers and contents.
135618c7db3SRobert Watson */
136618c7db3SRobert Watson #define TAR_USER "root"
137618c7db3SRobert Watson #define TAR_GROUP "wheel"
138618c7db3SRobert Watson #define TAR_UID "0"
139618c7db3SRobert Watson #define TAR_GID "0"
140618c7db3SRobert Watson #define TAR_MODE "0600"
141618c7db3SRobert Watson #define TAR_USTAR "ustar"
142618c7db3SRobert Watson
143618c7db3SRobert Watson #define TAR_CONFIG_FILENAME "config.txt" /* Kernel configuration. */
144*4133afcdSJose Luis Duran #define TAR_MSGBUF_FILENAME "msgbuf.txt" /* Kernel message buffer. */
145618c7db3SRobert Watson #define TAR_PANIC_FILENAME "panic.txt" /* Panic message. */
146618c7db3SRobert Watson #define TAR_VERSION_FILENAME "version.txt" /* Kernel version. */
147618c7db3SRobert Watson
148618c7db3SRobert Watson /*
149618c7db3SRobert Watson * Configure which files will be dumped.
150618c7db3SRobert Watson */
151618c7db3SRobert Watson #ifdef INCLUDE_CONFIG_FILE
152618c7db3SRobert Watson static int textdump_do_config = 1;
153618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_config, CTLFLAG_RW,
154618c7db3SRobert Watson &textdump_do_config, 0, "Dump kernel configuration in textdump");
155618c7db3SRobert Watson #endif
156618c7db3SRobert Watson
157618c7db3SRobert Watson static int textdump_do_ddb = 1;
158618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_ddb, CTLFLAG_RW,
159618c7db3SRobert Watson &textdump_do_ddb, 0, "Dump DDB captured output in textdump");
160618c7db3SRobert Watson
161618c7db3SRobert Watson static int textdump_do_msgbuf = 1;
162618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_msgbuf, CTLFLAG_RW,
163618c7db3SRobert Watson &textdump_do_msgbuf, 0, "Dump kernel message buffer in textdump");
164618c7db3SRobert Watson
165618c7db3SRobert Watson static int textdump_do_panic = 1;
166618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_panic, CTLFLAG_RW,
167618c7db3SRobert Watson &textdump_do_panic, 0, "Dump kernel panic message in textdump");
168618c7db3SRobert Watson
169618c7db3SRobert Watson static int textdump_do_version = 1;
170618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_version, CTLFLAG_RW,
171618c7db3SRobert Watson &textdump_do_version, 0, "Dump kernel version string in textdump");
172618c7db3SRobert Watson
173618c7db3SRobert Watson /*
174618c7db3SRobert Watson * State related to incremental writing of blocks to disk.
175618c7db3SRobert Watson */
176618c7db3SRobert Watson static off_t textdump_offset; /* Offset of next sequential write. */
177618c7db3SRobert Watson static int textdump_error; /* Carried write error, if any. */
178618c7db3SRobert Watson
179618c7db3SRobert Watson /*
180618c7db3SRobert Watson * Statically allocate space to prepare block-sized headers and data.
181618c7db3SRobert Watson */
182618c7db3SRobert Watson char textdump_block_buffer[TEXTDUMP_BLOCKSIZE];
183618c7db3SRobert Watson static struct kerneldumpheader kdh;
184618c7db3SRobert Watson
185618c7db3SRobert Watson /*
1869b0fce60SRobert Watson * Calculate and fill in the checksum for a ustar header.
187618c7db3SRobert Watson */
188618c7db3SRobert Watson static void
ustar_checksum(struct ustar_header * uhp)189618c7db3SRobert Watson ustar_checksum(struct ustar_header *uhp)
190618c7db3SRobert Watson {
191618c7db3SRobert Watson u_int sum;
192618c7db3SRobert Watson int i;
193618c7db3SRobert Watson
194618c7db3SRobert Watson for (i = 0; i < sizeof(uhp->uh_sum); i++)
195618c7db3SRobert Watson uhp->uh_sum[i] = ' ';
196618c7db3SRobert Watson sum = 0;
197618c7db3SRobert Watson for (i = 0; i < sizeof(*uhp); i++)
198618c7db3SRobert Watson sum += ((u_char *)uhp)[i];
199618c7db3SRobert Watson snprintf(uhp->uh_sum, sizeof(uhp->uh_sum), "%6o", sum);
200618c7db3SRobert Watson }
201618c7db3SRobert Watson
202618c7db3SRobert Watson /*
203618c7db3SRobert Watson * Each file in the tarball has a block-sized header with its name and other,
204618c7db3SRobert Watson * largely hard-coded, properties.
205618c7db3SRobert Watson */
206618c7db3SRobert Watson void
textdump_mkustar(char * block_buffer,const char * filename,u_int size)207618c7db3SRobert Watson textdump_mkustar(char *block_buffer, const char *filename, u_int size)
208618c7db3SRobert Watson {
209618c7db3SRobert Watson struct ustar_header *uhp;
210618c7db3SRobert Watson
21121d748a9SAlfred Perlstein #ifdef TEXTDUMP_VERBOSE
21221d748a9SAlfred Perlstein if (textdump_error == 0)
21321d748a9SAlfred Perlstein printf("textdump: creating '%s'.\n", filename);
21421d748a9SAlfred Perlstein #endif
215618c7db3SRobert Watson uhp = (struct ustar_header *)block_buffer;
216618c7db3SRobert Watson bzero(uhp, sizeof(*uhp));
217618c7db3SRobert Watson strlcpy(uhp->uh_filename, filename, sizeof(uhp->uh_filename));
218618c7db3SRobert Watson strlcpy(uhp->uh_mode, TAR_MODE, sizeof(uhp->uh_mode));
219618c7db3SRobert Watson snprintf(uhp->uh_size, sizeof(uhp->uh_size), "%o", size);
220618c7db3SRobert Watson strlcpy(uhp->uh_tar_owner, TAR_UID, sizeof(uhp->uh_tar_owner));
221618c7db3SRobert Watson strlcpy(uhp->uh_tar_group, TAR_GID, sizeof(uhp->uh_tar_group));
222618c7db3SRobert Watson strlcpy(uhp->uh_owner, TAR_USER, sizeof(uhp->uh_owner));
223618c7db3SRobert Watson strlcpy(uhp->uh_group, TAR_GROUP, sizeof(uhp->uh_group));
224618c7db3SRobert Watson snprintf(uhp->uh_mtime, sizeof(uhp->uh_mtime), "%lo",
225618c7db3SRobert Watson (unsigned long)time_second);
226618c7db3SRobert Watson uhp->uh_type = 0;
227618c7db3SRobert Watson strlcpy(uhp->uh_ustar, TAR_USTAR, sizeof(uhp->uh_ustar));
228618c7db3SRobert Watson ustar_checksum(uhp);
229618c7db3SRobert Watson }
230618c7db3SRobert Watson
231618c7db3SRobert Watson /*
232618c7db3SRobert Watson * textdump_writeblock() writes TEXTDUMP_BLOCKSIZE-sized blocks of data to
233618c7db3SRobert Watson * the space between di->mediaoffset and di->mediaoffset + di->mediasize. It
234618c7db3SRobert Watson * accepts an offset relative to di->mediaoffset. If we're carrying any
235618c7db3SRobert Watson * error from previous I/O, return that error and don't continue to try to
236618c7db3SRobert Watson * write. Most writers ignore the error and forge ahead on the basis that
237618c7db3SRobert Watson * there's not much you can do.
238618c7db3SRobert Watson */
239618c7db3SRobert Watson static int
textdump_writeblock(struct dumperinfo * di,off_t offset,char * buffer)240618c7db3SRobert Watson textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer)
241618c7db3SRobert Watson {
242618c7db3SRobert Watson
243618c7db3SRobert Watson if (textdump_error)
244618c7db3SRobert Watson return (textdump_error);
245618c7db3SRobert Watson if (offset + TEXTDUMP_BLOCKSIZE > di->mediasize)
246618c7db3SRobert Watson return (EIO);
247618c7db3SRobert Watson if (offset < SIZEOF_METADATA)
248618c7db3SRobert Watson return (ENOSPC);
249db71383bSMitchell Horne textdump_error = dump_write(di, buffer, offset + di->mediaoffset,
250990132f0SRobert Watson TEXTDUMP_BLOCKSIZE);
25121d748a9SAlfred Perlstein if (textdump_error)
25221d748a9SAlfred Perlstein printf("textdump_writeblock: offset %jd, error %d\n", (intmax_t)offset,
25321d748a9SAlfred Perlstein textdump_error);
254618c7db3SRobert Watson return (textdump_error);
255618c7db3SRobert Watson }
256618c7db3SRobert Watson
257618c7db3SRobert Watson /*
258618c7db3SRobert Watson * Interfaces to save and restore the dump offset, so that printers can go
259618c7db3SRobert Watson * back to rewrite a header if required, while avoiding their knowing about
260618c7db3SRobert Watson * the global layout of the blocks.
2619b0fce60SRobert Watson *
2629b0fce60SRobert Watson * If we ever want to support writing textdumps to tape or other
2639b0fce60SRobert Watson * stream-oriented target, we'll need to remove this.
264618c7db3SRobert Watson */
265618c7db3SRobert Watson void
textdump_saveoff(off_t * offsetp)266618c7db3SRobert Watson textdump_saveoff(off_t *offsetp)
267618c7db3SRobert Watson {
268618c7db3SRobert Watson
269618c7db3SRobert Watson *offsetp = textdump_offset;
270618c7db3SRobert Watson }
271618c7db3SRobert Watson
272618c7db3SRobert Watson void
textdump_restoreoff(off_t offset)273618c7db3SRobert Watson textdump_restoreoff(off_t offset)
274618c7db3SRobert Watson {
275618c7db3SRobert Watson
276618c7db3SRobert Watson textdump_offset = offset;
277618c7db3SRobert Watson }
278618c7db3SRobert Watson
279618c7db3SRobert Watson /*
280618c7db3SRobert Watson * Interface to write the "next block" relative to the current offset; since
281618c7db3SRobert Watson * we write backwards from the end of the partition, we subtract, but there's
282618c7db3SRobert Watson * no reason for the caller to know this.
283618c7db3SRobert Watson */
284618c7db3SRobert Watson int
textdump_writenextblock(struct dumperinfo * di,char * buffer)285618c7db3SRobert Watson textdump_writenextblock(struct dumperinfo *di, char *buffer)
286618c7db3SRobert Watson {
287618c7db3SRobert Watson int error;
288618c7db3SRobert Watson
289618c7db3SRobert Watson error = textdump_writeblock(di, textdump_offset, buffer);
290618c7db3SRobert Watson textdump_offset -= TEXTDUMP_BLOCKSIZE;
291618c7db3SRobert Watson return (error);
292618c7db3SRobert Watson }
293618c7db3SRobert Watson
294618c7db3SRobert Watson #ifdef INCLUDE_CONFIG_FILE
295618c7db3SRobert Watson extern char kernconfstring[];
296618c7db3SRobert Watson
297618c7db3SRobert Watson /*
298618c7db3SRobert Watson * Dump kernel configuration.
299618c7db3SRobert Watson */
300618c7db3SRobert Watson static void
textdump_dump_config(struct dumperinfo * di)301618c7db3SRobert Watson textdump_dump_config(struct dumperinfo *di)
302618c7db3SRobert Watson {
303618c7db3SRobert Watson u_int count, fullblocks, len;
304618c7db3SRobert Watson
305618c7db3SRobert Watson len = strlen(kernconfstring);
306618c7db3SRobert Watson textdump_mkustar(textdump_block_buffer, TAR_CONFIG_FILENAME, len);
307618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
308618c7db3SRobert Watson
309618c7db3SRobert Watson /*
310618c7db3SRobert Watson * Write out all full blocks directly from the string, and handle any
311618c7db3SRobert Watson * left-over bits by copying it to out to the local buffer and
312618c7db3SRobert Watson * zero-padding it.
313618c7db3SRobert Watson */
314618c7db3SRobert Watson fullblocks = len / TEXTDUMP_BLOCKSIZE;
315618c7db3SRobert Watson for (count = 0; count < fullblocks; count++)
316618c7db3SRobert Watson (void)textdump_writenextblock(di, kernconfstring + count *
317618c7db3SRobert Watson TEXTDUMP_BLOCKSIZE);
318618c7db3SRobert Watson if (len % TEXTDUMP_BLOCKSIZE != 0) {
319618c7db3SRobert Watson bzero(textdump_block_buffer, TEXTDUMP_BLOCKSIZE);
320618c7db3SRobert Watson bcopy(kernconfstring + count * TEXTDUMP_BLOCKSIZE,
321618c7db3SRobert Watson textdump_block_buffer, len % TEXTDUMP_BLOCKSIZE);
322618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
323618c7db3SRobert Watson }
324618c7db3SRobert Watson }
325618c7db3SRobert Watson #endif /* INCLUDE_CONFIG_FILE */
326618c7db3SRobert Watson
327618c7db3SRobert Watson /*
328618c7db3SRobert Watson * Dump kernel message buffer.
329618c7db3SRobert Watson */
330618c7db3SRobert Watson static void
textdump_dump_msgbuf(struct dumperinfo * di)331618c7db3SRobert Watson textdump_dump_msgbuf(struct dumperinfo *di)
332618c7db3SRobert Watson {
333618c7db3SRobert Watson off_t end_offset, tarhdr_offset;
334618c7db3SRobert Watson u_int i, len, offset, seq, total_len;
335618c7db3SRobert Watson char buf[16];
336618c7db3SRobert Watson
337618c7db3SRobert Watson /*
338618c7db3SRobert Watson * Write out a dummy tar header to advance the offset; we'll rewrite
339618c7db3SRobert Watson * it later once we know the true size.
340618c7db3SRobert Watson */
341618c7db3SRobert Watson textdump_saveoff(&tarhdr_offset);
342618c7db3SRobert Watson textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME, 0);
343618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
344618c7db3SRobert Watson
345618c7db3SRobert Watson /*
346618c7db3SRobert Watson * Copy out the data in small chunks, but don't copy nuls that may be
347618c7db3SRobert Watson * present if the message buffer has not yet completely filled at
348618c7db3SRobert Watson * least once.
349618c7db3SRobert Watson */
350618c7db3SRobert Watson total_len = 0;
351618c7db3SRobert Watson offset = 0;
352618c7db3SRobert Watson msgbuf_peekbytes(msgbufp, NULL, 0, &seq);
353618c7db3SRobert Watson while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) {
354618c7db3SRobert Watson for (i = 0; i < len; i++) {
355618c7db3SRobert Watson if (buf[i] == '\0')
356618c7db3SRobert Watson continue;
357618c7db3SRobert Watson textdump_block_buffer[offset] = buf[i];
358618c7db3SRobert Watson offset++;
359618c7db3SRobert Watson if (offset != sizeof(textdump_block_buffer))
360618c7db3SRobert Watson continue;
361618c7db3SRobert Watson (void)textdump_writenextblock(di,
362618c7db3SRobert Watson textdump_block_buffer);
363618c7db3SRobert Watson total_len += offset;
364618c7db3SRobert Watson offset = 0;
365618c7db3SRobert Watson }
366618c7db3SRobert Watson }
367618c7db3SRobert Watson total_len += offset; /* Without the zero-padding. */
368618c7db3SRobert Watson if (offset != 0) {
369618c7db3SRobert Watson bzero(textdump_block_buffer + offset,
370618c7db3SRobert Watson sizeof(textdump_block_buffer) - offset);
371618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
372618c7db3SRobert Watson }
373618c7db3SRobert Watson
374618c7db3SRobert Watson /*
375618c7db3SRobert Watson * Rewrite tar header to reflect how much was actually written.
376618c7db3SRobert Watson */
377618c7db3SRobert Watson textdump_saveoff(&end_offset);
378618c7db3SRobert Watson textdump_restoreoff(tarhdr_offset);
379618c7db3SRobert Watson textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME,
380618c7db3SRobert Watson total_len);
381618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
382618c7db3SRobert Watson textdump_restoreoff(end_offset);
383618c7db3SRobert Watson }
384618c7db3SRobert Watson
385618c7db3SRobert Watson static void
textdump_dump_panic(struct dumperinfo * di)386618c7db3SRobert Watson textdump_dump_panic(struct dumperinfo *di)
387618c7db3SRobert Watson {
388618c7db3SRobert Watson u_int len;
389618c7db3SRobert Watson
390618c7db3SRobert Watson /*
391618c7db3SRobert Watson * Write out tar header -- we store up to one block of panic message.
392618c7db3SRobert Watson */
393618c7db3SRobert Watson len = min(strlen(panicstr), TEXTDUMP_BLOCKSIZE);
394618c7db3SRobert Watson textdump_mkustar(textdump_block_buffer, TAR_PANIC_FILENAME, len);
395618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
396618c7db3SRobert Watson
397618c7db3SRobert Watson /*
398618c7db3SRobert Watson * Zero-pad the panic string and write out block.
399618c7db3SRobert Watson */
400618c7db3SRobert Watson bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
401618c7db3SRobert Watson bcopy(panicstr, textdump_block_buffer, len);
402618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
403618c7db3SRobert Watson }
404618c7db3SRobert Watson
405618c7db3SRobert Watson static void
textdump_dump_version(struct dumperinfo * di)406618c7db3SRobert Watson textdump_dump_version(struct dumperinfo *di)
407618c7db3SRobert Watson {
408618c7db3SRobert Watson u_int len;
409618c7db3SRobert Watson
410618c7db3SRobert Watson /*
411618c7db3SRobert Watson * Write out tar header -- at most one block of version information.
412618c7db3SRobert Watson */
413618c7db3SRobert Watson len = min(strlen(version), TEXTDUMP_BLOCKSIZE);
414618c7db3SRobert Watson textdump_mkustar(textdump_block_buffer, TAR_VERSION_FILENAME, len);
415618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
416618c7db3SRobert Watson
417618c7db3SRobert Watson /*
418618c7db3SRobert Watson * Zero pad the version string and write out block.
419618c7db3SRobert Watson */
420618c7db3SRobert Watson bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
421618c7db3SRobert Watson bcopy(version, textdump_block_buffer, len);
422618c7db3SRobert Watson (void)textdump_writenextblock(di, textdump_block_buffer);
423618c7db3SRobert Watson }
424618c7db3SRobert Watson
425618c7db3SRobert Watson /*
426618c7db3SRobert Watson * Commit text dump to disk.
427618c7db3SRobert Watson */
428618c7db3SRobert Watson void
textdump_dumpsys(struct dumperinfo * di)429618c7db3SRobert Watson textdump_dumpsys(struct dumperinfo *di)
430618c7db3SRobert Watson {
431480f31c2SKonrad Witaszczyk struct kerneldumpcrypto *kdc;
432618c7db3SRobert Watson off_t dumplen, trailer_offset;
433618c7db3SRobert Watson
434618c7db3SRobert Watson if (di->blocksize != TEXTDUMP_BLOCKSIZE) {
435618c7db3SRobert Watson printf("Dump partition block size (%ju) not textdump "
436618c7db3SRobert Watson "block size (%ju)", (uintmax_t)di->blocksize,
437618c7db3SRobert Watson (uintmax_t)TEXTDUMP_BLOCKSIZE);
438618c7db3SRobert Watson return;
439618c7db3SRobert Watson }
440618c7db3SRobert Watson
441618c7db3SRobert Watson /*
442618c7db3SRobert Watson * We don't know a priori how large the dump will be, but we do know
443618c7db3SRobert Watson * that we need to reserve space for metadata and that we need two
444618c7db3SRobert Watson * dump headers. Also leave room for one ustar header and one block
445618c7db3SRobert Watson * of data.
446618c7db3SRobert Watson */
447618c7db3SRobert Watson if (di->mediasize < SIZEOF_METADATA + 2 * sizeof(kdh)) {
44821d748a9SAlfred Perlstein printf("Insufficient space on dump partition for minimal textdump.\n");
449618c7db3SRobert Watson return;
450618c7db3SRobert Watson }
451618c7db3SRobert Watson textdump_error = 0;
452618c7db3SRobert Watson
453618c7db3SRobert Watson /*
454480f31c2SKonrad Witaszczyk * Disable EKCD because we don't provide encrypted textdumps.
455480f31c2SKonrad Witaszczyk */
45678f57a9cSMark Johnston kdc = di->kdcrypto;
45778f57a9cSMark Johnston di->kdcrypto = NULL;
458480f31c2SKonrad Witaszczyk
459480f31c2SKonrad Witaszczyk /*
460618c7db3SRobert Watson * Position the start of the dump so that we'll write the kernel dump
461618c7db3SRobert Watson * trailer immediately before the end of the partition, and then work
462618c7db3SRobert Watson * our way back. We will rewrite this header later to reflect the
463618c7db3SRobert Watson * true size if things go well.
464618c7db3SRobert Watson */
465618c7db3SRobert Watson textdump_offset = di->mediasize - sizeof(kdh);
466618c7db3SRobert Watson textdump_saveoff(&trailer_offset);
46701938d36SMark Johnston dump_init_header(di, &kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, 0);
468618c7db3SRobert Watson (void)textdump_writenextblock(di, (char *)&kdh);
469618c7db3SRobert Watson
470618c7db3SRobert Watson /*
471618c7db3SRobert Watson * Write a series of files in ustar format.
472618c7db3SRobert Watson */
473618c7db3SRobert Watson if (textdump_do_ddb)
474618c7db3SRobert Watson db_capture_dump(di);
475618c7db3SRobert Watson #ifdef INCLUDE_CONFIG_FILE
476618c7db3SRobert Watson if (textdump_do_config)
477618c7db3SRobert Watson textdump_dump_config(di);
478618c7db3SRobert Watson #endif
479618c7db3SRobert Watson if (textdump_do_msgbuf)
480618c7db3SRobert Watson textdump_dump_msgbuf(di);
481879e0604SMateusz Guzik if (textdump_do_panic && KERNEL_PANICKED())
482618c7db3SRobert Watson textdump_dump_panic(di);
483618c7db3SRobert Watson if (textdump_do_version)
484618c7db3SRobert Watson textdump_dump_version(di);
485618c7db3SRobert Watson
486618c7db3SRobert Watson /*
487618c7db3SRobert Watson * Now that we know the true size, we can write out the header, then
488618c7db3SRobert Watson * seek back to the end and rewrite the trailer with the correct
489618c7db3SRobert Watson * size.
490618c7db3SRobert Watson */
491618c7db3SRobert Watson dumplen = trailer_offset - (textdump_offset + TEXTDUMP_BLOCKSIZE);
49201938d36SMark Johnston dump_init_header(di, &kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION,
49301938d36SMark Johnston dumplen);
494618c7db3SRobert Watson (void)textdump_writenextblock(di, (char *)&kdh);
495618c7db3SRobert Watson textdump_restoreoff(trailer_offset);
496618c7db3SRobert Watson (void)textdump_writenextblock(di, (char *)&kdh);
497618c7db3SRobert Watson
498618c7db3SRobert Watson /*
499618c7db3SRobert Watson * Terminate the dump, report any errors, and clear the pending flag.
500618c7db3SRobert Watson */
501618c7db3SRobert Watson if (textdump_error == 0)
502db71383bSMitchell Horne (void)dump_write(di, NULL, 0, 0);
503618c7db3SRobert Watson if (textdump_error == ENOSPC)
50421d748a9SAlfred Perlstein printf("Textdump: Insufficient space on dump partition\n");
505618c7db3SRobert Watson else if (textdump_error != 0)
50621d748a9SAlfred Perlstein printf("Textdump: Error %d writing dump\n", textdump_error);
507618c7db3SRobert Watson else
508618c7db3SRobert Watson printf("Textdump complete.\n");
509618c7db3SRobert Watson textdump_pending = 0;
510480f31c2SKonrad Witaszczyk
511480f31c2SKonrad Witaszczyk /*
512480f31c2SKonrad Witaszczyk * Restore EKCD status.
513480f31c2SKonrad Witaszczyk */
51478f57a9cSMark Johnston di->kdcrypto = kdc;
515618c7db3SRobert Watson }
516618c7db3SRobert Watson
517618c7db3SRobert Watson /*-
518618c7db3SRobert Watson * DDB(4) command to manage textdumps:
519618c7db3SRobert Watson *
520618c7db3SRobert Watson * textdump set - request a textdump
521618c7db3SRobert Watson * textdump status - print DDB output textdump status
522618c7db3SRobert Watson * textdump unset - clear textdump request
523618c7db3SRobert Watson */
524618c7db3SRobert Watson static void
db_textdump_usage(void)525618c7db3SRobert Watson db_textdump_usage(void)
526618c7db3SRobert Watson {
527618c7db3SRobert Watson
52821d748a9SAlfred Perlstein db_printf("textdump [unset|set|status|dump]\n");
529618c7db3SRobert Watson }
530618c7db3SRobert Watson
531618c7db3SRobert Watson void
db_textdump_cmd(db_expr_t addr,bool have_addr,db_expr_t count,char * modif)532cd508278SPedro F. Giffuni db_textdump_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
533618c7db3SRobert Watson {
534618c7db3SRobert Watson int t;
535618c7db3SRobert Watson
536618c7db3SRobert Watson t = db_read_token();
537618c7db3SRobert Watson if (t != tIDENT) {
538618c7db3SRobert Watson db_textdump_usage();
539618c7db3SRobert Watson return;
540618c7db3SRobert Watson }
541618c7db3SRobert Watson if (db_read_token() != tEOL) {
542618c7db3SRobert Watson db_textdump_usage();
543618c7db3SRobert Watson return;
544618c7db3SRobert Watson }
545618c7db3SRobert Watson if (strcmp(db_tok_string, "set") == 0) {
546618c7db3SRobert Watson textdump_pending = 1;
547618c7db3SRobert Watson db_printf("textdump set\n");
548618c7db3SRobert Watson } else if (strcmp(db_tok_string, "status") == 0) {
549618c7db3SRobert Watson if (textdump_pending)
550618c7db3SRobert Watson db_printf("textdump is set\n");
551618c7db3SRobert Watson else
552618c7db3SRobert Watson db_printf("textdump is not set\n");
553618c7db3SRobert Watson } else if (strcmp(db_tok_string, "unset") == 0) {
554618c7db3SRobert Watson textdump_pending = 0;
555618c7db3SRobert Watson db_printf("textdump unset\n");
55621d748a9SAlfred Perlstein } else if (strcmp(db_tok_string, "dump") == 0) {
55721d748a9SAlfred Perlstein textdump_pending = 1;
5582b490bc7SPedro F. Giffuni doadump(true);
55921d748a9SAlfred Perlstein } else {
560618c7db3SRobert Watson db_textdump_usage();
561618c7db3SRobert Watson }
56221d748a9SAlfred Perlstein }
563