Index: head/sys/dev/null/null.c =================================================================== --- head/sys/dev/null/null.c +++ head/sys/dev/null/null.c @@ -107,14 +107,14 @@ int flags __unused, struct thread *td) { int error; - error = 0; + error = 0; switch (cmd) { #ifdef COMPAT_FREEBSD11 case DIOCSKERNELDUMP_FREEBSD11: #endif case DIOCSKERNELDUMP: - error = set_dumper(NULL, NULL, td, 0, 0, NULL, 0, NULL); + error = clear_dumper(td); break; case FIONBIO: break; Index: head/sys/geom/geom_dev.c =================================================================== --- head/sys/geom/geom_dev.c +++ head/sys/geom/geom_dev.c @@ -138,10 +138,11 @@ int error, len; if (dev == NULL || kda == NULL) - return (set_dumper(NULL, NULL, td, 0, 0, NULL, 0, NULL)); + return (clear_dumper(td)); cp = dev->si_drv2; len = sizeof(kd); + memset(&kd, 0, len); kd.offset = 0; kd.length = OFF_MAX; error = g_io_getattr("GEOM::kerneldump", cp, &len, &kd); @@ -833,7 +834,7 @@ /* Reset any dump-area set on this device */ if (dev->si_flags & SI_DUMPDEV) - (void)set_dumper(NULL, NULL, curthread, 0, 0, NULL, 0, NULL); + (void)clear_dumper(curthread); /* Destroy the struct cdev *so we get no more requests */ destroy_dev_sched_cb(dev, g_dev_callback, cp); Index: head/sys/kern/kern_shutdown.c =================================================================== --- head/sys/kern/kern_shutdown.c +++ head/sys/kern/kern_shutdown.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -1090,10 +1091,6 @@ if (error != 0) return (error); - if (di == NULL) { - error = 0; - goto cleanup; - } if (dumper.dumper != NULL) return (EBUSY); dumper = *di; @@ -1139,7 +1136,25 @@ dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO); return (0); + cleanup: + (void)clear_dumper(td); + return (error); +} + +int +clear_dumper(struct thread *td) +{ + int error; + + error = priv_check(td, PRIV_SETDUMPER); + if (error != 0) + return (error); + +#ifdef NETDUMP + netdump_mbuf_drain(); +#endif + #ifdef EKCD if (dumper.kdcrypto != NULL) { explicit_bzero(dumper.kdcrypto, sizeof(*dumper.kdcrypto) + @@ -1156,14 +1171,14 @@ } explicit_bzero(&dumper, sizeof(dumper)); dumpdevname[0] = '\0'; - return (error); + return (0); } static int dump_check_bounds(struct dumperinfo *di, off_t offset, size_t length) { - if (length != 0 && (offset < di->mediaoffset || + if (di->mediasize > 0 && length != 0 && (offset < di->mediaoffset || offset - di->mediaoffset + length > di->mediasize)) { if (di->kdcomp != NULL && offset >= di->mediaoffset) { printf( @@ -1244,18 +1259,6 @@ return (0); } - -static int -dump_write_key(struct dumperinfo *di, off_t offset) -{ - struct kerneldumpcrypto *kdc; - - kdc = di->kdcrypto; - if (kdc == NULL) - return (0); - return (dump_write(di, kdc->kdc_dumpkey, 0, offset, - kdc->kdc_dumpkeysize)); -} #endif /* EKCD */ static int @@ -1289,20 +1292,42 @@ } /* - * Write a kerneldumpheader at the specified offset. The header structure is 512 - * bytes in size, but we must pad to the device sector size. + * Write kernel dump headers at the beginning and end of the dump extent. + * Write the kernel dump encryption key after the leading header if we were + * configured to do so. */ static int -dump_write_header(struct dumperinfo *di, struct kerneldumpheader *kdh, - off_t offset) +dump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh) { - void *buf; +#ifdef EKCD + struct kerneldumpcrypto *kdc; +#endif + void *buf, *key; size_t hdrsz; + uint64_t extent; + uint32_t keysize; + int error; hdrsz = sizeof(*kdh); if (hdrsz > di->blocksize) return (ENOMEM); +#ifdef EKCD + kdc = di->kdcrypto; + key = kdc->kdc_dumpkey; + keysize = kerneldumpcrypto_dumpkeysize(kdc); +#else + key = NULL; + keysize = 0; +#endif + + /* + * If the dump device has special handling for headers, let it take care + * of writing them out. + */ + if (di->dumper_hdr != NULL) + return (di->dumper_hdr(di, kdh, key, keysize)); + if (hdrsz == di->blocksize) buf = kdh; else { @@ -1311,7 +1336,24 @@ memcpy(buf, kdh, hdrsz); } - return (dump_write(di, buf, 0, offset, di->blocksize)); + extent = dtoh64(kdh->dumpextent); +#ifdef EKCD + if (kdc != NULL) { + error = dump_write(di, kdc->kdc_dumpkey, 0, + di->mediaoffset + di->mediasize - di->blocksize - extent - + keysize, keysize); + if (error != 0) + return (error); + } +#endif + + error = dump_write(di, buf, 0, + di->mediaoffset + di->mediasize - 2 * di->blocksize - extent - + keysize, di->blocksize); + if (error == 0) + error = dump_write(di, buf, 0, di->mediaoffset + di->mediasize - + di->blocksize, di->blocksize); + return (error); } /* @@ -1336,26 +1378,37 @@ * Uncompressed dumps will use the entire extent, but compressed dumps typically * will not. The true length of the dump is recorded in the leading and trailing * headers once the dump has been completed. + * + * The dump device may provide a callback, in which case it will initialize + * dumpoff and take care of laying out the headers. */ int dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh) { - uint64_t dumpextent; + uint64_t dumpextent, span; uint32_t keysize; + int error; #ifdef EKCD - int error = kerneldumpcrypto_init(di->kdcrypto); + error = kerneldumpcrypto_init(di->kdcrypto); if (error != 0) return (error); keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto); #else + error = 0; keysize = 0; #endif - dumpextent = dtoh64(kdh->dumpextent); - if (di->mediasize < SIZEOF_METADATA + dumpextent + 2 * di->blocksize + - keysize) { - if (di->kdcomp != NULL) { + if (di->dumper_start != NULL) { + error = di->dumper_start(di); + } else { + dumpextent = dtoh64(kdh->dumpextent); + span = SIZEOF_METADATA + dumpextent + 2 * di->blocksize + + keysize; + if (di->mediasize < span) { + if (di->kdcomp == NULL) + return (E2BIG); + /* * We don't yet know how much space the compressed dump * will occupy, so try to use the whole swap partition @@ -1364,18 +1417,18 @@ * be enough, the bounds checking in dump_write() * will catch us and cause the dump to fail. */ - dumpextent = di->mediasize - SIZEOF_METADATA - - 2 * di->blocksize - keysize; + dumpextent = di->mediasize - span + dumpextent; kdh->dumpextent = htod64(dumpextent); - } else - return (E2BIG); - } + } - /* The offset at which to begin writing the dump. */ - di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize - - dumpextent; - - return (0); + /* + * The offset at which to begin writing the dump. + */ + di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize - + dumpextent; + } + di->origdumpoff = di->dumpoff; + return (error); } static int @@ -1443,17 +1496,10 @@ dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh) { uint64_t extent; - uint32_t keysize; int error; extent = dtoh64(kdh->dumpextent); -#ifdef EKCD - keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto); -#else - keysize = 0; -#endif - if (di->kdcomp != NULL) { error = compressor_flush(di->kdcomp->kdc_stream); if (error == EAGAIN) { @@ -1470,33 +1516,14 @@ * We now know the size of the compressed dump, so update the * header accordingly and recompute parity. */ - kdh->dumplength = htod64(di->dumpoff - - (di->mediaoffset + di->mediasize - di->blocksize - extent)); + kdh->dumplength = htod64(di->dumpoff - di->origdumpoff); kdh->parity = 0; kdh->parity = kerneldump_parity(kdh); compressor_reset(di->kdcomp->kdc_stream); } - /* - * Write kerneldump headers at the beginning and end of the dump extent. - * Write the key after the leading header. - */ - error = dump_write_header(di, kdh, - di->mediaoffset + di->mediasize - 2 * di->blocksize - extent - - keysize); - if (error != 0) - return (error); - -#ifdef EKCD - error = dump_write_key(di, - di->mediaoffset + di->mediasize - di->blocksize - extent - keysize); - if (error != 0) - return (error); -#endif - - error = dump_write_header(di, kdh, - di->mediaoffset + di->mediasize - di->blocksize); + error = dump_write_headers(di, kdh); if (error != 0) return (error); Index: head/sys/sys/conf.h =================================================================== --- head/sys/sys/conf.h +++ head/sys/sys/conf.h @@ -101,6 +101,8 @@ struct bio; struct buf; +struct dumperinfo; +struct kerneldumpheader; struct thread; struct uio; struct knote; @@ -131,6 +133,9 @@ vm_offset_t _physical, /* Physical address of virtual. */ off_t _offset, /* Byte-offset to write at. */ size_t _length); /* Number of bytes to dump. */ +typedef int dumper_start_t(struct dumperinfo *di); +typedef int dumper_hdr_t(struct dumperinfo *di, struct kerneldumpheader *kdh, + void *key, uint32_t keylen); #endif /* _KERNEL */ @@ -332,13 +337,18 @@ struct dumperinfo { dumper_t *dumper; /* Dumping function. */ + dumper_start_t *dumper_start; /* Dumper callback for dump_start(). */ + dumper_hdr_t *dumper_hdr; /* Dumper callback for writing headers. */ void *priv; /* Private parts. */ u_int blocksize; /* Size of block in bytes. */ u_int maxiosize; /* Max size allowed for an individual I/O */ off_t mediaoffset; /* Initial offset in bytes. */ off_t mediasize; /* Space available in bytes. */ + + /* MI kernel dump state. */ void *blockbuf; /* Buffer for padding shorter dump blocks */ off_t dumpoff; /* Offset of ongoing kernel dump. */ + off_t origdumpoff; /* Starting dump offset. */ struct kerneldumpcrypto *kdcrypto; /* Kernel dump crypto. */ struct kerneldumpcomp *kdcomp; /* Kernel dump compression. */ }; @@ -349,6 +359,7 @@ int set_dumper(struct dumperinfo *di, const char *devname, struct thread *td, uint8_t compression, uint8_t encryption, const uint8_t *key, uint32_t encryptedkeysize, const uint8_t *encryptedkey); +int clear_dumper(struct thread *td); int dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh); int dump_append(struct dumperinfo *, void *, vm_offset_t, size_t);