Support hibernating to softraid crypto volumes.
authormlarkin <mlarkin@openbsd.org>
Sun, 20 Jul 2014 18:05:21 +0000 (18:05 +0000)
committermlarkin <mlarkin@openbsd.org>
Sun, 20 Jul 2014 18:05:21 +0000 (18:05 +0000)
much help and ok from deraadt@

sys/arch/amd64/amd64/hibernate_machdep.c
sys/arch/i386/i386/hibernate_machdep.c
sys/arch/loongson/loongson/hibernate_machdep.c
sys/dev/acpi/acpi.c
sys/dev/softraid.c
sys/dev/softraidvar.h
sys/kern/subr_hibernate.c
sys/sys/hibernate.h

index 97e2f8c..5b7e148 100644 (file)
@@ -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 <mlarkin@openbsd.org>
@@ -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;
 }
 
index f007857..6b3bea4 100644 (file)
@@ -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 <mlarkin@openbsd.org>
@@ -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;
 }
 
index 8faf1a6..d95aee8 100644 (file)
@@ -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 <dev/ata/wdvar.h>
 #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;
 }
 
index 032f24d..9fdbfba 100644 (file)
@@ -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 <tholo@sigmasoft.com>
  * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
@@ -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;
                }
index 8380bf7..5c31f51 100644 (file)
@@ -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 <marco@peereboom.us>
  * Copyright (c) 2008 Chris Kuethe <ckuethe@openbsd.org>
 #include <dev/softraidvar.h>
 #include <dev/rndvar.h>
 
+#ifdef HIBERNATE
+#include <lib/libsa/aes_xts.h>
+#include <sys/hibernate.h>
+#include <scsi/sdvar.h>
+#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 */
index edf783f..21b89fb 100644 (file)
@@ -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 <marco@peereboom.us>
  * Copyright (c) 2008 Chris Kuethe <ckuethe@openbsd.org>
@@ -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);
index 6f54d7e..c8491bd 100644 (file)
@@ -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 <ariane@stack.nl>
@@ -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);
index e58b0ed..b9bd30d 100644 (file)
@@ -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 <ariane@stack.nl>
@@ -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);