From 7b90f98f7ed0e1bf3f4e3f790c91179eb4627560 Mon Sep 17 00:00:00 2001 From: mlarkin Date: Sun, 20 Jul 2014 18:05:21 +0000 Subject: [PATCH] Support hibernating to softraid crypto volumes. much help and ok from deraadt@ --- sys/arch/amd64/amd64/hibernate_machdep.c | 19 +- sys/arch/i386/i386/hibernate_machdep.c | 20 ++- .../loongson/loongson/hibernate_machdep.c | 20 ++- sys/dev/acpi/acpi.c | 4 +- sys/dev/softraid.c | 164 +++++++++++++++++- sys/dev/softraidvar.h | 6 +- sys/kern/subr_hibernate.c | 6 +- sys/sys/hibernate.h | 4 +- 8 files changed, 221 insertions(+), 22 deletions(-) diff --git a/sys/arch/amd64/amd64/hibernate_machdep.c b/sys/arch/amd64/amd64/hibernate_machdep.c index 97e2f8cf49f..5b7e148780f 100644 --- a/sys/arch/amd64/amd64/hibernate_machdep.c +++ b/sys/arch/amd64/amd64/hibernate_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hibernate_machdep.c,v 1.26 2014/07/16 17:44:16 mlarkin Exp $ */ +/* $OpenBSD: hibernate_machdep.c,v 1.27 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2012 Mike Larkin @@ -46,6 +46,7 @@ #include "acpi.h" #include "wd.h" #include "ahci.h" +#include "softraid.h" #include "sd.h" /* Hibernate support */ @@ -70,9 +71,9 @@ extern struct hibernate_state *hibernate_state; * Returns the hibernate write I/O function to use on this machine */ hibio_fn -get_hibernate_io_function(void) +get_hibernate_io_function(dev_t dev) { - char *blkname = findblkname(major(swdevt[0].sw_dev)); + char *blkname = findblkname(major(dev)); if (blkname == NULL) return NULL; @@ -84,20 +85,30 @@ get_hibernate_io_function(void) return wd_hibernate_io; } #endif -#if NAHCI > 0 && NSD > 0 +#if NSD > 0 if (strcmp(blkname, "sd") == 0) { extern struct cfdriver sd_cd; extern int ahci_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page); + extern int sr_hibernate_io(dev_t dev, daddr_t blkno, + vaddr_t addr, size_t size, int op, void *page); struct device *dv; +#if NAHCI > 0 dv = disk_lookup(&sd_cd, DISKUNIT(swdevt[0].sw_dev)); if (dv && dv->dv_parent && dv->dv_parent->dv_parent && strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name, "ahci") == 0) return ahci_hibernate_io; +#endif +#if NSOFTRAID > 0 + if (dv && dv->dv_parent && dv->dv_parent->dv_parent && + strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name, + "softraid") == 0) + return sr_hibernate_io; } #endif +#endif /* NSD > 0 */ return NULL; } diff --git a/sys/arch/i386/i386/hibernate_machdep.c b/sys/arch/i386/i386/hibernate_machdep.c index f0078574a98..6b3bea40635 100644 --- a/sys/arch/i386/i386/hibernate_machdep.c +++ b/sys/arch/i386/i386/hibernate_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hibernate_machdep.c,v 1.36 2014/07/16 17:44:16 mlarkin Exp $ */ +/* $OpenBSD: hibernate_machdep.c,v 1.37 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2011 Mike Larkin @@ -43,6 +43,7 @@ #include "acpi.h" #include "wd.h" #include "ahci.h" +#include "softraid.h" #include "sd.h" /* Hibernate support */ @@ -68,12 +69,13 @@ extern struct hibernate_state *hibernate_state; * Returns the hibernate write I/O function to use on this machine */ hibio_fn -get_hibernate_io_function(void) +get_hibernate_io_function(dev_t dev) { - char *blkname = findblkname(major(swdevt[0].sw_dev)); + char *blkname = findblkname(major(dev)); if (blkname == NULL) return NULL; + #if NWD > 0 if (strcmp(blkname, "wd") == 0) { extern int wd_hibernate_io(dev_t dev, daddr_t blkno, @@ -81,20 +83,30 @@ get_hibernate_io_function(void) return wd_hibernate_io; } #endif -#if NAHCI > 0 && NSD > 0 +#if NSD > 0 if (strcmp(blkname, "sd") == 0) { extern struct cfdriver sd_cd; extern int ahci_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page); + extern int sr_hibernate_io(dev_t dev, daddr_t blkno, + vaddr_t addr, size_t size, int op, void *page); struct device *dv; +#if NAHCI > 0 dv = disk_lookup(&sd_cd, DISKUNIT(swdevt[0].sw_dev)); if (dv && dv->dv_parent && dv->dv_parent->dv_parent && strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name, "ahci") == 0) return ahci_hibernate_io; +#endif +#if NSOFTRAID > 0 + if (dv && dv->dv_parent && dv->dv_parent->dv_parent && + strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name, + "softraid") == 0) + return sr_hibernate_io; } #endif +#endif /* NSD > 0 */ return NULL; } diff --git a/sys/arch/loongson/loongson/hibernate_machdep.c b/sys/arch/loongson/loongson/hibernate_machdep.c index 8faf1a61b59..d95aee8ce7b 100644 --- a/sys/arch/loongson/loongson/hibernate_machdep.c +++ b/sys/arch/loongson/loongson/hibernate_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hibernate_machdep.c,v 1.5 2014/07/09 15:03:12 mlarkin Exp $ */ +/* $OpenBSD: hibernate_machdep.c,v 1.6 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2013 Paul Irofti. @@ -41,6 +41,7 @@ #include "wd.h" #include "ahci.h" +#include "softraid.h" #include "sd.h" #if NWD > 0 @@ -48,7 +49,6 @@ #include #endif - /* * Loongson MD Hibernate functions * @@ -59,9 +59,9 @@ * Returns the hibernate write I/O function to use on this machine */ hibio_fn -get_hibernate_io_function(void) +get_hibernate_io_function(dev_t dev) { - char *blkname = findblkname(major(swdevt[0].sw_dev)); + char *blkname = findblkname(major(dev)); if (blkname == NULL) return NULL; @@ -70,20 +70,30 @@ get_hibernate_io_function(void) if (strcmp(blkname, "wd") == 0) return wd_hibernate_io; #endif -#if NAHCI > 0 && NSD > 0 +#if NSD > 0 if (strcmp(blkname, "sd") == 0) { extern struct cfdriver sd_cd; extern int ahci_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page); + extern int sr_hibernate_io(dev_t dev, daddr_t blkno, + vaddr_t addr, size_t size, int op, void *page); struct device *dv; +#if NAHCI > 0 dv = disk_lookup(&sd_cd, DISKUNIT(swdevt[0].sw_dev)); if (dv && dv->dv_parent && dv->dv_parent->dv_parent && strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name, "ahci") == 0) return ahci_hibernate_io; +#endif +#if NSOFTRAID > 0 + if (dv && dv->dv_parent && dv->dv_parent->dv_parent && + strcmp(dv->dv_parent->dv_parent->dv_cfdata->cf_driver->cd_name, + "softraid") == 0) + return sr_hibernate_io; } #endif +#endif /* NSD > 0 */ return NULL; } diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index 032f24d4ecc..9fdbfba3e98 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.266 2014/07/16 07:42:50 mlarkin Exp $ */ +/* $OpenBSD: acpi.c,v 1.267 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Jordan Hargrave @@ -2625,7 +2625,7 @@ acpiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) error = EBADF; break; } - if (get_hibernate_io_function() == NULL) { + if (get_hibernate_io_function(swdevt[0].sw_dev) == NULL) { error = EOPNOTSUPP; break; } diff --git a/sys/dev/softraid.c b/sys/dev/softraid.c index 8380bf75193..5c31f51fd8d 100644 --- a/sys/dev/softraid.c +++ b/sys/dev/softraid.c @@ -1,4 +1,4 @@ -/* $OpenBSD: softraid.c,v 1.335 2014/07/13 23:10:23 deraadt Exp $ */ +/* $OpenBSD: softraid.c,v 1.336 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2007, 2008, 2009 Marco Peereboom * Copyright (c) 2008 Chris Kuethe @@ -57,6 +57,12 @@ #include #include +#ifdef HIBERNATE +#include +#include +#include +#endif /* HIBERNATE */ + /* #define SR_FANCY_STATS */ #ifdef SR_DEBUG @@ -4968,3 +4974,159 @@ sr_dump_mem(u_int8_t *p, int len) } #endif /* SR_DEBUG */ + +#ifdef HIBERNATE +/* + * Side-effect free (no malloc, printf, pool, splx) softraid crypto writer. + * + * This function must perform the following: + * 1. Determine the underlying device's own side-effect free I/O function + * (eg, ahci_hibernate_io, wd_hibernate_io, etc). + * 2. Store enough information in the provided page argument for subsequent + * I/O calls (such as the crypto discipline structure for the keys, the + * offset of the softraid partition on the underlying disk, as well as + * the offset of the swap partition within the crypto volume. + * 3. Encrypt the incoming data using the sr_discipline keys, then pass + * the request to the underlying device's own I/O function. + */ +int +sr_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page) +{ + /* Struct for stashing data obtained on HIB_INIT. + * XXX + * We share the page with the underlying device's own + * side-effect free I/O function, so we pad our data to + * the end of the page. Presently this does not overlap + * with either of the two other side-effect free i/o + * functions (ahci/wd). + */ + struct { + char pad[3072]; + struct sr_discipline *srd; + hibio_fn subfn; /* underlying device i/o fn */ + dev_t subdev; /* underlying device dev_t */ + daddr_t sr_swapoff; /* ofs of swap part in sr volume */ + char buf[DEV_BSIZE]; /* encryption performed into this buf */ + } *my = page; + extern struct cfdriver sd_cd; + char errstr[128], *dl_ret; + struct sr_chunk *schunk; + struct sd_softc *sd; + struct aes_xts_ctx ctx; + struct sr_softc *sc; + struct device *dv; + daddr_t key_blkno; + uint32_t sub_raidoff; /* ofs of sr part in underlying dev */ + struct disklabel dl; + size_t i, j; + u_char iv[8]; + + /* + * In HIB_INIT, we are passed the swap partition size and offset + * in 'size' and 'blkno' respectively. These are relative to the + * start of the softraid partition, and we need to save these + * for later translation to the underlying device's layout. + */ + if (op == HIB_INIT) { + dv = disk_lookup(&sd_cd, DISKUNIT(dev)); + sd = (struct sd_softc *)dv; + sc = (struct sr_softc *)dv->dv_parent->dv_parent; + + /* + * Look up the sr discipline. This is used to determine + * if we are SR crypto and what the underlying device is. + */ + my->srd = sc->sc_targets[sd->sc_link->target]; + DNPRINTF(SR_D_MISC, "sr_hibernate_io: discipline is %s\n", + my->srd->sd_name); + if (strncmp(my->srd->sd_name, "CRYPTO", 10)) + return (ENOTSUP); + + /* Find the underlying device */ + schunk = my->srd->sd_vol.sv_chunks[0]; + my->subdev = schunk->src_dev_mm; + + /* + * Find the appropriate underlying device side effect free + * I/O function, based on the type of device it is. + */ + my->subfn = get_hibernate_io_function(my->subdev); + + /* + * Find block offset where this raid partition is on + * the underlying disk. + */ + dl_ret = disk_readlabel(&dl, my->subdev, errstr, + sizeof(errstr)); + if (dl_ret) { + printf("Hibernate error reading disklabel: %s\n", dl_ret); + return (ENOTSUP); + } + + if (dl.d_partitions[DISKPART(my->subdev)].p_fstype != FS_RAID || + DL_GETPSIZE(&dl.d_partitions[DISKPART(my->subdev)]) == 0) + return (ENOTSUP); + + /* Find the offset of the SR part in the underlying device */ + sub_raidoff = my->srd->sd_meta->ssd_data_offset + + DL_GETPOFFSET(&dl.d_partitions[DISKPART(my->subdev)]); + DNPRINTF(SR_D_MISC,"sr_hibernate_io: blk trans ofs: %d blks\n", + sub_raidoff); + + /* Save the offset of the swap partition in the SR disk */ + my->sr_swapoff = blkno; + + /* Initialize the sub-device */ + return my->subfn(my->subdev, sub_raidoff + blkno, + addr, size, op, page); + } + + /* Hibernate only uses (and we only support) writes */ + if (op != HIB_W) + return (ENOTSUP); + + /* + * Blocks act as the IV for the encryption. These block numbers + * are relative to the start of the sr partition, but the 'blkno' + * passed above is relative to the start of the swap partition + * inside the sr partition, so bias appropriately. + */ + key_blkno = my->sr_swapoff + blkno; + + /* Process each disk block one at a time. */ + for (i = 0; i < size; i += DEV_BSIZE) { + int res; + + bzero(&ctx, sizeof(ctx)); + + /* + * Set encryption key (from the sr discipline stashed + * during HIB_INIT. This code is based on the softraid + * bootblock code. + */ + aes_xts_setkey(&ctx, my->srd->mds.mdd_crypto.scr_key[0], 64); + /* We encrypt DEV_BSIZE bytes at a time in my->buf */ + bcopy(((char *)addr) + i, my->buf, DEV_BSIZE); + + /* Block number is the IV */ + bcopy(&key_blkno, &iv, sizeof(key_blkno)); + aes_xts_reinit(&ctx, iv); + + /* Encrypt DEV_BSIZE bytes, AES_XTS_BLOCKSIZE bytes at a time */ + for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE) + aes_xts_encrypt(&ctx, my->buf + j); + + /* + * Write one block out from my->buf to the underlying device + * using its own side-effect free I/O function. + */ + res = my->subfn(my->subdev, blkno + (i / DEV_BSIZE), + (vaddr_t)(my->buf), DEV_BSIZE, op, page); + if (res != 0) + return (res); + key_blkno++; + } + + return (0); +} +#endif /* HIBERNATE */ diff --git a/sys/dev/softraidvar.h b/sys/dev/softraidvar.h index edf783fa0ac..21b89fbc744 100644 --- a/sys/dev/softraidvar.h +++ b/sys/dev/softraidvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: softraidvar.h,v 1.156 2014/07/12 07:39:11 blambert Exp $ */ +/* $OpenBSD: softraidvar.h,v 1.157 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2006 Marco Peereboom * Copyright (c) 2008 Chris Kuethe @@ -730,6 +730,10 @@ int sr_crypto_create_keys(struct sr_discipline *); struct sr_chunk * sr_crypto_create_key_disk(struct sr_discipline *, dev_t); struct sr_chunk * sr_crypto_read_key_disk(struct sr_discipline *, dev_t); +/* Hibernate I/O function */ +int sr_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, + size_t size, int op, void *page); + #ifdef SR_DEBUG void sr_dump_block(void *, int); void sr_dump_mem(u_int8_t *, int); diff --git a/sys/kern/subr_hibernate.c b/sys/kern/subr_hibernate.c index 6f54d7e3470..c8491bd4ee4 100644 --- a/sys/kern/subr_hibernate.c +++ b/sys/kern/subr_hibernate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_hibernate.c,v 1.97 2014/07/16 07:42:51 mlarkin Exp $ */ +/* $OpenBSD: subr_hibernate.c,v 1.98 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2011 Ariane van der Steldt @@ -686,7 +686,7 @@ get_hibernate_info(union hibernate_info *hib, int suspend) #endif /* ! NO_PROPOLICE */ /* Determine I/O function to use */ - hib->io_func = get_hibernate_io_function(); + hib->io_func = get_hibernate_io_function(swdevt[0].sw_dev); if (hib->io_func == NULL) return (1); @@ -694,7 +694,7 @@ get_hibernate_info(union hibernate_info *hib, int suspend) hib->dev = swdevt[0].sw_dev; /* Read disklabel (used to calculate signature and image offsets) */ - dl_ret = disk_readlabel(&dl, hib->dev, err_string, 128); + dl_ret = disk_readlabel(&dl, hib->dev, err_string, sizeof(err_string)); if (dl_ret) { printf("Hibernate error reading disklabel: %s\n", dl_ret); diff --git a/sys/sys/hibernate.h b/sys/sys/hibernate.h index e58b0ed4fa6..b9bd30d7744 100644 --- a/sys/sys/hibernate.h +++ b/sys/sys/hibernate.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hibernate.h,v 1.35 2014/07/16 07:42:51 mlarkin Exp $ */ +/* $OpenBSD: hibernate.h,v 1.36 2014/07/20 18:05:21 mlarkin Exp $ */ /* * Copyright (c) 2011 Ariane van der Steldt @@ -115,7 +115,7 @@ void uvm_pmr_free_piglet(vaddr_t, vsize_t); int uvm_page_rle(paddr_t); void uvmpd_hibernate(void); -hibio_fn get_hibernate_io_function(void); +hibio_fn get_hibernate_io_function(dev_t); int get_hibernate_info(union hibernate_info *, int); int hibernate_zlib_reset(union hibernate_info *, int); -- 2.20.1