Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_shutdown.c
Show First 20 Lines • Show All 1,075 Lines • ▼ Show 20 Lines | dumpdevname_sysctl_handler(SYSCTL_HANDLER_ARGS) | ||||
sbuf_delete(&sb); | sbuf_delete(&sb); | ||||
return (error); | return (error); | ||||
} | } | ||||
SYSCTL_PROC(_kern_shutdown, OID_AUTO, dumpdevname, | SYSCTL_PROC(_kern_shutdown, OID_AUTO, dumpdevname, | ||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, &dumper_configs, 0, | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, &dumper_configs, 0, | ||||
dumpdevname_sysctl_handler, "A", | dumpdevname_sysctl_handler, "A", | ||||
"Device(s) for kernel dumps"); | "Device(s) for kernel dumps"); | ||||
static int _dump_append(struct dumperinfo *di, void *virtual, | static int _dump_append(struct dumperinfo *di, void *virtual, size_t length); | ||||
vm_offset_t physical, size_t length); | |||||
#ifdef EKCD | #ifdef EKCD | ||||
static struct kerneldumpcrypto * | static struct kerneldumpcrypto * | ||||
kerneldumpcrypto_create(size_t blocksize, uint8_t encryption, | kerneldumpcrypto_create(size_t blocksize, uint8_t encryption, | ||||
const uint8_t *key, uint32_t encryptedkeysize, const uint8_t *encryptedkey) | const uint8_t *key, uint32_t encryptedkeysize, const uint8_t *encryptedkey) | ||||
{ | { | ||||
struct kerneldumpcrypto *kdc; | struct kerneldumpcrypto *kdc; | ||||
struct kerneldumpkey *kdk; | struct kerneldumpkey *kdk; | ||||
▲ Show 20 Lines • Show All 396 Lines • ▼ Show 20 Lines | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Encrypt data and call dumper. */ | /* Encrypt data and call dumper. */ | ||||
static int | static int | ||||
dump_encrypted_write(struct dumperinfo *di, void *virtual, | dump_encrypted_write(struct dumperinfo *di, void *virtual, off_t offset, | ||||
vm_offset_t physical, off_t offset, size_t length) | size_t length) | ||||
{ | { | ||||
static uint8_t buf[KERNELDUMP_BUFFER_SIZE]; | static uint8_t buf[KERNELDUMP_BUFFER_SIZE]; | ||||
struct kerneldumpcrypto *kdc; | struct kerneldumpcrypto *kdc; | ||||
int error; | int error; | ||||
size_t nbytes; | size_t nbytes; | ||||
kdc = di->kdcrypto; | kdc = di->kdcrypto; | ||||
while (length > 0) { | while (length > 0) { | ||||
nbytes = MIN(length, sizeof(buf)); | nbytes = MIN(length, sizeof(buf)); | ||||
bcopy(virtual, buf, nbytes); | bcopy(virtual, buf, nbytes); | ||||
if (dump_encrypt(kdc, buf, nbytes) != 0) | if (dump_encrypt(kdc, buf, nbytes) != 0) | ||||
return (EIO); | return (EIO); | ||||
error = dump_write(di, buf, physical, offset, nbytes); | error = dump_write(di, buf, offset, nbytes); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
offset += nbytes; | offset += nbytes; | ||||
virtual = (void *)((uint8_t *)virtual + nbytes); | virtual = (void *)((uint8_t *)virtual + nbytes); | ||||
length -= nbytes; | length -= nbytes; | ||||
} | } | ||||
Show All 14 Lines | if (length % di->blocksize != 0) { | ||||
/* | /* | ||||
* This must be the final write after flushing the compression | * This must be the final write after flushing the compression | ||||
* stream. Write as many full blocks as possible and stash the | * stream. Write as many full blocks as possible and stash the | ||||
* residual data in the dumper's block buffer. It will be | * residual data in the dumper's block buffer. It will be | ||||
* padded and written in dump_finish(). | * padded and written in dump_finish(). | ||||
*/ | */ | ||||
rlength = rounddown(length, di->blocksize); | rlength = rounddown(length, di->blocksize); | ||||
if (rlength != 0) { | if (rlength != 0) { | ||||
error = _dump_append(di, base, 0, rlength); | error = _dump_append(di, base, rlength); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
resid = length - rlength; | resid = length - rlength; | ||||
memmove(di->blockbuf, (uint8_t *)base + rlength, resid); | memmove(di->blockbuf, (uint8_t *)base + rlength, resid); | ||||
bzero((uint8_t *)di->blockbuf + resid, di->blocksize - resid); | bzero((uint8_t *)di->blockbuf + resid, di->blocksize - resid); | ||||
di->kdcomp->kdc_resid = resid; | di->kdcomp->kdc_resid = resid; | ||||
return (EAGAIN); | return (EAGAIN); | ||||
} | } | ||||
return (_dump_append(di, base, 0, length)); | return (_dump_append(di, base, length)); | ||||
} | } | ||||
/* | /* | ||||
* Write kernel dump headers at the beginning and end of the dump extent. | * 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 | * Write the kernel dump encryption key after the leading header if we were | ||||
* configured to do so. | * configured to do so. | ||||
*/ | */ | ||||
static int | static int | ||||
Show All 32 Lines | else { | ||||
buf = di->blockbuf; | buf = di->blockbuf; | ||||
memset(buf, 0, di->blocksize); | memset(buf, 0, di->blocksize); | ||||
memcpy(buf, kdh, hdrsz); | memcpy(buf, kdh, hdrsz); | ||||
} | } | ||||
extent = dtoh64(kdh->dumpextent); | extent = dtoh64(kdh->dumpextent); | ||||
#ifdef EKCD | #ifdef EKCD | ||||
if (kdc != NULL) { | if (kdc != NULL) { | ||||
error = dump_write(di, kdc->kdc_dumpkey, 0, | error = dump_write(di, kdc->kdc_dumpkey, | ||||
di->mediaoffset + di->mediasize - di->blocksize - extent - | di->mediaoffset + di->mediasize - di->blocksize - extent - | ||||
keysize, keysize); | keysize, keysize); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
error = dump_write(di, buf, 0, | error = dump_write(di, buf, | ||||
di->mediaoffset + di->mediasize - 2 * di->blocksize - extent - | di->mediaoffset + di->mediasize - 2 * di->blocksize - extent - | ||||
keysize, di->blocksize); | keysize, di->blocksize); | ||||
if (error == 0) | if (error == 0) | ||||
error = dump_write(di, buf, 0, di->mediaoffset + di->mediasize - | error = dump_write(di, buf, di->mediaoffset + di->mediasize - | ||||
di->blocksize, di->blocksize); | di->blocksize, di->blocksize); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Don't touch the first SIZEOF_METADATA bytes on the dump device. This is to | * Don't touch the first SIZEOF_METADATA bytes on the dump device. This is to | ||||
* protect us from metadata and metadata from us. | * protect us from metadata and metadata from us. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | if (di->dumper_start != NULL) { | ||||
di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize - | di->dumpoff = di->mediaoffset + di->mediasize - di->blocksize - | ||||
dumpextent; | dumpextent; | ||||
} | } | ||||
di->origdumpoff = di->dumpoff; | di->origdumpoff = di->dumpoff; | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
_dump_append(struct dumperinfo *di, void *virtual, vm_offset_t physical, | _dump_append(struct dumperinfo *di, void *virtual, size_t length) | ||||
size_t length) | |||||
{ | { | ||||
int error; | int error; | ||||
#ifdef EKCD | #ifdef EKCD | ||||
if (di->kdcrypto != NULL) | if (di->kdcrypto != NULL) | ||||
error = dump_encrypted_write(di, virtual, physical, di->dumpoff, | error = dump_encrypted_write(di, virtual, di->dumpoff, length); | ||||
length); | |||||
else | else | ||||
#endif | #endif | ||||
error = dump_write(di, virtual, physical, di->dumpoff, length); | error = dump_write(di, virtual, di->dumpoff, length); | ||||
if (error == 0) | if (error == 0) | ||||
di->dumpoff += length; | di->dumpoff += length; | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Write to the dump device starting at dumpoff. When compression is enabled, | * Write to the dump device starting at dumpoff. When compression is enabled, | ||||
* writes to the device will be performed using a callback that gets invoked | * writes to the device will be performed using a callback that gets invoked | ||||
* when the compression stream's output buffer is full. | * when the compression stream's output buffer is full. | ||||
*/ | */ | ||||
int | int | ||||
dump_append(struct dumperinfo *di, void *virtual, vm_offset_t physical, | dump_append(struct dumperinfo *di, void *virtual, size_t length) | ||||
size_t length) | |||||
{ | { | ||||
void *buf; | void *buf; | ||||
if (di->kdcomp != NULL) { | if (di->kdcomp != NULL) { | ||||
/* Bounce through a buffer to avoid CRC errors. */ | /* Bounce through a buffer to avoid CRC errors. */ | ||||
if (length > di->maxiosize) | if (length > di->maxiosize) | ||||
return (EINVAL); | return (EINVAL); | ||||
buf = di->kdcomp->kdc_buf; | buf = di->kdcomp->kdc_buf; | ||||
memmove(buf, virtual, length); | memmove(buf, virtual, length); | ||||
return (compressor_write(di->kdcomp->kdc_stream, buf, length)); | return (compressor_write(di->kdcomp->kdc_stream, buf, length)); | ||||
} | } | ||||
return (_dump_append(di, virtual, physical, length)); | return (_dump_append(di, virtual, length)); | ||||
} | } | ||||
/* | /* | ||||
* Write to the dump device at the specified offset. | * Write to the dump device at the specified offset. | ||||
*/ | */ | ||||
int | int | ||||
dump_write(struct dumperinfo *di, void *virtual, vm_offset_t physical, | dump_write(struct dumperinfo *di, void *virtual, off_t offset, size_t length) | ||||
off_t offset, size_t length) | |||||
{ | { | ||||
int error; | int error; | ||||
error = dump_check_bounds(di, offset, length); | error = dump_check_bounds(di, offset, length); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
return (di->dumper(di->priv, virtual, offset, length)); | return (di->dumper(di->priv, virtual, offset, length)); | ||||
} | } | ||||
/* | /* | ||||
* Perform kernel dump finalization: flush the compression stream, if necessary, | * Perform kernel dump finalization: flush the compression stream, if necessary, | ||||
* write the leading and trailing kernel dump headers now that we know the true | * write the leading and trailing kernel dump headers now that we know the true | ||||
* length of the dump, and optionally write the encryption key following the | * length of the dump, and optionally write the encryption key following the | ||||
* leading header. | * leading header. | ||||
*/ | */ | ||||
int | int | ||||
dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh) | dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh) | ||||
{ | { | ||||
int error; | int error; | ||||
if (di->kdcomp != NULL) { | if (di->kdcomp != NULL) { | ||||
error = compressor_flush(di->kdcomp->kdc_stream); | error = compressor_flush(di->kdcomp->kdc_stream); | ||||
if (error == EAGAIN) { | if (error == EAGAIN) { | ||||
/* We have residual data in di->blockbuf. */ | /* We have residual data in di->blockbuf. */ | ||||
error = _dump_append(di, di->blockbuf, 0, di->blocksize); | error = _dump_append(di, di->blockbuf, di->blocksize); | ||||
if (error == 0) | if (error == 0) | ||||
/* Compensate for _dump_append()'s adjustment. */ | /* Compensate for _dump_append()'s adjustment. */ | ||||
di->dumpoff -= di->blocksize - di->kdcomp->kdc_resid; | di->dumpoff -= di->blocksize - di->kdcomp->kdc_resid; | ||||
di->kdcomp->kdc_resid = 0; | di->kdcomp->kdc_resid = 0; | ||||
} | } | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* We now know the size of the compressed dump, so update the | * We now know the size of the compressed dump, so update the | ||||
* header accordingly and recompute parity. | * header accordingly and recompute parity. | ||||
*/ | */ | ||||
kdh->dumplength = htod64(di->dumpoff - di->origdumpoff); | kdh->dumplength = htod64(di->dumpoff - di->origdumpoff); | ||||
kdh->parity = 0; | kdh->parity = 0; | ||||
kdh->parity = kerneldump_parity(kdh); | kdh->parity = kerneldump_parity(kdh); | ||||
compressor_reset(di->kdcomp->kdc_stream); | compressor_reset(di->kdcomp->kdc_stream); | ||||
} | } | ||||
error = dump_write_headers(di, kdh); | error = dump_write_headers(di, kdh); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
(void)dump_write(di, NULL, 0, 0, 0); | (void)dump_write(di, NULL, 0, 0); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
dump_init_header(const struct dumperinfo *di, struct kerneldumpheader *kdh, | dump_init_header(const struct dumperinfo *di, struct kerneldumpheader *kdh, | ||||
const char *magic, uint32_t archver, uint64_t dumplen) | const char *magic, uint32_t archver, uint64_t dumplen) | ||||
{ | { | ||||
size_t dstsize; | size_t dstsize; | ||||
Show All 36 Lines |