changeset 18843:2f1019fa0103

loader: add skein/edonr support illumos issue #8905
author Toomas Soome <tsoome@me.com>
date Mon, 04 Dec 2017 12:26:50 +0200
parents 969c95446ef0
children 30151770e575
files include/sys/debug.h include/sys/systm.h usr/src/boot/sys/boot/efi/boot1/Makefile usr/src/boot/sys/boot/i386/gptzfsboot/gptldr.S usr/src/boot/sys/boot/zfs/Makefile.com usr/src/boot/sys/boot/zfs/zfsimpl.c usr/src/boot/sys/cddl/boot/zfs/edonr_zfs.c usr/src/boot/sys/cddl/boot/zfs/skein_zfs.c usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h usr/src/boot/sys/cddl/boot/zfs/zfssubr.c usr/src/boot/sys/sys/types.h
diffstat 11 files changed, 410 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/include/sys/debug.h	Wed Jun 01 18:53:15 2016 +0300
+++ b/include/sys/debug.h	Mon Dec 04 12:26:50 2017 +0200
@@ -36,7 +36,9 @@
 #ifndef _SYS_DEBUG_H
 #define	_SYS_DEBUG_H
 
+#if !defined(_STANDALONE)
 #include <sys/isa_defs.h>
+#endif
 #include <sys/types.h>
 #include <sys/note.h>
 
--- a/include/sys/systm.h	Wed Jun 01 18:53:15 2016 +0300
+++ b/include/sys/systm.h	Mon Dec 04 12:26:50 2017 +0200
@@ -31,15 +31,21 @@
 #ifndef _SYS_SYSTM_H
 #define	_SYS_SYSTM_H
 
+#if defined(_STANDALONE)
+#include <sys/cdefs.h>
+#include <string.h>
+#else
 #include <sys/types.h>
 #include <sys/t_lock.h>
 #include <sys/proc.h>
 #include <sys/dditypes.h>
+#endif
 
 #ifdef	__cplusplus
 extern "C" {
 #endif
 
+#if !defined(_STANDALONE)
 /*
  * The pc_t is the type of the kernel's program counter.  In general, a
  * pc_t is a uintptr_t -- except for a sparcv9 kernel, in which case all
@@ -503,6 +509,7 @@
 #define	__lintzero 0
 #endif	/* __lint */
 #endif /* _KERNEL || _BOOT */
+#endif /* !_STANDALONE */
 
 #ifdef	__cplusplus
 }
--- a/usr/src/boot/sys/boot/efi/boot1/Makefile	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/boot/efi/boot1/Makefile	Mon Dec 04 12:26:50 2017 +0200
@@ -33,7 +33,7 @@
 
 ASFLAGS=-m64 -fPIC
 CFLAGS= -O2
-CPPFLAGS=	-nostdinc
+CPPFLAGS=	-nostdinc -D_STANDALONE
 CPPFLAGS +=	-I.
 CPPFLAGS +=	-I./../include
 CPPFLAGS +=	-I./../include/${MACHINE}
@@ -51,6 +51,9 @@
 # Always add MI sources and REGULAR efi loader bits
 CPPFLAGS +=	-I./../../common
 
+# For sys/skein.h
+CPPFLAGS +=	-I$(SRCTOP)/include
+
 include ../Makefile.inc
 
 FILES=  boot1.efi
--- a/usr/src/boot/sys/boot/i386/gptzfsboot/gptldr.S	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/boot/i386/gptzfsboot/gptldr.S	Mon Dec 04 12:26:50 2017 +0200
@@ -45,7 +45,7 @@
 /* Misc. Constants */
 		.set SIZ_PAG,0x1000		# Page size
 		.set SIZ_SEC,0x200		# Sector size
-		.set COPY_BLKS,0x4		# Number of blocks
+		.set COPY_BLKS,0x8		# Number of blocks
 						# to copy for boot2
 		.set COPY_BLK_SZ,0x8000		# Copy in 32k blocks; must be
 						# a multiple of 16 bytes
--- a/usr/src/boot/sys/boot/zfs/Makefile.com	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/boot/zfs/Makefile.com	Mon Dec 04 12:26:50 2017 +0200
@@ -29,13 +29,21 @@
 CPPFLAGS=
 
 SRCS +=		../zfs.c ../gzip.c
-OBJS +=		zfs.o gzip.o
+SRCS +=		$(SRC)/common/crypto/edonr/edonr.c
+SRCS +=		$(SRC)/common/crypto/skein/skein.c
+SRCS +=		$(SRC)/common/crypto/skein/skein_iv.c
+SRCS +=		$(SRC)/common/crypto/skein/skein_block.c
+OBJS +=		zfs.o gzip.o edonr.o skein.o skein_iv.o skein_block.o
 
-CFLAGS= -O2 -nostdinc -I../../../../include -I../../..
+CFLAGS= -O2 -D_STANDALONE -nostdinc -I../../../../include -I../../..
 CFLAGS +=	-I../../common -I../../.. -I.. -I.
 CFLAGS +=	-I../../../../lib/libstand
 CFLAGS +=	-I../../../../lib/libz
 CFLAGS +=	-I../../../cddl/boot/zfs
+CFLAGS +=	-I$(SRCTOP)/include
+
+# Do not unroll skein loops, reduce code size
+CFLAGS +=	-DSKEIN_LOOP=111
 
 CFLAGS +=	-ffreestanding
 CFLAGS +=	-mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 -msoft-float
@@ -59,4 +67,10 @@
 %.o:	../%.c
 	$(COMPILE.c) -o $@ $<
 
+%.o:	$(SRC)/common/crypto/edonr/%.c
+	$(COMPILE.c) -o $@ $<
+
+%.o:	$(SRC)/common/crypto/skein/%.c
+	$(COMPILE.c) -o $@ $<
+
 zfs.o: ../zfsimpl.c
--- a/usr/src/boot/sys/boot/zfs/zfsimpl.c	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/boot/zfs/zfsimpl.c	Mon Dec 04 12:26:50 2017 +0200
@@ -58,6 +58,8 @@
 	"com.delphix:embedded_data",
 	"org.open-zfs:large_blocks",
 	"org.illumos:sha512",
+	"org.illumos:skein",
+	"org.illumos:edonr",
 	"org.zfsonlinux:large_dnode",
 	"com.joyent:multi_vdev_crash_dump",
 	NULL
@@ -79,6 +81,9 @@
 static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf);
 static int zfs_get_root(const spa_t *spa, uint64_t *objid);
 static int zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result);
+static int zap_lookup(const spa_t *spa, const dnode_phys_t *dnode,
+    const char *name, uint64_t integer_size, uint64_t num_integers,
+    void *value);
 
 static void
 zfs_init(void)
@@ -421,7 +426,7 @@
 	rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize);
 	if (rc)
 		return (rc);
-	if (bp && zio_checksum_verify(bp, buf))
+	if (bp && zio_checksum_verify(vdev->spa, bp, buf))
 		return (EIO);
 
 	return (0);
@@ -1091,8 +1096,10 @@
 	STAILQ_FOREACH(pool_vdev, &spa->spa_vdevs, v_childlink)
 		if (top_vdev == pool_vdev)
 			break;
-	if (!pool_vdev && top_vdev)
+	if (!pool_vdev && top_vdev) {
+		top_vdev->spa = spa;
 		STAILQ_INSERT_TAIL(&spa->spa_vdevs, top_vdev, v_childlink);
+	}
 
 	/*
 	 * We should already have created an incomplete vdev for this
@@ -1153,6 +1160,7 @@
 	}
 	zfs_free(upbuf, VDEV_UBERBLOCK_SIZE(vdev));
 
+	vdev->spa = spa;
 	if (spap != NULL)
 		*spap = spa;
 	return (0);
@@ -1201,7 +1209,7 @@
 		pbuf += BP_GET_PSIZE(gbp);
 	}
 
-	if (zio_checksum_verify(bp, buf))
+	if (zio_checksum_verify(spa, bp, buf))
 		return (EIO);
 	return (0);
 }
@@ -1451,12 +1459,93 @@
 	return value;
 }
 
+static void
+stv(int len, void *addr, uint64_t value)
+{
+	switch (len) {
+	case 1:
+		*(uint8_t *)addr = value;
+		return;
+	case 2:
+		*(uint16_t *)addr = value;
+		return;
+	case 4:
+		*(uint32_t *)addr = value;
+		return;
+	case 8:
+		*(uint64_t *)addr = value;
+		return;
+	}
+}
+
+/*
+ * Extract a array from a zap leaf entry.
+ */
+static void
+fzap_leaf_array(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc,
+    uint64_t integer_size, uint64_t num_integers, void *buf)
+{
+	uint64_t array_int_len = zc->l_entry.le_value_intlen;
+	uint64_t value = 0;
+	uint64_t *u64 = buf;
+	char *p = buf;
+	int len = MIN(zc->l_entry.le_value_numints, num_integers);
+	int chunk = zc->l_entry.le_value_chunk;
+	int byten = 0;
+
+	if (integer_size == 8 && len == 1) {
+		*u64 = fzap_leaf_value(zl, zc);
+		return;
+	}
+
+	while (len > 0) {
+		struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(zl, chunk).l_array;
+		int i;
+
+		ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(zl));
+		for (i = 0; i < ZAP_LEAF_ARRAY_BYTES && len > 0; i++) {
+			value = (value << 8) | la->la_array[i];
+			byten++;
+			if (byten == array_int_len) {
+				stv(integer_size, p, value);
+				byten = 0;
+				len--;
+				if (len == 0)
+					return;
+				p += integer_size;
+			}
+		}
+		chunk = la->la_next;
+	}
+}
+
+static int
+fzap_check_size(uint64_t integer_size, uint64_t num_integers)
+{
+
+	switch (integer_size) {
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	if (integer_size * num_integers > ZAP_MAXVALUELEN)
+		return (E2BIG);
+
+	return (0);
+}
+
 /*
  * Lookup a value in a fatzap directory. Assumes that the zap scratch
  * buffer contains the directory header.
  */
 static int
-fzap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value)
+fzap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name,
+    uint64_t integer_size, uint64_t num_integers, void *value)
 {
 	int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
 	zap_phys_t zh = *(zap_phys_t *) zap_scratch;
@@ -1468,6 +1557,9 @@
 	if (zh.zap_magic != ZAP_MAGIC)
 		return (EIO);
 
+	if ((rc = fzap_check_size(integer_size, num_integers)) != 0)
+		return (rc);
+
 	z.zap_block_shift = ilog2(bsize);
 	z.zap_phys = (zap_phys_t *) zap_scratch;
 
@@ -1523,9 +1615,10 @@
 		zc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_next);
 	}
 	if (fzap_name_equal(&zl, zc, name)) {
-		if (zc->l_entry.le_value_intlen * zc->l_entry.le_value_numints > 8)
-			return (E2BIG);
-		*value = fzap_leaf_value(&zl, zc);
+		if (zc->l_entry.le_value_intlen > integer_size)
+			return (EINVAL);
+
+		fzap_leaf_array(&zl, zc, integer_size, num_integers, value);
 		return (0);
 	}
 
@@ -1536,7 +1629,8 @@
  * Lookup a name in a zap object and return its value as a uint64_t.
  */
 static int
-zap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value)
+zap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name,
+    uint64_t integer_size, uint64_t num_integers, void *value)
 {
 	int rc;
 	uint64_t zap_type;
@@ -1549,8 +1643,10 @@
 	zap_type = *(uint64_t *) zap_scratch;
 	if (zap_type == ZBT_MICRO)
 		return mzap_lookup(dnode, name, value);
-	else if (zap_type == ZBT_HEADER)
-		return fzap_lookup(spa, dnode, name, value);
+	else if (zap_type == ZBT_HEADER) {
+		return fzap_lookup(spa, dnode, name, integer_size,
+		    num_integers, value);
+	}
 	printf("ZFS: invalid zap_type=%d\n", (int)zap_type);
 	return (EIO);
 }
@@ -1889,7 +1985,8 @@
 
 	if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir))
 		return (EIO);
-	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &dir_obj))
+	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (dir_obj),
+	    1, &dir_obj))
 		return (EIO);
 
 	p = name;
@@ -1919,7 +2016,8 @@
 			return (EIO);
 
 		/* Actual loop condition #2. */
-		if (zap_lookup(spa, &child_dir_zap, element, &dir_obj) != 0)
+		if (zap_lookup(spa, &child_dir_zap, element, sizeof (dir_obj),
+		    1, &dir_obj) != 0)
 			return (ENOENT);
 	}
 
@@ -2048,9 +2146,9 @@
 	/*
 	 * Lookup the pool_props and see if we can find a bootfs.
 	 */
-	if (zap_lookup(spa, &dir, DMU_POOL_PROPS, &props) == 0
+	if (zap_lookup(spa, &dir, DMU_POOL_PROPS, sizeof (props), 1, &props) == 0
 	     && objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0
-	     && zap_lookup(spa, &propdir, "bootfs", &bootfs) == 0
+	     && zap_lookup(spa, &propdir, "bootfs", sizeof (bootfs), 1, &bootfs) == 0
 	     && bootfs != 0)
 	{
 		*objid = bootfs;
@@ -2059,7 +2157,7 @@
 	/*
 	 * Lookup the root dataset directory
 	 */
-	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &root)
+	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (root), 1, &root)
 	    || objset_get_dnode(spa, &spa->spa_mos, root, &dir)) {
 		printf("ZFS: can't find root dsl_dir\n");
 		return (EIO);
@@ -2134,7 +2232,7 @@
 	    &dir)) != 0)
 		return (rc);
 	if ((rc = zap_lookup(spa, &dir, DMU_POOL_FEATURES_FOR_READ,
-	    &objnum)) != 0) {
+	    sizeof (objnum), 1, &objnum)) != 0) {
 		/*
 		 * It is older pool without features. As we have already
 		 * tested the label, just return without raising the error.
@@ -2166,6 +2264,7 @@
 static int
 zfs_spa_init(spa_t *spa)
 {
+	dnode_phys_t dir;
 	int rc;
 
 	if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
@@ -2177,6 +2276,17 @@
 		return (EIO);
 	}
 
+	if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT,
+	    &dir)) {
+		printf("ZFS: failed to read pool %s directory object\n",
+		    spa->spa_name);
+		return (EIO);
+	}
+	/* this is allowed to fail, older pools do not have salt */
+	rc = zap_lookup(spa, &dir, DMU_POOL_CHECKSUM_SALT, 1,
+	    sizeof (spa->spa_cksum_salt.zcs_bytes),
+	    spa->spa_cksum_salt.zcs_bytes);
+
 	rc = check_mos_features(spa);
 	if (rc != 0) {
 		printf("ZFS: pool %s is not supported\n", spa->spa_name);
@@ -2329,7 +2439,7 @@
 		return (rc);
 	}
 
-	rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, &objnum);
+	rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, sizeof(objnum), 1, &objnum);
 	if (rc) {
 		free(entry);
 		return (rc);
@@ -2389,7 +2499,7 @@
 			goto done;
 		}
 
-		rc = zap_lookup(spa, &dn, element, &objnum);
+		rc = zap_lookup(spa, &dn, element, sizeof(objnum), 1, &objnum);
 		if (rc)
 			goto done;
 		objnum = ZFS_DIRENT_OBJ(objnum);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/boot/sys/cddl/boot/zfs/edonr_zfs.c	Mon Dec 04 12:26:50 2017 +0200
@@ -0,0 +1,100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://opensource.org/licenses/CDDL-1.0.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2013 Saso Kiselkov.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#include <sys/edonr.h>
+
+#define	EDONR_MODE		512
+#define	EDONR_BLOCK_SIZE	EdonR512_BLOCK_SIZE
+
+/*
+ * Native zio_checksum interface for the Edon-R hash function.
+ */
+/*ARGSUSED*/
+static void
+zio_checksum_edonr_native(const void *buf, uint64_t size,
+    const void *ctx_template, zio_cksum_t *zcp)
+{
+	uint8_t		digest[EDONR_MODE / 8];
+	EdonRState	ctx;
+
+	ASSERT(ctx_template != NULL);
+	bcopy(ctx_template, &ctx, sizeof (ctx));
+	EdonRUpdate(&ctx, buf, size * 8);
+	EdonRFinal(&ctx, digest);
+	bcopy(digest, zcp->zc_word, sizeof (zcp->zc_word));
+}
+
+/*
+ * Byteswapped zio_checksum interface for the Edon-R hash function.
+ */
+static void
+zio_checksum_edonr_byteswap(const void *buf, uint64_t size,
+    const void *ctx_template, zio_cksum_t *zcp)
+{
+	zio_cksum_t	tmp;
+
+	zio_checksum_edonr_native(buf, size, ctx_template, &tmp);
+	zcp->zc_word[0] = BSWAP_64(zcp->zc_word[0]);
+	zcp->zc_word[1] = BSWAP_64(zcp->zc_word[1]);
+	zcp->zc_word[2] = BSWAP_64(zcp->zc_word[2]);
+	zcp->zc_word[3] = BSWAP_64(zcp->zc_word[3]);
+}
+
+static void *
+zio_checksum_edonr_tmpl_init(const zio_cksum_salt_t *salt)
+{
+	EdonRState	*ctx;
+	uint8_t		salt_block[EDONR_BLOCK_SIZE];
+
+	/*
+	 * Edon-R needs all but the last hash invocation to be on full-size
+	 * blocks, but the salt is too small. Rather than simply padding it
+	 * with zeros, we expand the salt into a new salt block of proper
+	 * size by double-hashing it (the new salt block will be composed of
+	 * H(salt) || H(H(salt))).
+	 */
+	EdonRHash(EDONR_MODE, salt->zcs_bytes, sizeof (salt->zcs_bytes) * 8,
+	    salt_block);
+	EdonRHash(EDONR_MODE, salt_block, EDONR_MODE, salt_block +
+	    EDONR_MODE / 8);
+
+	/*
+	 * Feed the new salt block into the hash function - this will serve
+	 * as our MAC key.
+	 */
+	ctx = malloc(sizeof (*ctx));
+	bzero(ctx, sizeof (*ctx));
+	EdonRInit(ctx, EDONR_MODE);
+	EdonRUpdate(ctx, salt_block, sizeof (salt_block) * 8);
+	return (ctx);
+}
+
+static void
+zio_checksum_edonr_tmpl_free(void *ctx_template)
+{
+	EdonRState	*ctx = ctx_template;
+
+	bzero(ctx, sizeof (*ctx));
+	free(ctx);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/boot/sys/cddl/boot/zfs/skein_zfs.c	Mon Dec 04 12:26:50 2017 +0200
@@ -0,0 +1,90 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://opensource.org/licenses/CDDL-1.0.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2013 Saso Kiselkov.  All rights reserved.
+ */
+#include <sys/skein.h>
+
+/*
+ * Computes a native 256-bit skein MAC checksum. Please note that this
+ * function requires the presence of a ctx_template that should be allocated
+ * using zio_checksum_skein_tmpl_init.
+ */
+/*ARGSUSED*/
+static void
+zio_checksum_skein_native(const void *buf, uint64_t size,
+    const void *ctx_template, zio_cksum_t *zcp)
+{
+	Skein_512_Ctxt_t	ctx;
+
+	ASSERT(ctx_template != NULL);
+	bcopy(ctx_template, &ctx, sizeof (ctx));
+	(void) Skein_512_Update(&ctx, buf, size);
+	(void) Skein_512_Final(&ctx, (uint8_t *)zcp);
+	bzero(&ctx, sizeof (ctx));
+}
+
+/*
+ * Byteswapped version of zio_checksum_skein_native. This just invokes
+ * the native checksum function and byteswaps the resulting checksum (since
+ * skein is internally endian-insensitive).
+ */
+static void
+zio_checksum_skein_byteswap(const void *buf, uint64_t size,
+    const void *ctx_template, zio_cksum_t *zcp)
+{
+	zio_cksum_t	tmp;
+
+	zio_checksum_skein_native(buf, size, ctx_template, &tmp);
+	zcp->zc_word[0] = BSWAP_64(tmp.zc_word[0]);
+	zcp->zc_word[1] = BSWAP_64(tmp.zc_word[1]);
+	zcp->zc_word[2] = BSWAP_64(tmp.zc_word[2]);
+	zcp->zc_word[3] = BSWAP_64(tmp.zc_word[3]);
+}
+
+/*
+ * Allocates a skein MAC template suitable for using in skein MAC checksum
+ * computations and returns a pointer to it.
+ */
+static void *
+zio_checksum_skein_tmpl_init(const zio_cksum_salt_t *salt)
+{
+	Skein_512_Ctxt_t	*ctx;
+
+	ctx = malloc(sizeof (*ctx));
+	bzero(ctx, sizeof (*ctx));
+	(void) Skein_512_InitExt(ctx, sizeof (zio_cksum_t) * 8, 0,
+	    salt->zcs_bytes, sizeof (salt->zcs_bytes));
+	return (ctx);
+}
+
+/*
+ * Frees a skein context template previously allocated using
+ * zio_checksum_skein_tmpl_init.
+ */
+static void
+zio_checksum_skein_tmpl_free(void *ctx_template)
+{
+	Skein_512_Ctxt_t	*ctx = ctx_template;
+
+	bzero(ctx, sizeof (*ctx));
+	free(ctx);
+}
--- a/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h	Mon Dec 04 12:26:50 2017 +0200
@@ -1071,6 +1071,8 @@
 	DMU_OST_NUMTYPES
 } dmu_objset_type_t;
 
+#define	ZAP_MAXVALUELEN	(1024 * 8)
+
 /*
  * header for all bonus and spill buffers.
  * The header has a fixed portion with a variable number
@@ -1206,6 +1208,7 @@
 #define	DMU_POOL_DEFLATE		"deflate"
 #define	DMU_POOL_HISTORY		"history"
 #define	DMU_POOL_PROPS			"pool_props"
+#define	DMU_POOL_CHECKSUM_SALT		"org.illumos:checksum_salt"
 
 #define	ZAP_MAGIC 0x2F52AB2ABULL
 
@@ -1511,6 +1514,7 @@
  * In-core vdev representation.
  */
 struct vdev;
+struct spa;
 typedef int vdev_phys_read_t(struct vdev *vdev, void *priv,
     off_t offset, void *buf, size_t bytes);
 typedef int vdev_read_t(struct vdev *vdev, const blkptr_t *bp,
@@ -1535,6 +1539,7 @@
 	vdev_phys_read_t *v_phys_read;	/* read from raw leaf vdev */
 	vdev_read_t	*v_read;	/* read from vdev */
 	void		*v_read_priv;	/* private data for read function */
+	struct spa	*spa;		/* link to spa */
 } vdev_t;
 
 /*
@@ -1550,6 +1555,8 @@
 	struct uberblock spa_uberblock;	/* best uberblock so far */
 	vdev_list_t	spa_vdevs;	/* list of all toplevel vdevs */
 	objset_phys_t	spa_mos;	/* MOS for this pool */
+	zio_cksum_salt_t spa_cksum_salt;	/* secret salt for cksum */
+	void		*spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
 	int		spa_inited;	/* initialized */
 	vdev_t		*spa_boot_vdev;	/* boot device for kernel */
 } spa_t;
--- a/usr/src/boot/sys/cddl/boot/zfs/zfssubr.c	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/cddl/boot/zfs/zfssubr.c	Mon Dec 04 12:26:50 2017 +0200
@@ -105,6 +105,8 @@
 
 #include "fletcher.c"
 #include "sha256.c"
+#include "skein_zfs.c"
+#include "edonr_zfs.c"
 
 static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
 	{{NULL, NULL}, NULL, NULL, 0, "inherit"},
@@ -131,11 +133,14 @@
 	    NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP |
 	    ZCHECKSUM_FLAG_NOPWRITE, "SHA512"},
 	/* no skein and edonr for now */
-	{{NULL, NULL}, NULL, NULL, ZCHECKSUM_FLAG_METADATA |
-	    ZCHECKSUM_FLAG_DEDUP | ZCHECKSUM_FLAG_SALTED |
-	    ZCHECKSUM_FLAG_NOPWRITE, "skein"},
-	{{NULL, NULL}, NULL, NULL, ZCHECKSUM_FLAG_METADATA |
-	    ZCHECKSUM_FLAG_SALTED | ZCHECKSUM_FLAG_NOPWRITE, "edonr"},
+	{{zio_checksum_skein_native, zio_checksum_skein_byteswap},
+	    zio_checksum_skein_tmpl_init, zio_checksum_skein_tmpl_free,
+	    ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP |
+	    ZCHECKSUM_FLAG_SALTED | ZCHECKSUM_FLAG_NOPWRITE, "skein"},
+	{{zio_checksum_edonr_native, zio_checksum_edonr_byteswap},
+	    zio_checksum_edonr_tmpl_init, zio_checksum_edonr_tmpl_free,
+	    ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_SALTED |
+	    ZCHECKSUM_FLAG_NOPWRITE, "edonr"},
 };
 
 /*
@@ -228,33 +233,48 @@
  * templates and installs the template into the spa_t.
  */
 static void
-zio_checksum_template_init(enum zio_checksum checksum, const spa_t *spa)
+zio_checksum_template_init(enum zio_checksum checksum, spa_t *spa)
 {
 	zio_checksum_info_t *ci = &zio_checksum_table[checksum];
 
 	if (ci->ci_tmpl_init == NULL)
 		return;
-#if 0	/* for now we dont have anything here */
+
 	if (spa->spa_cksum_tmpls[checksum] != NULL)
 		return;
 
-	VERIFY(ci->ci_tmpl_free != NULL);
-	mutex_enter(&spa->spa_cksum_tmpls_lock);
 	if (spa->spa_cksum_tmpls[checksum] == NULL) {
 		spa->spa_cksum_tmpls[checksum] =
 		    ci->ci_tmpl_init(&spa->spa_cksum_salt);
-		VERIFY(spa->spa_cksum_tmpls[checksum] != NULL);
 	}
-	mutex_exit(&spa->spa_cksum_tmpls_lock);
-#endif
+}
+
+/*
+ * Called by a spa_t that's about to be deallocated. This steps through
+ * all of the checksum context templates and deallocates any that were
+ * initialized using the algorithm-specific template init function.
+ */
+void
+zio_checksum_templates_free(spa_t *spa)
+{
+	for (enum zio_checksum checksum = 0;
+	    checksum < ZIO_CHECKSUM_FUNCTIONS; checksum++) {
+		if (spa->spa_cksum_tmpls[checksum] != NULL) {
+			zio_checksum_info_t *ci = &zio_checksum_table[checksum];
+
+			ci->ci_tmpl_free(spa->spa_cksum_tmpls[checksum]);
+			spa->spa_cksum_tmpls[checksum] = NULL;
+		}
+	}
 }
 
 static int
-zio_checksum_verify(const blkptr_t *bp, void *data)
+zio_checksum_verify(const spa_t *spa, const blkptr_t *bp, void *data)
 {
 	uint64_t size;
 	unsigned int checksum;
 	zio_checksum_info_t *ci;
+	void *ctx = NULL;
 	zio_cksum_t actual_cksum, expected_cksum, verifier;
 	int byteswap;
 
@@ -267,7 +287,11 @@
 	if (ci->ci_func[0] == NULL || ci->ci_func[1] == NULL)
 		return (EINVAL);
 
-	zio_checksum_template_init(checksum, NULL);
+	if (spa != NULL) {
+		zio_checksum_template_init(checksum, (spa_t *) spa);
+		ctx = spa->spa_cksum_tmpls[checksum];
+	}
+
 	if (ci->ci_flags & ZCHECKSUM_FLAG_EMBEDDED) {
 		zio_eck_t *eck;
 
@@ -291,7 +315,7 @@
 
 		expected_cksum = eck->zec_cksum;
 		eck->zec_cksum = verifier;
-		ci->ci_func[byteswap](data, size, NULL, &actual_cksum);
+		ci->ci_func[byteswap](data, size, ctx, &actual_cksum);
 		eck->zec_cksum = expected_cksum;
 
 		if (byteswap)
@@ -299,11 +323,11 @@
 			    sizeof (zio_cksum_t));
 	} else {
 		expected_cksum = bp->blk_cksum;
-		ci->ci_func[0](data, size, NULL, &actual_cksum);
+		ci->ci_func[0](data, size, ctx, &actual_cksum);
 	}
 
 	if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) {
-		/*printf("ZFS: read checksum failed\n");*/
+		/* printf("ZFS: read checksum %s failed\n", ci->ci_name); */
 		return (EIO);
 	}
 
@@ -1313,10 +1337,11 @@
  * any ereports we generate can note it.
  */
 static int
-raidz_checksum_verify(const blkptr_t *bp, void *data, uint64_t size)
+raidz_checksum_verify(const spa_t *spa, const blkptr_t *bp, void *data,
+    uint64_t size)
 {
 
-	return (zio_checksum_verify(bp, data));
+	return (zio_checksum_verify(spa, bp, data));
 }
 
 /*
@@ -1365,8 +1390,8 @@
  * cases we'd only use parity information in column 0.
  */
 static int
-vdev_raidz_combrec(raidz_map_t *rm, const blkptr_t *bp, void *data,
-    off_t offset, uint64_t bytes, int total_errors, int data_errors)
+vdev_raidz_combrec(const spa_t *spa, raidz_map_t *rm, const blkptr_t *bp,
+    void *data, off_t offset, uint64_t bytes, int total_errors, int data_errors)
 {
 	raidz_col_t *rc;
 	void *orig[VDEV_RAIDZ_MAXPARITY];
@@ -1445,7 +1470,7 @@
 			 * success.
 			 */
 			code = vdev_raidz_reconstruct(rm, tgts, n);
-			if (raidz_checksum_verify(bp, data, bytes) == 0) {
+			if (raidz_checksum_verify(spa, bp, data, bytes) == 0) {
 				for (i = 0; i < n; i++) {
 					c = tgts[i];
 					rc = &rm->rm_col[c];
@@ -1616,7 +1641,7 @@
 	 */
 	if (total_errors <= rm->rm_firstdatacol - parity_untried) {
 		if (data_errors == 0) {
-			if (raidz_checksum_verify(bp, data, bytes) == 0) {
+			if (raidz_checksum_verify(vd->spa, bp, data, bytes) == 0) {
 				/*
 				 * If we read parity information (unnecessarily
 				 * as it happens since no reconstruction was
@@ -1661,7 +1686,7 @@
 
 			code = vdev_raidz_reconstruct(rm, tgts, n);
 
-			if (raidz_checksum_verify(bp, data, bytes) == 0) {
+			if (raidz_checksum_verify(vd->spa, bp, data, bytes) == 0) {
 				/*
 				 * If we read more parity disks than were used
 				 * for reconstruction, confirm that the other
@@ -1735,7 +1760,7 @@
 	if (total_errors > rm->rm_firstdatacol) {
 		error = EIO;
 	} else if (total_errors < rm->rm_firstdatacol &&
-	    (code = vdev_raidz_combrec(rm, bp, data, offset, bytes,
+	    (code = vdev_raidz_combrec(vd->spa, rm, bp, data, offset, bytes,
 	     total_errors, data_errors)) != 0) {
 		/*
 		 * If we didn't use all the available parity for the
--- a/usr/src/boot/sys/sys/types.h	Wed Jun 01 18:53:15 2016 +0300
+++ b/usr/src/boot/sys/sys/types.h	Mon Dec 04 12:26:50 2017 +0200
@@ -58,6 +58,11 @@
 #endif
 
 /*
+ * POSIX Extensions
+ */
+typedef	unsigned int	uint_t;
+
+/*
  * XXX POSIX sized integrals that should appear only in <sys/stdint.h>.
  */
 #include <sys/_stdint.h>