Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/ddb/db_textdump.c
39476 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2007 Robert N. M. Watson
5
* All rights reserved.
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
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
/*-
30
* Kernel text-dump support: write a series of text files to the dump
31
* partition for later recovery, including captured DDB output, kernel
32
* configuration, message buffer, and panic message. This allows for a more
33
* compact representation of critical debugging information than traditional
34
* binary dumps, as well as allowing dump information to be used without
35
* access to kernel symbols, source code, etc.
36
*
37
* Storage Layout
38
* --------------
39
*
40
* Crash dumps are aligned to the end of the dump or swap partition in order
41
* to minimize the chances of swap duing fsck eating into the dump. However,
42
* unlike a memory dump, we don't know the size of the textdump a priori, so
43
* can't just write it out sequentially in order from a known starting point
44
* calculated with respect to the end of the partition. In order to address
45
* this, we actually write out the textdump in reverse block order, allowing
46
* us to directly align it to the end of the partition and then write out the
47
* dump header and trailer before and after it once done. savecore(8) must
48
* know to reverse the order of the blocks in order to produce a readable
49
* file.
50
*
51
* Data is written out in the ustar file format so that we can write data
52
* incrementally as a stream without reference to previous files.
53
*
54
* TODO
55
* ----
56
*
57
* - Allow subsystems to register to submit files for inclusion in the text
58
* dump in a generic way.
59
*/
60
61
#include <sys/cdefs.h>
62
#include "opt_config.h"
63
64
#include "opt_ddb.h"
65
66
#include <sys/param.h>
67
#include <sys/conf.h>
68
#include <sys/kernel.h>
69
#include <sys/kerneldump.h>
70
#include <sys/msgbuf.h>
71
#include <sys/sysctl.h>
72
#include <sys/systm.h>
73
74
#include <ddb/ddb.h>
75
#include <ddb/db_lex.h>
76
77
static SYSCTL_NODE(_debug_ddb, OID_AUTO, textdump,
78
CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
79
"DDB textdump options");
80
81
/*
82
* Don't touch the first SIZEOF_METADATA bytes on the dump device. This is
83
* to protect us from metadata and metadata from us.
84
*/
85
#define SIZEOF_METADATA (64*1024)
86
87
/*
88
* Data is written out as a series of files in the ustar tar format. ustar
89
* is a simple streamed format consiting of a series of files prefixed with
90
* headers, and all padded to 512-byte block boundaries, which maps
91
* conveniently to our requirements.
92
*/
93
struct ustar_header {
94
char uh_filename[100];
95
char uh_mode[8];
96
char uh_tar_owner[8];
97
char uh_tar_group[8];
98
char uh_size[12];
99
char uh_mtime[12];
100
char uh_sum[8];
101
char uh_type;
102
char uh_linkfile[100];
103
char uh_ustar[6];
104
char uh_version[2];
105
char uh_owner[32];
106
char uh_group[32];
107
char uh_major[8];
108
char uh_minor[8];
109
char uh_filenameprefix[155];
110
char uh_zeropad[12];
111
} __packed;
112
113
/*
114
* Various size assertions -- pretty much everything must be one block in
115
* size.
116
*/
117
CTASSERT(sizeof(struct kerneldumpheader) == TEXTDUMP_BLOCKSIZE);
118
CTASSERT(sizeof(struct ustar_header) == TEXTDUMP_BLOCKSIZE);
119
120
/*
121
* Is a textdump scheduled? If so, the shutdown code will invoke our dumpsys
122
* routine instead of the machine-dependent kernel dump routine.
123
*/
124
#ifdef TEXTDUMP_PREFERRED
125
int textdump_pending = 1;
126
#else
127
int textdump_pending = 0;
128
#endif
129
SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, pending, CTLFLAG_RW,
130
&textdump_pending, 0,
131
"Perform textdump instead of regular kernel dump.");
132
133
/*
134
* Various constants for tar headers and contents.
135
*/
136
#define TAR_USER "root"
137
#define TAR_GROUP "wheel"
138
#define TAR_UID "0"
139
#define TAR_GID "0"
140
#define TAR_MODE "0600"
141
#define TAR_USTAR "ustar"
142
143
#define TAR_CONFIG_FILENAME "config.txt" /* Kernel configuration. */
144
#define TAR_MSGBUF_FILENAME "msgbuf.txt" /* Kernel message buffer. */
145
#define TAR_PANIC_FILENAME "panic.txt" /* Panic message. */
146
#define TAR_VERSION_FILENAME "version.txt" /* Kernel version. */
147
148
/*
149
* Configure which files will be dumped.
150
*/
151
#ifdef INCLUDE_CONFIG_FILE
152
static int textdump_do_config = 1;
153
SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_config, CTLFLAG_RW,
154
&textdump_do_config, 0, "Dump kernel configuration in textdump");
155
#endif
156
157
static int textdump_do_ddb = 1;
158
SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_ddb, CTLFLAG_RW,
159
&textdump_do_ddb, 0, "Dump DDB captured output in textdump");
160
161
static int textdump_do_msgbuf = 1;
162
SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_msgbuf, CTLFLAG_RW,
163
&textdump_do_msgbuf, 0, "Dump kernel message buffer in textdump");
164
165
static int textdump_do_panic = 1;
166
SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_panic, CTLFLAG_RW,
167
&textdump_do_panic, 0, "Dump kernel panic message in textdump");
168
169
static int textdump_do_version = 1;
170
SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_version, CTLFLAG_RW,
171
&textdump_do_version, 0, "Dump kernel version string in textdump");
172
173
/*
174
* State related to incremental writing of blocks to disk.
175
*/
176
static off_t textdump_offset; /* Offset of next sequential write. */
177
static int textdump_error; /* Carried write error, if any. */
178
179
/*
180
* Statically allocate space to prepare block-sized headers and data.
181
*/
182
char textdump_block_buffer[TEXTDUMP_BLOCKSIZE];
183
static struct kerneldumpheader kdh;
184
185
/*
186
* Calculate and fill in the checksum for a ustar header.
187
*/
188
static void
189
ustar_checksum(struct ustar_header *uhp)
190
{
191
u_int sum;
192
int i;
193
194
for (i = 0; i < sizeof(uhp->uh_sum); i++)
195
uhp->uh_sum[i] = ' ';
196
sum = 0;
197
for (i = 0; i < sizeof(*uhp); i++)
198
sum += ((u_char *)uhp)[i];
199
snprintf(uhp->uh_sum, sizeof(uhp->uh_sum), "%6o", sum);
200
}
201
202
/*
203
* Each file in the tarball has a block-sized header with its name and other,
204
* largely hard-coded, properties.
205
*/
206
void
207
textdump_mkustar(char *block_buffer, const char *filename, u_int size)
208
{
209
struct ustar_header *uhp;
210
211
#ifdef TEXTDUMP_VERBOSE
212
if (textdump_error == 0)
213
printf("textdump: creating '%s'.\n", filename);
214
#endif
215
uhp = (struct ustar_header *)block_buffer;
216
bzero(uhp, sizeof(*uhp));
217
strlcpy(uhp->uh_filename, filename, sizeof(uhp->uh_filename));
218
strlcpy(uhp->uh_mode, TAR_MODE, sizeof(uhp->uh_mode));
219
snprintf(uhp->uh_size, sizeof(uhp->uh_size), "%o", size);
220
strlcpy(uhp->uh_tar_owner, TAR_UID, sizeof(uhp->uh_tar_owner));
221
strlcpy(uhp->uh_tar_group, TAR_GID, sizeof(uhp->uh_tar_group));
222
strlcpy(uhp->uh_owner, TAR_USER, sizeof(uhp->uh_owner));
223
strlcpy(uhp->uh_group, TAR_GROUP, sizeof(uhp->uh_group));
224
snprintf(uhp->uh_mtime, sizeof(uhp->uh_mtime), "%lo",
225
(unsigned long)time_second);
226
uhp->uh_type = 0;
227
strlcpy(uhp->uh_ustar, TAR_USTAR, sizeof(uhp->uh_ustar));
228
ustar_checksum(uhp);
229
}
230
231
/*
232
* textdump_writeblock() writes TEXTDUMP_BLOCKSIZE-sized blocks of data to
233
* the space between di->mediaoffset and di->mediaoffset + di->mediasize. It
234
* accepts an offset relative to di->mediaoffset. If we're carrying any
235
* error from previous I/O, return that error and don't continue to try to
236
* write. Most writers ignore the error and forge ahead on the basis that
237
* there's not much you can do.
238
*/
239
static int
240
textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer)
241
{
242
243
if (textdump_error)
244
return (textdump_error);
245
if (offset + TEXTDUMP_BLOCKSIZE > di->mediasize)
246
return (EIO);
247
if (offset < SIZEOF_METADATA)
248
return (ENOSPC);
249
textdump_error = dump_write(di, buffer, offset + di->mediaoffset,
250
TEXTDUMP_BLOCKSIZE);
251
if (textdump_error)
252
printf("textdump_writeblock: offset %jd, error %d\n", (intmax_t)offset,
253
textdump_error);
254
return (textdump_error);
255
}
256
257
/*
258
* Interfaces to save and restore the dump offset, so that printers can go
259
* back to rewrite a header if required, while avoiding their knowing about
260
* the global layout of the blocks.
261
*
262
* If we ever want to support writing textdumps to tape or other
263
* stream-oriented target, we'll need to remove this.
264
*/
265
void
266
textdump_saveoff(off_t *offsetp)
267
{
268
269
*offsetp = textdump_offset;
270
}
271
272
void
273
textdump_restoreoff(off_t offset)
274
{
275
276
textdump_offset = offset;
277
}
278
279
/*
280
* Interface to write the "next block" relative to the current offset; since
281
* we write backwards from the end of the partition, we subtract, but there's
282
* no reason for the caller to know this.
283
*/
284
int
285
textdump_writenextblock(struct dumperinfo *di, char *buffer)
286
{
287
int error;
288
289
error = textdump_writeblock(di, textdump_offset, buffer);
290
textdump_offset -= TEXTDUMP_BLOCKSIZE;
291
return (error);
292
}
293
294
#ifdef INCLUDE_CONFIG_FILE
295
extern char kernconfstring[];
296
297
/*
298
* Dump kernel configuration.
299
*/
300
static void
301
textdump_dump_config(struct dumperinfo *di)
302
{
303
u_int count, fullblocks, len;
304
305
len = strlen(kernconfstring);
306
textdump_mkustar(textdump_block_buffer, TAR_CONFIG_FILENAME, len);
307
(void)textdump_writenextblock(di, textdump_block_buffer);
308
309
/*
310
* Write out all full blocks directly from the string, and handle any
311
* left-over bits by copying it to out to the local buffer and
312
* zero-padding it.
313
*/
314
fullblocks = len / TEXTDUMP_BLOCKSIZE;
315
for (count = 0; count < fullblocks; count++)
316
(void)textdump_writenextblock(di, kernconfstring + count *
317
TEXTDUMP_BLOCKSIZE);
318
if (len % TEXTDUMP_BLOCKSIZE != 0) {
319
bzero(textdump_block_buffer, TEXTDUMP_BLOCKSIZE);
320
bcopy(kernconfstring + count * TEXTDUMP_BLOCKSIZE,
321
textdump_block_buffer, len % TEXTDUMP_BLOCKSIZE);
322
(void)textdump_writenextblock(di, textdump_block_buffer);
323
}
324
}
325
#endif /* INCLUDE_CONFIG_FILE */
326
327
/*
328
* Dump kernel message buffer.
329
*/
330
static void
331
textdump_dump_msgbuf(struct dumperinfo *di)
332
{
333
off_t end_offset, tarhdr_offset;
334
u_int i, len, offset, seq, total_len;
335
char buf[16];
336
337
/*
338
* Write out a dummy tar header to advance the offset; we'll rewrite
339
* it later once we know the true size.
340
*/
341
textdump_saveoff(&tarhdr_offset);
342
textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME, 0);
343
(void)textdump_writenextblock(di, textdump_block_buffer);
344
345
/*
346
* Copy out the data in small chunks, but don't copy nuls that may be
347
* present if the message buffer has not yet completely filled at
348
* least once.
349
*/
350
total_len = 0;
351
offset = 0;
352
msgbuf_peekbytes(msgbufp, NULL, 0, &seq);
353
while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) {
354
for (i = 0; i < len; i++) {
355
if (buf[i] == '\0')
356
continue;
357
textdump_block_buffer[offset] = buf[i];
358
offset++;
359
if (offset != sizeof(textdump_block_buffer))
360
continue;
361
(void)textdump_writenextblock(di,
362
textdump_block_buffer);
363
total_len += offset;
364
offset = 0;
365
}
366
}
367
total_len += offset; /* Without the zero-padding. */
368
if (offset != 0) {
369
bzero(textdump_block_buffer + offset,
370
sizeof(textdump_block_buffer) - offset);
371
(void)textdump_writenextblock(di, textdump_block_buffer);
372
}
373
374
/*
375
* Rewrite tar header to reflect how much was actually written.
376
*/
377
textdump_saveoff(&end_offset);
378
textdump_restoreoff(tarhdr_offset);
379
textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME,
380
total_len);
381
(void)textdump_writenextblock(di, textdump_block_buffer);
382
textdump_restoreoff(end_offset);
383
}
384
385
static void
386
textdump_dump_panic(struct dumperinfo *di)
387
{
388
u_int len;
389
390
/*
391
* Write out tar header -- we store up to one block of panic message.
392
*/
393
len = min(strlen(panicstr), TEXTDUMP_BLOCKSIZE);
394
textdump_mkustar(textdump_block_buffer, TAR_PANIC_FILENAME, len);
395
(void)textdump_writenextblock(di, textdump_block_buffer);
396
397
/*
398
* Zero-pad the panic string and write out block.
399
*/
400
bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
401
bcopy(panicstr, textdump_block_buffer, len);
402
(void)textdump_writenextblock(di, textdump_block_buffer);
403
}
404
405
static void
406
textdump_dump_version(struct dumperinfo *di)
407
{
408
u_int len;
409
410
/*
411
* Write out tar header -- at most one block of version information.
412
*/
413
len = min(strlen(version), TEXTDUMP_BLOCKSIZE);
414
textdump_mkustar(textdump_block_buffer, TAR_VERSION_FILENAME, len);
415
(void)textdump_writenextblock(di, textdump_block_buffer);
416
417
/*
418
* Zero pad the version string and write out block.
419
*/
420
bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
421
bcopy(version, textdump_block_buffer, len);
422
(void)textdump_writenextblock(di, textdump_block_buffer);
423
}
424
425
/*
426
* Commit text dump to disk.
427
*/
428
void
429
textdump_dumpsys(struct dumperinfo *di)
430
{
431
struct kerneldumpcrypto *kdc;
432
off_t dumplen, trailer_offset;
433
434
if (di->blocksize != TEXTDUMP_BLOCKSIZE) {
435
printf("Dump partition block size (%ju) not textdump "
436
"block size (%ju)", (uintmax_t)di->blocksize,
437
(uintmax_t)TEXTDUMP_BLOCKSIZE);
438
return;
439
}
440
441
/*
442
* We don't know a priori how large the dump will be, but we do know
443
* that we need to reserve space for metadata and that we need two
444
* dump headers. Also leave room for one ustar header and one block
445
* of data.
446
*/
447
if (di->mediasize < SIZEOF_METADATA + 2 * sizeof(kdh)) {
448
printf("Insufficient space on dump partition for minimal textdump.\n");
449
return;
450
}
451
textdump_error = 0;
452
453
/*
454
* Disable EKCD because we don't provide encrypted textdumps.
455
*/
456
kdc = di->kdcrypto;
457
di->kdcrypto = NULL;
458
459
/*
460
* Position the start of the dump so that we'll write the kernel dump
461
* trailer immediately before the end of the partition, and then work
462
* our way back. We will rewrite this header later to reflect the
463
* true size if things go well.
464
*/
465
textdump_offset = di->mediasize - sizeof(kdh);
466
textdump_saveoff(&trailer_offset);
467
dump_init_header(di, &kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, 0);
468
(void)textdump_writenextblock(di, (char *)&kdh);
469
470
/*
471
* Write a series of files in ustar format.
472
*/
473
if (textdump_do_ddb)
474
db_capture_dump(di);
475
#ifdef INCLUDE_CONFIG_FILE
476
if (textdump_do_config)
477
textdump_dump_config(di);
478
#endif
479
if (textdump_do_msgbuf)
480
textdump_dump_msgbuf(di);
481
if (textdump_do_panic && KERNEL_PANICKED())
482
textdump_dump_panic(di);
483
if (textdump_do_version)
484
textdump_dump_version(di);
485
486
/*
487
* Now that we know the true size, we can write out the header, then
488
* seek back to the end and rewrite the trailer with the correct
489
* size.
490
*/
491
dumplen = trailer_offset - (textdump_offset + TEXTDUMP_BLOCKSIZE);
492
dump_init_header(di, &kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION,
493
dumplen);
494
(void)textdump_writenextblock(di, (char *)&kdh);
495
textdump_restoreoff(trailer_offset);
496
(void)textdump_writenextblock(di, (char *)&kdh);
497
498
/*
499
* Terminate the dump, report any errors, and clear the pending flag.
500
*/
501
if (textdump_error == 0)
502
(void)dump_write(di, NULL, 0, 0);
503
if (textdump_error == ENOSPC)
504
printf("Textdump: Insufficient space on dump partition\n");
505
else if (textdump_error != 0)
506
printf("Textdump: Error %d writing dump\n", textdump_error);
507
else
508
printf("Textdump complete.\n");
509
textdump_pending = 0;
510
511
/*
512
* Restore EKCD status.
513
*/
514
di->kdcrypto = kdc;
515
}
516
517
/*-
518
* DDB(4) command to manage textdumps:
519
*
520
* textdump set - request a textdump
521
* textdump status - print DDB output textdump status
522
* textdump unset - clear textdump request
523
*/
524
static void
525
db_textdump_usage(void)
526
{
527
528
db_printf("textdump [unset|set|status|dump]\n");
529
}
530
531
void
532
db_textdump_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
533
{
534
int t;
535
536
t = db_read_token();
537
if (t != tIDENT) {
538
db_textdump_usage();
539
return;
540
}
541
if (db_read_token() != tEOL) {
542
db_textdump_usage();
543
return;
544
}
545
if (strcmp(db_tok_string, "set") == 0) {
546
textdump_pending = 1;
547
db_printf("textdump set\n");
548
} else if (strcmp(db_tok_string, "status") == 0) {
549
if (textdump_pending)
550
db_printf("textdump is set\n");
551
else
552
db_printf("textdump is not set\n");
553
} else if (strcmp(db_tok_string, "unset") == 0) {
554
textdump_pending = 0;
555
db_printf("textdump unset\n");
556
} else if (strcmp(db_tok_string, "dump") == 0) {
557
textdump_pending = 1;
558
doadump(true);
559
} else {
560
db_textdump_usage();
561
}
562
}
563
564