diff --git sbin/tgptbootdata/Makefile sbin/tgptbootdata/Makefile
new file mode 100644
index 0000000..1b03ebe
--- /dev/null
+++ sbin/tgptbootdata/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+PROG=	tgptbootdata
+SRCS=	parse.y scan.l y.tab.h tgptbootdata.h tgptbootdata.c
+SRCS+=	rijndael-alg-fst.c rijndael-api-fst.c
+CFLAGS+=-I. -I${.CURDIR}
+CFLAGS+=-DYY_NO_UNPUT -DYY_NO_INPUT
+CFLAGS+= -I${.CURDIR}/../../sys
+
+NO_WCAST_ALIGN=
+NO_WMISSING_VARIABLE_DECLARATIONS=
+
+.PATH: ${.CURDIR}/../../sys/crypto/rijndael
+
+LDADD=	-ll -lmd
+DPADD=	${LIBL} ${LIBMD}
+
+MAN=	tgptbootdata.8
+
+WARN?= 3
+
+.include <bsd.prog.mk>
diff --git sbin/tgptbootdata/parse.y sbin/tgptbootdata/parse.y
new file mode 100644
index 0000000..6ac5894
--- /dev/null
+++ sbin/tgptbootdata/parse.y
@@ -0,0 +1,191 @@
+%{
+/*-
+ *
+ * Copyright (c) 2014, GSMK mbh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include "tgptbootdata.h"
+
+static void
+add_env(const char *key, const char *val)
+{
+	struct q_env *qq;
+
+	if (NULL == (qq = (struct q_env *)malloc(sizeof(struct q_env))) ||
+		NULL == (qq->key = strdup(key)) ||
+		NULL == (qq->val = strdup(val)))
+		errx(1, "malloc failed");
+	STAILQ_INSERT_TAIL(&q_env_h, qq, link);
+}
+
+static void
+add_load(const char *module_file, const char *path, const char *flags)
+{
+	struct q_load *qq;
+
+	if (NULL == (qq = (struct q_load *)malloc(sizeof(struct q_load))) ||
+		NULL == (qq->module_file = strdup(module_file)) ||
+		NULL == (qq->path = strdup(path)))
+		errx(1, "malloc failed");
+	if (NULL == flags || 0 == *flags)
+		qq->flags = NULL;
+	else if (NULL == (qq->flags = strdup(flags)))
+		errx(1, "malloc failed");
+	STAILQ_INSERT_TAIL(&q_load_h, qq, link);
+}
+
+
+static void
+add_key(const char *key_name, const char *prov, const char *key_file)
+{
+	struct q_key *qq;
+
+	if (NULL == (qq = (struct q_key *)malloc(sizeof(struct q_key))) ||
+		NULL == (qq->key_name = strdup(key_name)) ||
+		NULL == (qq->prov = strdup(prov)) ||
+		NULL == (qq->key_file = strdup(key_file)))
+		errx(1, "malloc failed");
+	STAILQ_INSERT_TAIL(&q_key_h, qq, link);
+}
+%}
+
+%union {
+	char *str;
+}
+
+%token COMMA DOT BEGINTUPLE ENDTUPLE
+%token BEGINLIST ENDLIST
+%token SEMICOLON BEGINBLOCK ENDBLOCK COMMA
+%token <str> STRING
+%token KEY ENV LOAD PROVISION UNDEFINED
+
+
+%%
+
+config_file
+	: provision_t
+	|
+	;
+
+provision_t
+	: BEGINTUPLE PROVISION COMMA provision_l ENDTUPLE DOT
+	;
+
+provision_l
+	: BEGINLIST provision_i_ ENDLIST
+	;
+
+provision_i_
+	: provision_i provision_i__
+	;
+
+provision_i__
+	: provision_i__ COMMA provision_i
+	|
+	;
+
+provision_i
+	: env_t
+	| load_t
+	| key_t
+	;
+
+env_t
+	: BEGINTUPLE ENV COMMA env_l ENDTUPLE
+	;
+
+env_l
+	: BEGINLIST env_i_ ENDLIST
+	| BEGINLIST ENDLIST
+	;
+
+env_i_
+	: env_i env_i__
+	;
+
+env_i__
+	: env_i__ COMMA env_i
+	|
+	;
+
+env_i
+	: BEGINTUPLE STRING COMMA STRING ENDTUPLE { add_env($2,$4); }
+	;
+
+load_t
+	: BEGINTUPLE LOAD COMMA load_l ENDTUPLE
+	;
+
+load_l
+	: BEGINLIST load_i_ ENDLIST
+	| BEGINLIST ENDLIST
+	;
+
+load_i_
+	: load_i load_i__
+	;
+
+load_i__
+	: load_i__ COMMA load_i
+	|
+	;
+
+load_i
+	: BEGINTUPLE STRING COMMA STRING COMMA STRING ENDTUPLE { add_load($2,$4,$6); }
+	| BEGINTUPLE STRING COMMA STRING COMMA UNDEFINED ENDTUPLE { add_load($2,$4,NULL); }
+	;
+
+key_t
+	: BEGINTUPLE KEY COMMA key_l ENDTUPLE
+	;
+
+key_l
+	: BEGINLIST key_i_ ENDLIST
+	| BEGINLIST ENDLIST
+	;
+
+key_i_
+	: key_i key_i__
+	;
+
+key_i__
+	: key_i__ COMMA key_i
+	|
+	;
+
+key_i
+	: BEGINTUPLE STRING COMMA STRING COMMA STRING ENDTUPLE { add_key($2,$4,$6); }
+	;
+
+%%
diff --git sbin/tgptbootdata/scan.l sbin/tgptbootdata/scan.l
new file mode 100644
index 0000000..8bf4d46
--- /dev/null
+++ sbin/tgptbootdata/scan.l
@@ -0,0 +1,102 @@
+%{
+/*-
+ *
+ * Copyright (c) 2014, GSMK mbh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "y.tab.h"
+
+int lineno = 1;
+
+void yyerror(const char *);
+
+int yylex(void);
+
+static void
+update_lineno(const char *cp)
+{
+	while (*cp)
+		if (*cp++ == '\n')
+			lineno++;
+}
+
+%}
+
+%option nounput
+%option noinput
+
+%%
+
+[ \t]+		;
+\n			lineno++;
+,			{ return COMMA; }
+\.			{ return DOT; }
+\/\*([^*]|(\*+([^*\/])))*\*+\/ { update_lineno(yytext); }
+\{			{ return BEGINTUPLE; }
+\}			{ return ENDTUPLE; }
+\[			{ return BEGINLIST; }
+\]			{ return ENDLIST; }
+\"[^"]*\"		{
+				size_t len = strlen(yytext) - 2;
+				char *walker;
+				unsigned int i;
+				update_lineno(yytext);
+				if ((yylval.str = (char *) malloc(len + 1)) == NULL)
+					goto out;
+				walker = yylval.str;
+				for (i = 1; i <= len; i++) {
+					if (yytext[i] == '\\' &&
+					    yytext[i + 1] == '\n') {
+						i += 2;
+						while(isspace(yytext[i]))
+							i++;
+					}
+					*walker++ = yytext[i];
+				}
+				*walker++ = '\0';
+			out:;
+				return STRING;
+			}
+\%.*$		;
+
+key	{ return KEY; }
+env { return ENV; }
+load { return LOAD; }
+provision { return PROVISION; }
+undefined { return UNDEFINED; }
+
+%%
+
+void
+yyerror(const char *s)
+{
+	fprintf(stderr, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s);
+}
diff --git sbin/tgptbootdata/tgptbootdata.8 sbin/tgptbootdata/tgptbootdata.8
new file mode 100644
index 0000000..f34c7f9
--- /dev/null
+++ sbin/tgptbootdata/tgptbootdata.8
@@ -0,0 +1,46 @@
+.\" Copyright (c) 2014, GSMK mbh
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 6, 2014
+.Dt TGPTBOOTDATA 8
+.Os
+.Sh NAME
+.Nm tgptbootdata
+.Nd create tgptboot data files
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+TODO
+.Sh SEE ALSO
+.Xr tgptboot 8
+.Sh AUTHORS
+Stefan Grundmann
diff --git sbin/tgptbootdata/tgptbootdata.c sbin/tgptbootdata/tgptbootdata.c
new file mode 100644
index 0000000..41837f4
--- /dev/null
+++ sbin/tgptbootdata/tgptbootdata.c
@@ -0,0 +1,434 @@
+/*-
+ * Copyright (c) 2014, GSMK mbh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <sysexits.h>
+#include <sha512.h>
+#include <crypto/rijndael/rijndael-api-fst.h>
+#include <crypto/rijndael.h>
+
+#include "tgptbootdata.h"
+
+/* yacc leaves us with these */
+extern FILE *yyin;
+extern int lineno;
+extern int yparse(void);
+
+
+struct q_env_h q_env_h = STAILQ_HEAD_INITIALIZER(q_env_h);
+struct q_load_h q_load_h = STAILQ_HEAD_INITIALIZER(q_load_h);
+struct q_key_h q_key_h = STAILQ_HEAD_INITIALIZER(q_key_h);
+
+enum {
+	C_LEN = 4,			/* four bytes (Big Endian) length(s) */
+	C_TAG = 1,			/* one byte TAG */
+	C_TERM = 1,			/* \0 terminated strings */
+	C_SIZE = C_LEN,
+	C_STR = C_TAG + C_LEN + C_TERM,
+};
+
+enum {
+	AES_KEY_SIZE = 32,
+	AES_KEY_BITS = (AES_KEY_SIZE * 8),
+	AES_BLOCK_SIZE = 16,
+	AES_IV_SIZE = AES_BLOCK_SIZE,
+	SHA512_DIGEST_SIZE = 64,
+};
+
+enum {
+	T_env = 8,
+	T_env_Key = 9,
+	T_env_Val = 10,
+	T_key = 16,
+	T_key_Prov = 17,
+	T_key_Key = 18,
+	T_key_Name = 19,
+	T_load = 32,
+	T_load_Path = 33,
+	T_load_Size = 34,
+	T_load_Args = 35,
+	T_load_Hash = 36,
+};
+
+
+static inline void
+st32BE(uint8_t *p, uint32_t x)
+{
+	p[0] = x >> 24;
+	p[1] = (x >> 16) & 0xff;
+	p[2] = (x >> 8) & 0xff;
+	p[3] = x & 0xff;
+}
+
+static void
+usage(void)
+{
+	fprintf(stderr, "usage: %s -f configfile [-k keyfile] [-o outfile]\n",
+	    getprogname());
+	exit(1);
+}
+
+static void
+parse_config(const char *configfile)
+{
+	if (NULL == (yyin = fopen(configfile, "r")))
+		errx(EX_OSFILE, "Cannot open config file %s", configfile);
+
+	lineno = 1;
+
+	if (0 != yyparse())
+		errx(EX_CONFIG, "Cannot parse %s at line %d", configfile, lineno);
+	if (0 != fclose(yyin))
+		errx(EX_OSERR, "fclose failed; %s", strerror(errno));
+}
+
+static uint32_t
+file_size(const char *path)
+{
+	struct stat sb;
+
+	if (0 != stat(path, &sb))
+		errx(EX_OSFILE, "coud not stat \"%s\" : %s", path, strerror(errno));
+	if (sb.st_size > UINT32_MAX)
+		errx(EX_DATAERR, "%s too large", path);
+	return (uint32_t)sb.st_size;
+}
+
+static void
+file_sha512(const char *module_path, uint8_t *hash)
+{
+	FILE *fp;
+	SHA512_CTX sha512;
+	size_t cnt;
+	const size_t buf_size = 1 << 12;/* 4K */
+	uint8_t *buf;
+
+
+	if (NULL == (fp = fopen(module_path, "rb")))
+		errx(EX_OSFILE, "Cannot open module file %s: %s", module_path, strerror(errno));
+
+	if (NULL == (buf = (uint8_t *)malloc(buf_size)))
+		errx(EX_OSERR, "malloc failed");
+
+	SHA512_Init(&sha512);
+	while (0 != (cnt = fread(buf, 1, buf_size, fp)))
+		SHA512_Update(&sha512, buf, cnt);
+
+	SHA512_Final(hash, &sha512);
+	if (0 != fclose(fp))
+		errx(EX_OSERR, "fclose failed: %s", strerror(errno));
+	free(buf);
+}
+
+static void
+encode_load(uint8_t **buf, uint32_t *buf_size)
+{
+	struct q_load *qq;
+	uint8_t *p;
+
+	for (qq = STAILQ_FIRST(&q_load_h), *buf_size = 0; NULL != qq; (qq = STAILQ_NEXT(qq, link))) {
+		*buf_size += C_TAG + C_LEN
+		    + C_STR + strlen(qq->path)
+		    + C_TAG + C_LEN + C_SIZE
+		    + ((NULL == qq->flags) ? C_TAG + C_LEN : C_STR + (strlen(qq->flags)))
+		    + C_TAG + C_LEN + SHA512_DIGEST_SIZE;
+	}
+	if (NULL == (*buf = (uint8_t *)malloc(*buf_size)))
+		errx(EX_OSERR, "malloc failed");
+
+	p = *buf;
+	memset(p, 0, *buf_size);
+
+	for (qq = STAILQ_FIRST(&q_load_h); NULL != qq; (qq = STAILQ_NEXT(qq, link))) {
+		uint8_t sha[SHA512_DIGEST_SIZE];
+
+		const uint32_t sz = file_size(qq->module_file);
+		const uint32_t pl = strlen(qq->path);
+		const uint32_t fl = (NULL == qq->flags) ? 0 : (strlen(qq->flags));
+
+		file_sha512(qq->module_file, sha);
+
+		/* start load */
+		*p++ = T_load;
+		st32BE(p, C_STR + pl + C_TAG + C_LEN + C_SIZE + (fl ? C_STR + fl : C_TAG + C_LEN)
+		    + C_TAG + C_LEN + SHA512_DIGEST_SIZE);
+		p += C_LEN;
+
+		/* store path */
+		*p++ = T_load_Path;
+		st32BE(p, pl + C_TERM);
+		p += C_LEN;
+		memcpy(p, qq->path, pl);
+		p += pl + C_TERM;	/* string + \0 (from memset) */
+
+		/* store size */
+		*p++ = T_load_Size;
+		st32BE(p, C_SIZE),
+		    p += C_LEN;
+		st32BE(p, sz);
+		p += C_SIZE;
+
+		/* store args/flags */
+		*p++ = T_load_Args;
+		st32BE(p, fl ? fl + C_TERM : 0);
+		p += C_LEN;
+		if (fl) {
+			memcpy(p, qq->flags, fl);
+			p += fl + C_TERM;	/* string + \0 (from memset) */
+		}
+		/* store hash */
+		*p++ = T_load_Hash;
+		st32BE(p, SHA512_DIGEST_SIZE);
+		p += C_LEN;
+		memcpy(p, sha, SHA512_DIGEST_SIZE);
+		p += SHA512_DIGEST_SIZE;
+	}
+}
+
+static void
+encode_env(uint8_t **buf, uint32_t *buf_size)
+{
+	struct q_env *qq;
+	uint8_t *p;
+
+	for (qq = STAILQ_FIRST(&q_env_h), *buf_size = 0; NULL != qq; (qq = STAILQ_NEXT(qq, link)))
+		*buf_size += C_TAG + C_LEN
+		    + C_STR + strlen(qq->key)
+		    + C_STR + strlen(qq->val);
+	if (NULL == (*buf = (uint8_t *)malloc(*buf_size)))
+		errx(EX_OSERR, "malloc failed");
+
+	p = *buf;
+	memset(p, 0, *buf_size);
+
+	for (qq = STAILQ_FIRST(&q_env_h); NULL != qq; (qq = STAILQ_NEXT(qq, link))) {
+		const uint32_t kl = strlen(qq->key);
+		const uint32_t vl = strlen(qq->val);
+
+		/* start env */
+		*p++ = T_env;
+		st32BE(p, kl + vl + 2 * C_STR);
+		p += C_LEN;
+
+		/* store key */
+		*p++ = T_env_Key;
+		st32BE(p, kl + C_TERM);
+		p += C_LEN;
+		memcpy(p, qq->key, kl);
+		p += kl + C_TERM;	/* string + \0 (from memset) */
+
+		/* store value */
+		*p++ = T_env_Val;
+		st32BE(p, vl + C_TERM);
+		p += C_LEN;
+		memcpy(p, qq->val, vl);
+		p += vl + C_TERM;	/* string + \0 (from memset) */
+	}
+}
+
+static void
+encode_gelikey(uint8_t **buf, uint32_t *buf_size)
+{
+	struct q_key *qq;
+	uint8_t *p;
+
+	for (qq = STAILQ_FIRST(&q_key_h), *buf_size = 0; NULL != qq; (qq = STAILQ_NEXT(qq, link)))
+		*buf_size += C_TAG + C_LEN
+		    + C_STR + strlen(qq->key_name)
+		    + C_STR + strlen(qq->prov)
+		    + C_TAG + C_LEN + file_size(qq->key_file);
+	if (NULL == (*buf = (uint8_t *)malloc(*buf_size)))
+		errx(EX_OSERR, "malloc failed");
+
+	p = *buf;
+	memset(p, 0, *buf_size);
+
+	for (qq = STAILQ_FIRST(&q_key_h); NULL != qq; (qq = STAILQ_NEXT(qq, link))) {
+		FILE *fp;
+		const uint32_t nl = strlen(qq->key_name);
+		const uint32_t pl = strlen(qq->prov);
+		const uint32_t sz = file_size(qq->key_file);
+
+		/* start key */
+		*p++ = T_key;
+		st32BE(p, C_STR + nl + C_STR + pl + C_TAG + C_LEN + sz);
+		p += C_LEN;
+
+		/* store name */
+		*p++ = T_key_Name;
+		st32BE(p, nl + C_TERM);
+		p += C_LEN;
+		memcpy(p, qq->key_name, nl);
+		p += nl + C_TERM;	/* string + \0 (from memset) */
+
+		/* store provider */
+		*p++ = T_key_Prov;
+		st32BE(p, pl + C_TERM);
+		p += C_LEN;
+		memcpy(p, qq->prov, pl);
+		p += pl + C_TERM;	/* string + \0 (from memset) */
+
+		/* store the key */
+		*p++ = T_key_Key;
+		st32BE(p, sz);
+		p += C_LEN;
+		if (NULL == (fp = fopen(qq->key_file, "rb")))
+			errx(EX_OSFILE, "Cannot open key file %s: %s", qq->key_file, strerror(errno));
+		if (0 == fread(p, sz, 1, fp))
+			errx(EX_OSFILE, "Error reading %s: %s", qq->key_file, strerror(errno));
+		if (0 != fclose(fp))
+			errx(EX_OSERR, "fclose failed: %s", strerror(errno));
+		p += sz;
+	}
+}
+
+static void
+encode_magic(uint8_t **buf, uint32_t *buf_size)
+{
+	uint8_t *env, *load, *key, *p;
+	uint32_t env_size, load_size, key_size, pt_size, pad_size;
+	SHA512_CTX sha512;
+
+	encode_env(&env, &env_size);
+	encode_load(&load, &load_size);
+	encode_gelikey(&key, &key_size);
+
+	pt_size = env_size + load_size + key_size;
+	pad_size = AES_BLOCK_SIZE - (pt_size % AES_BLOCK_SIZE);
+	*buf_size = SHA512_DIGEST_SIZE + AES_BLOCK_SIZE + pt_size + pad_size;
+
+	if (NULL == (*buf = (uint8_t *)malloc(*buf_size)))
+		errx(EX_OSERR, "malloc failed");
+
+
+	p = *buf;
+	memset(p, 0, *buf_size);
+
+	p += SHA512_DIGEST_SIZE;	/* skip 64 bytes for the hash */
+	st32BE(p, pt_size);		/* encode PT length */
+	p += AES_BLOCK_SIZE;		/* leave the rest of the block at \0 */
+
+	memcpy(p, env, env_size);
+	p += env_size;
+	free(env);
+	memcpy(p, load, load_size);
+	p += load_size;
+	free(load);
+	memcpy(p, key, key_size);
+	p += key_size;
+	free(key);
+
+	SHA512_Init(&sha512);
+	SHA512_Update(&sha512, *buf + SHA512_DIGEST_SIZE, pt_size + AES_BLOCK_SIZE);
+	SHA512_Final(*buf, &sha512);
+
+}
+
+static void
+encrypt_magic(const char *keyfile, const char *outfile, uint8_t *buf, uint32_t buf_size)
+{
+	uint8_t key[AES_KEY_SIZE];
+	keyInstance ki;
+	cipherInstance ci;
+	int error;
+	FILE *fp;
+
+	/* key: 32 bytes from keyfile or stdin */
+	if (NULL != keyfile) {
+		if (NULL == (fp = fopen(keyfile, "r")))
+			errx(EX_OSFILE, "Cannot open key file %s: %s", keyfile, strerror(errno));
+		if (0 == fread(key, AES_KEY_SIZE, 1, fp))
+			errx(EX_OSFILE, "Error reading %s:%s", keyfile, strerror(errno));
+		if (0 != fclose(fp))
+			errx(EX_OSERR, "fclose failed: %s", strerror(errno));
+	} else
+		fread(key, AES_KEY_SIZE, 1, stdin);
+
+	if (NULL == outfile)
+		fp = stdout;
+	else if (NULL == (fp = fopen(outfile, "wb")))
+		errx(EX_OSFILE, "Cannot open output file %s: %s", outfile, strerror(errno));
+
+	/* encrypt PT buffer in place */
+	if (0 >= (error = rijndael_cipherInit(&ci, MODE_CBC, NULL)))
+		errx(1, "rijndael_cipherInit=%d", error);
+	if (0 >= (error = rijndael_makeKey(&ki, DIR_ENCRYPT, AES_KEY_BITS, (char *)key)))
+		errx(1, "rijndael_makeKey=%d", error);
+	if (0 >= (error = rijndael_blockEncrypt(&ci, &ki, buf, 8 * buf_size, buf)))
+		errx(1, "rijndael_blockEncrypt=%d", error);
+
+	/* write CT */
+	if (0 == fwrite(buf, buf_size, 1, fp))
+		errx(EX_OSFILE, "Error writing %s", (NULL == outfile) ? "" : outfile);
+	if (0 != fclose(fp))
+		errx(EX_OSERR, "fclose failed; %s", strerror(errno));
+}
+
+int
+main(int argc, char **argv)
+{
+	char *configfile = NULL;
+	char *keyfile = NULL;
+	char *outfile = NULL;
+	uint8_t *buf;
+	uint32_t buf_size;
+	int ch;
+
+	while ((ch = getopt(argc, argv, "f:k:o:")) != -1) {
+		switch (ch) {
+		case 'f':
+			configfile = optarg;
+			break;
+		case 'k':
+			keyfile = optarg;
+			break;
+		case 'o':
+			outfile = optarg;
+			break;
+		default:
+			usage();
+		}
+	}
+	if (NULL == configfile)
+		usage();
+
+	parse_config(configfile);
+	encode_magic(&buf, &buf_size);
+	encrypt_magic(keyfile, outfile, buf, buf_size);
+	return 0;
+}
diff --git sbin/tgptbootdata/tgptbootdata.h sbin/tgptbootdata/tgptbootdata.h
new file mode 100644
index 0000000..ce84c33
--- /dev/null
+++ sbin/tgptbootdata/tgptbootdata.h
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2014, GSMK mbh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef TGPTBOOTDATA_H
+#define TGPTBOOTDATA_H
+
+
+STAILQ_HEAD (q_env_h, q_env);
+
+struct q_env {
+    char *key;
+    char *val;
+    STAILQ_ENTRY (q_env) link;
+};
+
+
+STAILQ_HEAD (q_load_h, q_load);
+
+struct q_load {
+	char   *module_file;
+	char   *path;
+	char   *flags;
+	STAILQ_ENTRY (q_load) link;
+};
+
+
+STAILQ_HEAD (q_key_h, q_key);
+
+struct q_key {
+	char   *key_name;
+	char   *prov;
+	char   *key_file;
+	STAILQ_ENTRY (q_key) link;
+};
+
+extern struct q_env_h q_env_h;
+extern struct q_load_h q_load_h;
+extern struct q_key_h q_key_h;
+
+void yyerror(const char *s);
+int yylex(void);
+int yyparse(void);
+
+#endif
diff --git sys/boot/i386/tgptboot/Makefile sys/boot/i386/tgptboot/Makefile
new file mode 100644
index 0000000..e9fbf1b
--- /dev/null
+++ sys/boot/i386/tgptboot/Makefile
@@ -0,0 +1,77 @@
+# $FreeBSD$
+
+.PATH:		${.CURDIR}/../common \
+			${.CURDIR}/../../common ${SYSDIR}/crypto/sha2 \
+			${SYSDIR}/crypto/rijndael
+FILES=		tgptboot
+
+ORG1=		0x7c00
+ORG2=		0x0
+
+CFLAGS=		-DBOOTPROG=\"tgptboot\" \
+			-O1 \
+			-DGPT \
+			-DTLOADER_VERBOSE \
+			-DLOADER_MBR_SUPPORT \
+			-DLOADER_GPT_SUPPORT \
+			-I${.CURDIR}/.. \
+			-I${SYSDIR}/crypto/sha2 \
+			-I${SYSDIR}/crypto/rijndael \
+			-I${.CURDIR}/../../common \
+			-I${.CURDIR}/../common \
+			-I${.CURDIR}/../btx/lib -I. \
+			-I${.CURDIR}/../../.. \
+			-Wall -Waggregate-return -Wbad-function-cast \
+			-Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+			-Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+			-Winline
+
+CFLAGS.gcc+=	--param max-inline-insns-single=100
+
+LIBI386=	${.OBJDIR}/../libi386/libi386.a
+LD_FLAGS=	-static -N --gc-sections
+LIBSTAND=	${.OBJDIR}/../../libstand32/libstand.a
+
+# Pick up ../Makefile.inc early.
+.include <bsd.init.mk>
+
+CLEANFILES=	tgptboot
+
+tgptboot: gptldr.bin tgptboot.bin ${BTXKERN}
+	btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
+	    -o ${.TARGET} tgptboot.bin
+
+gptldr.bin: gptldr.out
+	objcopy -S -O binary gptldr.out ${.TARGET}
+
+gptldr.out: gptldr.o
+	${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
+
+CLEANFILES+=	gptldr.bin gptldr.out gptldr.o
+
+tgptboot.bin: tgptboot.out
+	objcopy -S -O binary tgptboot.out ${.TARGET}
+
+TGPTOBJS=	bcache.o conf.o console.o crc32.o devopen.o disk.o drv.o gpt.o \
+			helper.o isapnp.o load_elf32.o load_elf32_obj.o load_elf64.o \
+			load_elf64_obj.o misc.o panic.o part.o pnp.o reloc_elf32.o \
+			reloc_elf64.o rijndael-alg-fst.o rijndael-api-fst.o sha2.o  \
+			tgptboot.o tloader.o tpm.o
+
+tgptboot.out: ${BTXCRT} ${TGPTOBJS}
+	${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBI386} ${LIBSTAND}
+
+CLEANFILES+=	${TGPTOBJS} tgptboot.out tgptboot.bin
+
+.if ${MACHINE_CPUARCH} == "amd64"
+beforedepend tgptboot.o: machine
+CLEANFILES+=	machine
+machine:
+	ln -sf ${.CURDIR}/../../../i386/include machine
+.endif
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.gptldr.S=	${CLANG_NO_IAS}
+CFLAGS+=		${CFLAGS.${.IMPSRC:T}}
diff --git sys/boot/i386/tgptboot/conf.c sys/boot/i386/tgptboot/conf.c
new file mode 100644
index 0000000..abae79f
--- /dev/null
+++ sys/boot/i386/tgptboot/conf.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include "libi386/libi386.h"
+#if defined(LOADER_ZFS_SUPPORT)
+#include "../zfs/libzfs.h"
+#endif
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap.  So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ *
+ * XXX as libi386 and biosboot merge, some of these can become linker sets.
+ */
+
+#if defined(LOADER_NFS_SUPPORT) && defined(LOADER_TFTP_SUPPORT)
+#error "Cannot have both tftp and nfs support yet."
+#endif
+
+#if defined(LOADER_FIREWIRE_SUPPORT)
+extern struct devsw fwohci;
+
+#endif
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+	&biosdisk,
+#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT)
+	&pxedisk,
+#endif
+#if defined(LOADER_FIREWIRE_SUPPORT)
+	&fwohci,
+#endif
+#if defined(LOADER_ZFS_SUPPORT)
+	&zfs_dev,
+#endif
+	NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_ZFS_SUPPORT)
+	&zfs_fsops,
+#endif
+	&ufs_fsops,
+	&ext2fs_fsops,
+	&dosfs_fsops,
+	&cd9660_fsops,
+#if defined(LOADER_NANDFS_SUPPORT)
+	&nandfs_fsops,
+#endif
+#ifdef LOADER_SPLIT_SUPPORT
+	&splitfs_fsops,
+#endif
+#ifdef LOADER_GZIP_SUPPORT
+	&gzipfs_fsops,
+#endif
+#ifdef LOADER_BZIP2_SUPPORT
+	&bzipfs_fsops,
+#endif
+#ifdef LOADER_NFS_SUPPORT
+	&nfs_fsops,
+#endif
+#ifdef LOADER_TFTP_SUPPORT
+	&tftp_fsops,
+#endif
+	NULL
+};
+
+/* Exported for i386 only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+extern struct file_format i386_elf;
+extern struct file_format i386_elf_obj;
+extern struct file_format amd64_elf;
+extern struct file_format amd64_elf_obj;
+
+struct file_format *file_formats[] = {
+#ifdef LOADER_PREFER_AMD64
+	&amd64_elf,
+	&amd64_elf_obj,
+#endif
+	&i386_elf,
+	&i386_elf_obj,
+#ifndef LOADER_PREFER_AMD64
+	&amd64_elf,
+	&amd64_elf_obj,
+#endif
+	NULL
+};
+
+/*
+ * Consoles
+ *
+ * We don't prototype these in libi386.h because they require
+ * data structures from bootstrap.h as well.
+ */
+extern struct console vidconsole;
+extern struct console comconsole;
+
+#if defined(LOADER_FIREWIRE_SUPPORT)
+extern struct console dconsole;
+
+#endif
+extern struct console nullconsole;
+extern struct console spinconsole;
+
+struct console *consoles[] = {
+	&vidconsole,
+	&comconsole,
+#if defined(LOADER_FIREWIRE_SUPPORT)
+	&dconsole,
+#endif
+	&nullconsole,
+	&spinconsole,
+	NULL
+};
+
+extern struct pnphandler isapnphandler;
+extern struct pnphandler biospnphandler;
+extern struct pnphandler biospcihandler;
+
+struct pnphandler *pnphandlers[] = {
+	&biospnphandler,		/* should go first, as it may set
+					 * isapnp_readport */
+	&isapnphandler,
+	&biospcihandler,
+	NULL
+};
diff --git sys/boot/i386/tgptboot/gptldr.S sys/boot/i386/tgptboot/gptldr.S
new file mode 100644
index 0000000..b951c79
--- /dev/null
+++ sys/boot/i386/tgptboot/gptldr.S
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2007 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Partly from: src/sys/boot/i386/boot2/boot1.S 1.31
+ *
+ * extended to handle up to 256k boot2 code (GSMK mbh 2014)
+ */
+
+/* Memory Locations */
+		.set MEM_REL,0x700		# Relocation address
+		.set MEM_ARG,0x900		# Arguments
+		.set MEM_ORG,0x7c00		# Origin
+		.set MEM_BUF,0x8cec		# Load area
+		.set MEM_BTX,0x9000		# BTX start
+		.set MEM_JMP,0x9010		# BTX entry point
+		.set MEM_USR,0xa000		# Client start
+		.set BDA_BOOT,0x472		# Boot howto flag
+
+/* Misc. Constants */
+		.set SIZ_PAG,0x1000		# Page size
+		.set SIZ_SEC,0x200		# Sector size
+
+		.globl start
+		.code16
+
+/*
+ * Copy BTX and boot2 to the right locations and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+start:		xor %cx,%cx			# Zero
+		mov %cx,%es			# Address
+		mov %cx,%ds			#  data
+		mov %cx,%ss			# Set up
+		mov $start,%sp			#  stack
+/*
+ * BTX is right after us at 'end'.  We read the length of BTX out of
+ * its header to find boot2.  We need to copy BTX to MEM_BTX and boot2
+ * to MEM_USR.  We aren't sure exactly how long boot2 is, but we assume
+ * it can't be longer than 256k, so we just always copy 256k.
+ */
+		mov $end,%bx			# BTX
+
+		cld				# Copy forwards
+
+		mov 0xa(%bx),%cx		# Get BTX length and set
+		mov %bx,%si			#  %si to start of BTX
+		mov $MEM_BTX,%di		# start of BTX at MEM_BTX
+		rep movsb			# Move BTX
+
+		mov 0xa(%bx),%si		# Get BTX length and set
+		add %bx,%si			#  %si to start of boot2
+		mov %si,%ax			# Align %ds:%si on a
+		shr $4,%ax			#  paragraph boundary
+		and $0xf,%si			#  with the smallest
+		mov %ax,%ds			#  possible %si
+
+		mov %si,%bp			# Save the 0..15 offset in %bp
+
+		xor %di,%di
+		mov $MEM_USR/16,%ax
+		mov %ax,%es
+
+		mov $4,%dh			# Repeat 4 times ie. copy 256k
+
+copy64k:	sub %si,%cx			# First, copy 64k-%si bytes
+		rep movsb			# Now %si=64k and %di=64k-%bp
+
+		mov %ds,%ax
+		add $0x1000,%ax			# Actually %si=0
+		mov %ax,%ds			#  so move %ds 64k ahead
+
+		mov %bp,%cx			# Next, copy %bp bytes
+		rep movsb			# Now %si=%bp and %di=64k
+
+		mov %es,%ax
+		add $0x1000,%ax			# Actually %di=0
+		mov %ax,%es			#  so move %es 64k ahead
+
+		dec %dh
+		jnz copy64k
+		mov $0,%ax			# zero segment register
+		mov %ax,%ds
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: 	cli				# Disable interrupts
+seta20.1:	dec %cx				# Timeout?
+		jz seta20.3			# Yes
+		inb $0x64,%al			# Get status
+		testb $0x2,%al			# Busy?
+		jnz seta20.1			# Yes
+		movb $0xd1,%al			# Command: Write
+		outb %al,$0x64			#  output port
+seta20.2:	inb $0x64,%al			# Get status
+		testb $0x2,%al			# Busy?
+		jnz seta20.2			# Yes
+		movb $0xdf,%al			# Enable
+		outb %al,$0x60			#  A20
+seta20.3:	sti				# Enable interrupts
+
+/*
+ * Save drive number from BIOS so boot2 can see it and start BTX.
+ */
+		movb %dl,MEM_ARG
+		jmp MEM_JMP			# Start BTX
+
+endorg:		.org MEM_USR-MEM_ORG-(endorg-start)
+end:
diff --git sys/boot/i386/tgptboot/helper.c sys/boot/i386/tgptboot/helper.c
new file mode 100644
index 0000000..115d7a2
--- /dev/null
+++ sys/boot/i386/tgptboot/helper.c
@@ -0,0 +1,249 @@
+/*-
+ * derived from /sys/boot/common/boot.c:
+ *   Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ *   All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+
+
+#include "bootstrap.h"
+
+
+vm_offset_t loadaddr = 0;
+struct preloaded_file *preloaded_files = NULL;
+
+static struct kernel_module *file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
+void	file_insert_tail(struct preloaded_file *fp);
+int	file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
+
+
+int
+getrootmount(char *ignored)
+{
+	if (NULL == getenv("vfs.root.mountfrom"))
+		return 1;
+	return 0;
+}
+
+int
+file_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
+{
+	static int last_file_format = 0;
+	struct preloaded_file *fp;
+	int error;
+	int i;
+
+	if (archsw.arch_loadaddr != NULL)
+		dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
+
+	error = EFTYPE;
+	for (i = last_file_format, fp = NULL;
+	    file_formats[i] && fp == NULL; i++) {
+		error = (file_formats[i]->l_load) (filename, dest, &fp);
+		if (error == 0) {
+			fp->f_loader = last_file_format = i;	/* remember the loader */
+			*result = fp;
+			break;
+		} else if (last_file_format == i && i != 0) {
+			/* Restart from the beginning */
+			i = -1;
+			last_file_format = 0;
+			fp = NULL;
+			continue;
+		}
+		if (error == EFTYPE)
+			continue;	/* Unknown to this handler? */
+		if (error) {
+			printf("can't load file '%s': %s",
+			    filename, strerror(error));
+			break;
+		}
+	}
+	return (error);
+}
+
+void
+file_insert_tail(struct preloaded_file *fp)
+{
+	struct preloaded_file *cm;
+
+	/* Append to list of loaded file */
+	fp->f_next = NULL;
+	if (preloaded_files == NULL) {
+		preloaded_files = fp;
+	} else {
+		for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next);
+		cm->f_next = fp;
+	}
+}
+
+struct preloaded_file *
+file_alloc(void)
+{
+	struct preloaded_file *fp;
+
+	if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
+		bzero(fp, sizeof(struct preloaded_file));
+	}
+	return (fp);
+}
+
+void
+file_discard(struct preloaded_file *fp)
+{
+	struct file_metadata *md, *md1;
+	struct kernel_module *mp, *mp1;
+
+	if (fp == NULL)
+		return;
+	md = fp->f_metadata;
+	while (md) {
+		md1 = md;
+		md = md->md_next;
+		free(md1);
+	}
+	mp = fp->f_modules;
+	while (mp) {
+		if (mp->m_name)
+			free(mp->m_name);
+		mp1 = mp;
+		mp = mp->m_next;
+		free(mp1);
+	}
+	if (fp->f_name != NULL)
+		free(fp->f_name);
+	if (fp->f_type != NULL)
+		free(fp->f_type);
+	if (fp->f_args != NULL)
+		free(fp->f_args);
+	free(fp);
+}
+
+struct preloaded_file *
+file_findfile(char *name, char *type)
+{
+	struct preloaded_file *fp;
+
+	for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
+		if (((name == NULL) || !strcmp(name, fp->f_name)) &&
+		    ((type == NULL) || !strcmp(type, fp->f_type)))
+			break;
+	}
+	return (fp);
+}
+
+void
+file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
+{
+	struct file_metadata *md;
+
+	md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
+	md->md_size = size;
+	md->md_type = type;
+	bcopy(p, md->md_data, size);
+	md->md_next = fp->f_metadata;
+	fp->f_metadata = md;
+}
+
+struct kernel_module *
+file_findmodule(struct preloaded_file *fp, char *modname,
+    struct mod_depend *verinfo)
+{
+	struct kernel_module *mp, *best;
+	int bestver, mver;
+
+	if (fp == NULL) {
+		for (fp = preloaded_files; fp; fp = fp->f_next) {
+			mp = file_findmodule(fp, modname, verinfo);
+			if (mp)
+				return (mp);
+		}
+		return (NULL);
+	}
+	best = NULL;
+	bestver = 0;
+	for (mp = fp->f_modules; mp; mp = mp->m_next) {
+		if (strcmp(modname, mp->m_name) == 0) {
+			if (verinfo == NULL)
+				return (mp);
+			mver = mp->m_version;
+			if (mver == verinfo->md_ver_preferred)
+				return (mp);
+			if (mver >= verinfo->md_ver_minimum &&
+			    mver <= verinfo->md_ver_maximum &&
+			    mver > bestver) {
+				best = mp;
+				bestver = mver;
+			}
+		}
+	}
+	return (best);
+}
+
+int
+file_addmodule(struct preloaded_file *fp, char *modname, int version,
+    struct kernel_module **newmp)
+{
+	struct kernel_module *mp;
+	struct mod_depend mdepend;
+
+	bzero(&mdepend, sizeof(mdepend));
+	mdepend.md_ver_preferred = version;
+	mp = file_findmodule(fp, modname, &mdepend);
+	if (mp)
+		return (EEXIST);
+	mp = malloc(sizeof(struct kernel_module));
+	if (mp == NULL)
+		return (ENOMEM);
+	bzero(mp, sizeof(struct kernel_module));
+	mp->m_name = strdup(modname);
+	mp->m_version = version;
+	mp->m_fp = fp;
+	mp->m_next = fp->f_modules;
+	fp->f_modules = mp;
+	if (newmp)
+		*newmp = mp;
+	return (0);
+}
+
+struct file_metadata *
+file_findmetadata(struct preloaded_file *fp, int type)
+{
+	struct file_metadata *md;
+
+	for (md = fp->f_metadata; md != NULL; md = md->md_next)
+		if (md->md_type == type)
+			break;
+	return (md);
+}
diff --git sys/boot/i386/tgptboot/tgptboot.8 sys/boot/i386/tgptboot/tgptboot.8
new file mode 100644
index 0000000..a6db09e
--- /dev/null
+++ sys/boot/i386/tgptboot/tgptboot.8
@@ -0,0 +1,49 @@
+.\" Copyright (c) 2014, GSMK mbh
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 23, 2014
+.Dt TGPTBOOT 8
+.Os
+.Sh NAME
+.Nm tgptboot
+.Nd TPM enabled GPT bootcode and UFS loader for BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a UFS partition on a
+GPT-partitioned disk.
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition with
+.Xr gpart 8 .
+.Sh IMPLEMENTATION NOTES
+TODO
+
+.Sh SEE ALSO
+.Xr tgptbootdata 8 ,
+.Xr loader 8 ,
+.Xr tpmbr 8 ,
+.Xr gptboot 8
diff --git sys/boot/i386/tgptboot/tgptboot.c sys/boot/i386/tgptboot/tgptboot.c
new file mode 100644
index 0000000..89bc6f5
--- /dev/null
+++ sys/boot/i386/tgptboot/tgptboot.c
@@ -0,0 +1,310 @@
+/*-
+ * based on /sys/boot/i386/gptboot.c:  Copyright (c) 1998 Robert Nordier
+ * and /sys/boot/i386/loader/main.c: Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/psl.h>
+#include <sys/gpt.h>
+#include <machine/bootinfo.h>
+#include <btxv86.h>
+
+
+#include <stand.h>
+#include <stddef.h>
+#include <string.h>
+#include <machine/cpufunc.h>
+#include <sys/reboot.h>
+
+#include "bootstrap.h"
+#include "common/bootargs.h"
+#include "libi386/libi386.h"
+
+#include "gpt.h"
+
+#define ARGS		0x900
+#define NDEV		3
+#define MEM_BASE	0x12
+#define MEM_EXT 	0x15
+
+#define DRV_HARD	0x80
+#define DRV_MASK	0x7f
+
+#define TYPE_AD		0
+#define TYPE_DA		1
+#define TYPE_MAXHARD	TYPE_DA
+#define TYPE_FD		2
+
+extern uint32_t _end;
+
+static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
+uint32_t opts;				/* drv.c needs this */
+
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static struct dsk dsk;
+struct bootinfo bootinfo;
+
+static u_int32_t initial_howto;
+
+struct arch_switch archsw;		/* MI/MD interface boundary */
+
+extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
+
+static int load(struct bootinfo *initial_bootinfo, u_int32_t initial_bootdev);
+
+static uint32_t memsize(void);
+
+static int isa_inb(int port);
+static void isa_outb(int port, int value);
+static void extract_currdev(struct bootinfo *, u_int32_t);
+void	exit(int code);
+int	main(void);
+
+extern int tloader(void);
+
+static char dma_secbuf[DEV_BSIZE];
+static uint8_t dsk_meta;
+
+static inline uint32_t
+memsize(void)
+{
+
+	v86.addr = MEM_EXT;
+	v86.eax = 0x8800;
+	v86int();
+	return (v86.eax);
+}
+
+static inline void
+clear_screen(void)
+{
+	v86.addr = 0x10;
+	v86.eax = 0x0700;		/* scroll down, entire window */
+	v86.ecx = 0x0;			/* 0,0 as upper left corner */
+	v86.edx = 0x184f;		/* 24,79 as lower right corner */
+	v86.ebx = 0x0700;		/* normal attributes */
+	v86int();
+	v86.eax = 0x0200;		/* set cursor position */
+	v86.edx = 0x0;			/* cursor at 0,0 */
+	v86.ebx = 0x0;			/* video page 0 */
+	v86int();
+}
+
+static int
+gptinit(void)
+{
+	if (gptread(&freebsd_ufs_uuid, &dsk, dma_secbuf) == -1) {
+		printf("%s: unable to load GPT\n", BOOTPROG);
+		return (-1);
+	}
+	if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) {
+		printf("%s: no UFS partition was found\n", BOOTPROG);
+		return (-1);
+	}
+	dsk_meta = 0;
+	return (0);
+}
+
+int
+main(void)
+{
+	clear_screen();
+	v86.ctl = V86_FLAGS;
+	v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+	dsk.drive = *(uint8_t *)PTOV(ARGS);
+	dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+	dsk.unit = dsk.drive & DRV_MASK;
+	dsk.part = -1;
+	dsk.start = 0;
+	bootinfo.bi_version = BOOTINFO_VERSION;
+	bootinfo.bi_size = sizeof(bootinfo);
+	bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or
+					 * kernel */
+	bootinfo.bi_extmem = memsize();
+	bootinfo.bi_memsizes_valid++;
+
+	if (gptinit() != 0)
+		return (-1);
+
+	for (;;) {
+		bootinfo.bi_bios_dev = dsk.drive;
+		load(&bootinfo, MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff));
+		gptbootfailed(&dsk);
+		if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1)
+			break;
+		dsk_meta = 0;
+	}
+	panic("out of bootable UFS filesystems");
+}
+
+/* provide this for panic, as it's not in the startup code */
+void
+exit(int code)
+{
+	__exit(code);
+}
+
+
+/* ISA bus access functions for PnP. */
+static int
+isa_inb(int port)
+{
+	return (inb(port));
+}
+
+static void
+isa_outb(int port, int value)
+{
+	outb(port, value);
+}
+
+/*
+ * Set the 'current device' by (if possible) recovering the boot device as
+ * supplied by the initial bootstrap.
+ *
+ */
+static void
+extract_currdev(struct bootinfo *initial_bootinfo, u_int32_t initial_bootdev)
+{
+	struct i386_devdesc new_currdev;
+	int biosdev = -1;
+
+	/* Assume we are booting from a BIOS disk by default */
+	new_currdev.d_dev = &biosdisk;
+
+	/* new-style boot loaders such as pxeldr and cdldr */
+	if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
+		/* The passed-in boot device is bad */
+		new_currdev.d_kind.biosdisk.slice = -1;
+		new_currdev.d_kind.biosdisk.partition = 0;
+		biosdev = -1;
+	} else {
+		new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1;
+		new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
+		biosdev = initial_bootinfo->bi_bios_dev;
+
+		/*
+		 * If we are booted by an old bootstrap, we have to guess at the BIOS
+		 * unit number.  We will lose if there is more than one disk type
+		 * and we are not booting from the lowest-numbered disk type
+		 * (ie. SCSI when IDE also exists).
+		 */
+		if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2))	/* biosdev doesn't match
+									 * major */
+			biosdev = 0x80 + B_UNIT(initial_bootdev);	/* assume harddisk */
+	}
+	new_currdev.d_type = new_currdev.d_dev->dv_type;
+
+	/*
+	 * If we are booting off of a BIOS disk and we didn't succeed in determining
+	 * which one we booted off of, just use disk0: as a reasonable default.
+	 */
+	if ((new_currdev.d_type == biosdisk.dv_type) &&
+	    ((new_currdev.d_unit = bd_bios2unit(biosdev)) == -1)) {
+		printf("Can't work out which disk we are booting from.\n"
+		    "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev);
+		new_currdev.d_unit = 0;
+
+	}
+	env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),
+	    i386_setcurrdev, env_nounset);
+	env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
+	    env_nounset);
+}
+
+int
+load(struct bootinfo *initial_bootinfo, u_int32_t initial_bootdev)
+{
+
+
+	static void *heap_top;
+	static void *heap_bottom;
+	int i;
+	int error;
+
+	initial_howto = 0x80000000;
+
+	/*
+	 * Initialise the heap as early as possible.  Once this is done, malloc() is usable.
+	*/
+	bios_getmem();
+
+	if (high_heap_size > 0) {
+		heap_top = PTOV(high_heap_base + high_heap_size);
+		heap_bottom = PTOV(high_heap_base);
+		if (high_heap_base < memtop_copyin)
+			memtop_copyin = high_heap_base;
+	} else {
+		heap_top = (void *)PTOV(bios_basemem);
+		heap_bottom = (void *)_end;
+	}
+	setheap(heap_bottom, heap_top);
+
+	bi_setboothowto(initial_howto);
+
+	if (initial_howto & RB_MULTIPLE) {
+		if (initial_howto & RB_SERIAL)
+			setenv("console", "comconsole vidconsole", 1);
+		else
+			setenv("console", "vidconsole comconsole", 1);
+	} else if (initial_howto & RB_SERIAL)
+		setenv("console", "comconsole", 1);
+	else if (initial_howto & RB_MUTE)
+		setenv("console", "nullconsole", 1);
+	cons_probe();
+
+	/*
+     * Initialise the block cache
+     */
+	bcache_init(32, 512);		/* 16k cache XXX tune this */
+
+	archsw.arch_autoload = i386_autoload;
+	archsw.arch_getdev = i386_getdev;
+	archsw.arch_copyin = i386_copyin;
+	archsw.arch_copyout = i386_copyout;
+	archsw.arch_readin = i386_readin;
+	archsw.arch_isainb = isa_inb;
+	archsw.arch_isaoutb = isa_outb;
+
+	/*
+	 * March through the device switch probing for things.
+	 */
+	for (i = 0; devsw[i] != NULL; i++)
+		if (devsw[i]->dv_init != NULL)
+			(devsw[i]->dv_init) ();
+	printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024);
+	if (initial_bootinfo != NULL) {
+		initial_bootinfo->bi_basemem = bios_basemem / 1024;
+		initial_bootinfo->bi_extmem = bios_extmem / 1024;
+	}
+	/* detect ACPI for future reference */
+	biosacpi_detect();
+
+	/* detect SMBIOS for future reference */
+	smbios_detect();
+
+	printf("\n");
+	extract_currdev(initial_bootinfo, initial_bootdev);	/* set $currdev and *
+								 * $loaddev */
+	setenv("LINES", "24", 1);	/* optional */
+	bios_getsmap();
+
+	error = tloader();
+	printf("tloader returned %d\n", error);
+	return (error);
+}
diff --git sys/boot/i386/tgptboot/tloader.c sys/boot/i386/tgptboot/tloader.c
new file mode 100644
index 0000000..a096de4
--- /dev/null
+++ sys/boot/i386/tgptboot/tloader.c
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 2014, GSMK mbh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * get key and data path from nvram (or fallback) then
+ * decrypt, checksum and process the data (set environment,
+ * check and load kernel+modules, load disk keys), boot.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "rijndael-api-fst.h"
+#include "sha2.h"
+
+#include "tpm.h"
+
+#ifdef TLOADER_VERBOSE
+#define v_printf printf
+#else
+#define v_printf(...) while(0)
+#endif
+
+#define		FALLBACK_KEY		"/boot/magic/key"
+#define		FALLBACK_DATA  		"/boot/magic/data"
+#define		FALLBACK_FORCE_DATA	"/boot/magic/data_force"
+
+/* Error Codes */
+enum {
+	E_TPM_STATUS = -7,
+	E_TPM_NV_READ = -8,
+	E_OPEN = -9,
+	E_READ = -10,
+	E_STAT = -11,
+	E_NO_KEY = -12,
+	E_NO_DATA = -13,
+	E_MALLOC = -14,
+	E_DIGEST_MISMATCH = -15,
+	E_PARSE = -16,
+	E_UNSETENV = -17,
+	E_SETENV = -18,
+	E_SIZE_MISMATCH = -19,
+	E_FILE_LOAD = -20,
+	E_NO_KERNEL = -21,
+};
+
+extern vm_offset_t loadaddr;
+extern void file_insert_tail(struct preloaded_file *mp);
+extern int file_load(char *, vm_offset_t, struct preloaded_file **);
+
+int	tloader(void);
+
+/* data tags */
+enum {
+	T_env = 8,
+	T_env_Key = 9,
+	T_env_Val = 10,
+	T_key = 16,
+	T_key_Prov = 17,
+	T_key_Key = 18,
+	T_key_Name = 19,
+	T_load = 32,
+	T_load_Path = 33,
+	T_load_Size = 34,
+	T_load_Args = 35,
+	T_load_Hash = 36,
+};
+
+enum {
+	C_LEN = 4,			/* four bytes (Big Endian) length(s) */
+	C_TAG = 1,			/* one byte TAG */
+};
+
+/* cipher/hash constants */
+enum {
+	AES_KEY_SIZE = 32,
+	AES_KEY_BITS = (AES_KEY_SIZE * 8),
+	AES_BLOCK_SIZE = 16,
+	AES_BLOCK_BITS = (AES_BLOCK_SIZE * 8),
+	SHA512_DIGEST_SIZE = 64,
+};
+
+typedef struct {
+	uint8_t	tag;
+	uint32_t len;
+	const uint8_t *val;
+} tlv_t;
+
+typedef struct saved_env {
+	char   *name;
+	char   *value;
+	struct saved_env *next;
+} saved_env_t;
+
+typedef struct unset_env {
+	char   *name;
+	struct unset_env *next;
+} unset_env_t;
+
+static inline uint32_t
+get_32be(const uint8_t *p)
+{
+	return (uint32_t)p[0] << 24 |
+	(uint32_t)p[1] << 16 |
+	(uint32_t)p[2] << 8 |
+	(uint32_t)p[3];
+}
+
+static int
+get_key_tpm(uint8_t *key)
+{
+	uint32_t r;
+
+	if (0 != tpm_status()) {
+		v_printf("key from nv ram failed: tpm_status\n");
+		return E_TPM_STATUS;
+	}
+	if (0 != (r = key_from_tpm_nvram(key))) {
+		v_printf("key from nv ram failed: 0x%08x\n", r);
+		return E_TPM_NV_READ;
+	}
+	v_printf("key from nv ram.\n");
+	return 0;
+}
+
+static int
+get_key(uint8_t *key)
+{
+	int fd;
+	struct stat sb;
+
+	if (0 == get_key_tpm(key))
+		return 0;
+	if (-1 == (fd = open(FALLBACK_KEY, O_RDONLY))) {
+		v_printf("key from fallback failed: open\n");
+		return E_OPEN;
+	}
+	if (AES_KEY_SIZE != read(fd, key, AES_KEY_SIZE)) {
+		close(fd);
+		v_printf("key from fallback failed: read\n");
+		return E_READ;
+	}
+	close(fd);
+	v_printf("key from fallback.\n");
+	return 0;
+}
+
+static int
+get_path_tpm(char **path)
+{
+	uint32_t r;
+	struct stat sb;
+
+	if (0 != tpm_status()) {
+		v_printf("path from nv ram failed: tpm_status\n");
+		return E_TPM_STATUS;
+	}
+	if (0 != (r = path_from_tpm_nvram((uint8_t **)path))) {
+		v_printf("path from nv ram failed: 0x%08x\n", r);
+		return E_TPM_NV_READ;
+	}
+	if (0 != stat(*path, &sb)) {
+		v_printf("path from nv ram failed: stat\n");
+		return E_STAT;
+	}
+	v_printf("path from nv ram: %s\n", *path);
+	return 0;
+}
+
+static int
+get_path(char **path)
+{
+	struct stat sb;
+
+	if (0 != stat(FALLBACK_FORCE_DATA, &sb))
+		if (0 == get_path_tpm(path))
+			return 0;
+	if (0 != stat(FALLBACK_DATA, &sb)) {
+		v_printf("path from fallback failed: stat\n");
+		return E_STAT;
+	}
+	*path = strdup(FALLBACK_DATA);
+	v_printf("path from fallback: %s\n", *path);
+	return 0;
+}
+
+static int
+decrypt_data(uint8_t **buf, size_t *buf_size, const char *path, const uint8_t *key)
+{
+	int fd, cnt, r;
+	uint8_t ctbuf[AES_BLOCK_SIZE], ptbuf[AES_BLOCK_SIZE];
+	uint8_t sha_result[SHA512_DIGEST_SIZE], checksum[SHA512_DIGEST_SIZE];
+	uint8_t *p;
+	SHA512_CTX sha;
+	keyInstance ki;
+	cipherInstance ci;
+
+	if (-1 == (fd = open(path, O_RDONLY)))
+		return E_OPEN;
+
+	rijndael_cipherInit(&ci, MODE_CBC, NULL);
+	rijndael_makeKey(&ki, DIR_DECRYPT, AES_KEY_BITS, (char *)key);
+
+	/* read and decrypt checksum */
+	cnt = SHA512_DIGEST_SIZE / AES_BLOCK_SIZE;
+	p = checksum;
+	while (cnt) {
+		if (AES_BLOCK_SIZE != read(fd, ctbuf, AES_BLOCK_SIZE)) {
+			r = E_READ;
+			goto fail;
+		}
+		r = rijndael_blockDecrypt(&ci, &ki, ctbuf, AES_BLOCK_BITS, p);
+		if (0 > r)
+			goto fail;
+		rijndael_cipherInit(&ci, MODE_CBC, (char *)ctbuf);
+		p += AES_BLOCK_SIZE;
+		cnt--;
+	}
+	SHA512_Init(&sha);
+
+	/* read and decrypt buf_size, checksum the block */
+	if (AES_BLOCK_SIZE != read(fd, ctbuf, AES_BLOCK_SIZE)) {
+		r = E_READ;
+		goto fail;
+	}
+	rijndael_blockDecrypt(&ci, &ki, ctbuf, AES_BLOCK_BITS, ptbuf);
+	rijndael_cipherInit(&ci, MODE_CBC, (char *)ctbuf);
+	*buf_size = get_32be(ptbuf);
+	SHA512_Update(&sha, ptbuf, AES_BLOCK_SIZE);
+
+	/* alloc buf */
+	if (NULL == (*buf = (uint8_t *)malloc(*buf_size))) {
+		r = E_MALLOC;
+		goto fail;
+	}
+	cnt = *buf_size;
+	p = *buf;
+
+	/* read and decrypt the rest int buf */
+	while (cnt >= AES_BLOCK_SIZE) {
+		if (AES_BLOCK_SIZE != read(fd, ctbuf, AES_BLOCK_SIZE)) {
+			r = E_READ;
+			goto fail;
+		}
+		rijndael_blockDecrypt(&ci, &ki, ctbuf, AES_BLOCK_BITS, p);
+		rijndael_cipherInit(&ci, MODE_CBC, (char *)ctbuf);
+		p += AES_BLOCK_SIZE;
+		cnt -= AES_BLOCK_SIZE;
+	}
+	if (cnt) {
+		if (AES_BLOCK_SIZE != read(fd, ctbuf, AES_BLOCK_SIZE)) {
+			r = E_READ;
+			goto fail;
+		}
+		rijndael_blockDecrypt(&ci, &ki, ctbuf, AES_BLOCK_BITS, ptbuf);
+		memcpy(p, ptbuf, cnt);
+	}
+	close(fd);
+	bzero(&ki, sizeof(ki));
+	bzero(&ci, sizeof(ci));
+
+	/* check sha512 digest */
+	SHA512_Update(&sha, *buf, *buf_size);
+	SHA512_Final(sha_result, &sha);
+	if (memcmp(sha_result, checksum, SHA512_DIGEST_SIZE)) {
+		free(*buf);
+		*buf = NULL;
+		return E_DIGEST_MISMATCH;
+	}
+	return 0;
+fail:
+	close(fd);
+	bzero(&ci, sizeof(ci));
+	bzero(&ki, sizeof(ki));
+	if (NULL != *buf) {
+		bzero(buf, *buf_size);
+		free(*buf);
+		*buf = NULL;
+	}
+	return r;
+}
+
+static int
+find_tlv(tlv_t *res, const uint8_t *buf, size_t buf_size)
+{
+	uint8_t tag;
+	uint32_t len;
+
+	while (buf_size >= (C_TAG + C_LEN)) {
+		tag = *buf++;
+		buf_size--;
+		len = get_32be(buf);
+		buf += C_LEN;
+		buf_size -= C_LEN;
+		if (buf_size < len)
+			break;
+		if (tag == res->tag) {
+			res->len = len;
+			res->val = buf;
+			return 0;
+		}
+		buf += len;
+		buf_size -= len;
+	}
+	return 1;
+}
+
+static int
+process_env(tlv_t *x)
+{
+	tlv_t name;
+	tlv_t value;
+
+	name.tag = T_env_Key;
+	value.tag = T_env_Val;
+	if (0 != find_tlv(&name, x->val, x->len))
+		return E_PARSE;
+	if (0 != find_tlv(&value, x->val, x->len))
+		return E_PARSE;
+	if (0 == value.len) {
+		if (0 != unsetenv((const char *)name.val))
+			return E_UNSETENV;
+	} else {
+		if (0 != setenv((const char *)name.val,
+		    (const char *)value.val, 1))
+			return E_SETENV;
+	}
+	return 0;
+}
+
+static int
+hash_and_size(const char *path, const uint8_t *checksum, uint32_t fsize)
+{
+	SHA512_CTX sha;
+	uint8_t buf[4096];
+	uint8_t sha_result[SHA512_DIGEST_SIZE];
+	uint32_t cnt = 0;
+	int fd;
+
+	if (-1 == (fd = open(path, O_RDONLY))) {
+		v_printf("size and digest (\"%s\") failed: open\n", path);
+		return E_OPEN;
+	}
+	SHA512_Init(&sha);
+
+	for (;;) {
+		ssize_t res;
+
+		if (0 > (res = read(fd, buf, sizeof(buf)))) {
+			v_printf("size and digest (\"%s\") failed: read\n", path);
+			return E_READ;
+		} else if (0 == res) {
+			close(fd);
+			if (cnt == fsize)
+				break;
+			else {
+				v_printf("size and digest (\"%s\") failed: size\n", path);
+				return E_SIZE_MISMATCH;
+			}
+		} else {
+			SHA512_Update(&sha, buf, res);
+			cnt += res;
+		}
+	}
+
+	SHA512_Final(sha_result, &sha);
+	if (memcmp(sha_result, checksum, SHA512_DIGEST_SIZE)) {
+		v_printf("size and digest (\"%s\") failed: digest\n", path);
+		return E_DIGEST_MISMATCH;
+	}
+	v_printf("size and digest ok: %s\n", path);
+	return 0;
+}
+
+static void
+unload(void)
+{
+	struct preloaded_file *fp;
+
+	while (preloaded_files != NULL) {
+		fp = preloaded_files;
+		preloaded_files = preloaded_files->f_next;
+		bzero((void *)fp->f_addr, fp->f_size);
+		file_discard(fp);
+	}
+	loadaddr = 0;
+	unsetenv("kernelname");
+}
+
+static int
+process_load(const tlv_t *x)
+{
+	tlv_t path, size, args, hash;
+	uint32_t fsize;
+	int r;
+	struct preloaded_file *fp;
+
+	path.tag = T_load_Path;
+	size.tag = T_load_Size;
+	args.tag = T_load_Args;
+	hash.tag = T_load_Hash;
+	if (0 != find_tlv(&path, x->val, x->len))
+		return E_PARSE;
+	if (0 != find_tlv(&size, x->val, x->len))
+		return E_PARSE;
+	if (0 != find_tlv(&args, x->val, x->len))
+		return E_PARSE;
+	if (0 != find_tlv(&hash, x->val, x->len))
+		return E_PARSE;
+
+	fsize = get_32be(size.val);
+	if (0 != (r = hash_and_size((const char *)path.val, hash.val, fsize)))
+		return r;
+
+	if (0 != (r = file_load((char *)path.val, loadaddr, &fp))) {
+		if (fp)
+			file_discard(fp);
+		return E_FILE_LOAD;
+	}
+	if (0 != args.len)
+		fp->f_args = strdup((char *)args.val);
+	else
+		fp->f_args = NULL;
+	loadaddr = fp->f_addr + fp->f_size;
+	file_insert_tail(fp);
+	return 0;
+}
+
+static int
+process_disk_key(const tlv_t *x)
+{
+	tlv_t prov, name, key;
+	struct preloaded_file *fp;
+	vm_offset_t laddr;
+
+	prov.tag = T_key_Prov;
+	key.tag = T_key_Key;
+	name.tag = T_key_Name;
+	if (0 != find_tlv(&prov, x->val, x->len))
+		return E_PARSE;
+	if (0 != find_tlv(&key, x->val, x->len))
+		return E_PARSE;
+	if (0 != find_tlv(&name, x->val, x->len))
+		return E_PARSE;
+
+	if (0 == loadaddr)
+		return E_NO_KERNEL;
+
+	laddr = loadaddr;
+	laddr = archsw.arch_copyin((void *)key.val, laddr, key.len);
+	fp = file_alloc();
+	fp->f_name = strdup((char *)name.val);
+	fp->f_type = strdup((char *)prov.val);
+	fp->f_args = NULL;
+	fp->f_metadata = NULL;
+	fp->f_loader = -1;
+	fp->f_addr = loadaddr;
+	fp->f_size = key.len;
+	loadaddr = laddr;
+	file_insert_tail(fp);
+
+	v_printf("disk key loaded: %s\n", prov.val);
+	return 0;
+}
+
+static int
+process_data(const uint8_t *buf, size_t buf_size)
+{
+	size_t pz = buf_size;
+	tlv_t x;
+	int r;
+
+	while (pz >= (C_TAG + C_LEN)) {
+		x.tag = *buf++;
+		pz--;
+		x.len = get_32be(buf);
+		buf += C_LEN;
+		pz -= C_LEN;
+		if (pz < x.len)
+			return E_PARSE;
+		x.val = buf;
+		switch (x.tag) {
+		case T_env:
+			if (0 != (r = process_env(&x)))
+				return r;
+			break;
+		case T_load:
+			if (0 != (r = process_load(&x)))
+				return r;
+			break;
+		case T_key:
+			if (0 != (r = process_disk_key(&x)))
+				return r;
+			break;
+		default:
+			return E_PARSE;
+		}
+		buf += x.len;
+		pz -= x.len;
+	}
+	if (0 == loadaddr)
+		return E_NO_KERNEL;
+	return 0;
+}
+
+static saved_env_t *
+save_env(void)
+{
+	saved_env_t *start = NULL, *p1 = NULL, *p;
+
+	struct env_var *ev = environ;
+
+	for (; NULL != ev; ev = ev->ev_next) {
+		p = malloc(sizeof(saved_env_t)),
+		    p->name = strdup(ev->ev_name);
+		if (NULL != ev->ev_value)
+			p->value = strdup(ev->ev_value);
+		else
+			p->value = NULL;
+		p->next = NULL;
+		if (NULL == p1)
+			start = p;
+		else
+			p1->next = p;
+		p1 = p;
+	}
+	return start;
+}
+
+static void
+free_saved_env(saved_env_t *p)
+{
+	saved_env_t *p1;
+
+	while (NULL != p) {
+		p1 = p;
+		p = p->next;
+		if (NULL != p1->value)
+			free(p1->value);
+		if (NULL != p1->name)
+			free(p1->name);
+		free(p1);
+	}
+}
+
+static void
+restore_env(saved_env_t *saved)
+{
+	saved_env_t *p;
+	unset_env_t *unset = NULL, *up1 = NULL, *up;
+	struct env_var *ev = environ;
+
+	for (; NULL != ev; ev = ev->ev_next) {
+		for (p = saved; NULL != p; p = p->next) {
+			if (!strcmp(ev->ev_name, p->name)) {
+				if (strcmp(ev->ev_value, p->value))
+					setenv(p->name, p->value, 1);
+				break;
+			}
+		}
+		if (NULL == p) {
+			up = malloc(sizeof(unset_env_t));
+			up->name = strdup(ev->ev_name);
+			up->next = NULL;
+			if (NULL == unset)
+				unset = up;
+			else
+				up1->next = up;
+			up1 = up;
+		}
+	}
+	while (NULL != unset) {
+		up = unset;
+		unset = unset->next;
+		unsetenv(up->name);
+		free(up->name);
+		free(up);
+	}
+}
+
+int
+tloader(void)
+{
+	int r;
+	size_t buf_size = 0;
+	uint8_t *buf = NULL;
+	uint8_t key[AES_KEY_SIZE];
+	char *path;
+	saved_env_t *env = save_env();
+
+	if (0 != get_key(key)) {
+		v_printf("no key.\n");
+		return E_NO_KEY;
+	}
+	if (0 != get_path(&path)) {
+		v_printf("no data path.\n");
+		return E_NO_DATA;
+	}
+	r = decrypt_data(&buf, &buf_size, path, key);
+	bzero(key, AES_KEY_SIZE);
+	free(path);
+
+	if (0 != r) {
+		v_printf("decrypt_data failed (%d)\n", r);
+		return r;
+	}
+	if (0 != (r = process_data(buf, buf_size))) {
+		restore_env(env);
+		bzero(buf, buf_size);
+		free(buf);
+		unload();
+		v_printf("process_data failed (%d).\n", r);
+		return r;
+	}
+	free_saved_env(env);
+	bzero(buf, buf_size);
+	free(buf);
+
+	if (0 == tpm_status()) {
+		uint32_t r = key_read_stclear();
+		v_printf("key_read_stclear: (%d)\n", r);
+	}
+	  
+	/* does not return: */
+	file_formats[preloaded_files->f_loader]->l_exec(preloaded_files);
+	return 0;
+}
diff --git sys/boot/i386/tgptboot/tpm.c sys/boot/i386/tgptboot/tpm.c
new file mode 100644
index 0000000..0bfc535
--- /dev/null
+++ sys/boot/i386/tgptboot/tpm.c
@@ -0,0 +1,426 @@
+/*-
+ * Copyright (c) 2014, GSMK mbh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * TPM support for tgptboot:
+ *
+ *  -> check TPM status
+ *  -> get tgptboot data key from NV area
+ *  -> get tgptboot data path from NV area
+ *
+ * Implements INT 1A TCG Functions TCG_StatusCheck ([4] 13.7) and
+ * TCG_PassThroughToTPMOutput ([4] 13.9). The pass-through function
+ * provides access for TPM_NV_ReadValue ([3] 20.4) and
+ * TPM_GetCapability ([3] 7.1). TPM_GetCapability is used to get the
+ * dataSize of an NV area and TPM_NV_ReadValue to get its content.
+ *
+ * [2] TPM Main
+ *     Part 2 TPM Structures
+ *     Specification version 1.2
+ *     Level 2 Revision 161
+ *     March 2011
+ * [3] TPM Main
+ *     Part 3 Commands
+ *     Specification Version 1.2
+ *     Level 2 Revision 116
+ *     1 March 2011
+ * [4] TCG PC Client Specific
+ *     Implementaion Specification for Conventional BIOS
+ *     Specification Version 1.21 Errata
+ *     Revision 1.00
+ *     February 24th, 2012
+ *     For TPM Family 1.2; Level 2
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <btxv86.h>
+#include <stand.h>
+
+#include "tpm.h"
+
+/* [4] 13.3 Return Codes */
+#define TCG_PC_OK 0x0000
+
+/* [4] 13.Application Level Interface - INT 1A TCG Functions */
+#define TCG_TCPA 0x41504354L
+
+/* [2] 6. TPM_TAG (Command and Response Tags) */
+#define TPM_TAG_RSP_COMMAND 0x00c4
+#define TPM_TAG_RQU_COMMAND 0x00c1
+
+/* [2] 3.1 TPM_STRUCTURE_TAG */
+#define TPM_TAG_NV_DATA_PUBLIC 0x0018
+
+/* [2] 17. Ordinals */
+#define TPM_ORD_NV_ReadValue 0x000000cfL
+#define TPM_ORD_GetCapability 0x00000065L
+
+/* [2] 21.1 TPM_CAPABILITY_AREA for TPM_GetCapability */
+#define TPM_CAP_NV_LIST 0x0000000dL
+#define TPM_CAP_NV_INDEX 0x00000011L
+
+/* [2] 16. Return Codes */
+#define TPM_SUCCESS 0x00000000L
+
+
+/* TCG_PassThroughToTPM input and output buffer,
+ * accessed in real mode by btx v86, must be located
+ * below 1MB
+ */
+uint8_t	TCG_IPB[512];
+uint8_t	TCG_OPB[1024];
+
+/* [4] 13.9.1 TCG_PassThroughToTPMInput Parameter Block */
+enum {
+	IPB_IPBLength = 0,
+	IPB_RESERVED1 = 2,
+	IPB_OPBLength = 4,
+	IPB_RESERVED2 = 6,
+	IPB_TPMOperandIn = 8
+};
+
+/* [4] 13.9.2 TCG_PassThroughToTPMOutput Parameter Block */
+enum {
+	OPB_OPBLength = 0,
+	OPB_RESERVED = 2,
+	OPB_TPMOperandOut = 4,
+};
+
+/* Outgoing Parameters
+ * [3]  7.1 TPM_GetCapability
+ *     20.4 TPM_NV_ReadValue
+ */
+enum {
+	OpOut_tag = 0,
+	OpOut_paramSize = 2,
+	OpOut_returnCode = 6,
+	OpOut_respSize = 10,
+	OpOut_resp = 14,
+};
+
+const size_t MAX_respSize = sizeof(TCG_OPB) - (OpOut_resp + OPB_TPMOperandOut);
+
+static inline void
+ld32BE(const uint8_t *p, uint32_t *x)
+{
+	*x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+static inline void
+st32BE(uint8_t *p, uint32_t x)
+{
+	p[0] = x >> 24;
+	p[1] = (x >> 16) & 0xff;
+	p[2] = (x >> 8) & 0xff;
+	p[3] = x & 0xff;
+}
+
+static inline void
+ld16BE(const uint8_t *p, uint16_t *x)
+{
+	*x = (p[0] << 8) | p[1];
+}
+
+static inline void
+st16BE(uint8_t *p, uint16_t x)
+{
+	p[0] = (x >> 8) & 0xff;
+	p[1] = x & 0xff;
+}
+
+static inline void
+st16LE(uint8_t *p, uint16_t x)
+{
+	p[0] = x & 0xff;
+	p[1] = (x >> 8) & 0xff;
+}
+
+/* [4] 13.9 TCG_PassThroughToTPM */
+static uint32_t
+TCG_PassThroughToTPM(uint8_t **TPMOperandOut, const uint8_t *TPMOperandIn)
+{
+	uint32_t paramSize = 0, IPBLength, ret;
+
+	ld32BE(TPMOperandIn + OpOut_paramSize, &paramSize);
+	IPBLength = paramSize + IPB_TPMOperandIn;
+	if (IPBLength > sizeof(TCG_IPB))
+		return 0xffff0001;
+
+	st16LE(TCG_IPB + IPB_IPBLength, IPBLength);
+	st16LE(TCG_IPB + IPB_RESERVED1, 0);
+	st16LE(TCG_IPB + IPB_OPBLength, sizeof(TCG_OPB));
+	st16LE(TCG_IPB + IPB_RESERVED2, 0);
+	memcpy(TCG_IPB + IPB_TPMOperandIn, TPMOperandIn, paramSize);
+
+	v86.ctl = 0;
+	v86.addr = 0x1a;
+	v86.eax = 0xbb02;
+	v86.ebx = TCG_TCPA;
+	v86.ecx = 0;
+	v86.edx = 0;
+	v86.es = VTOPSEG(TCG_IPB);
+	v86.edi = VTOPOFF(TCG_IPB);
+	v86.ds = VTOPSEG(TCG_OPB);
+	v86.esi = VTOPOFF(TCG_OPB);
+	v86int();
+
+	if (TCG_PC_OK != (ret = v86.eax))
+		goto fail;
+
+	ld32BE(TCG_OPB + OPB_TPMOperandOut + OpOut_paramSize, &paramSize);
+	if (paramSize > (sizeof(TCG_OPB) - OPB_TPMOperandOut)) {
+		ret = 0xffff0002;
+		goto fail;
+	}
+	if (NULL == (*TPMOperandOut = (uint8_t *)malloc(paramSize))) {
+		ret = 0xffff0003;
+		goto fail;
+	}
+	memcpy(*TPMOperandOut, TCG_OPB + OPB_TPMOperandOut, paramSize);
+
+fail:
+	bzero(TCG_IPB, IPBLength);
+	bzero(TCG_OPB, sizeof(TCG_OPB));
+	return ret;
+}
+
+static uint32_t
+TPM_NV_ReadValue_1(uint8_t *dst, uint32_t nvIndex, uint32_t offset,
+    uint32_t size)
+{
+	uint16_t tag;
+	uint32_t ret, returnCode, respSize, paramSize;
+	uint8_t OpIn[22];
+	uint8_t *OpOut;
+
+	/* [3] 20.4 Incoming Operands and  Sizes */
+	enum {
+		OpIn_tag = 0,
+		OpIn_paramSize = 2,
+		OpIn_ordinal = 6,
+		OpIn_nvIndex = 10,
+		OpIn_offset = 14,
+		OpIn_dataSize = 18,
+	};
+
+	st16BE(OpIn + OpIn_tag, TPM_TAG_RQU_COMMAND);
+	st32BE(OpIn + OpIn_paramSize, sizeof(OpIn));
+	st32BE(OpIn + OpIn_ordinal, TPM_ORD_NV_ReadValue);
+	st32BE(OpIn + OpIn_nvIndex, nvIndex);
+	st32BE(OpIn + OpIn_offset, offset);
+	st32BE(OpIn + OpIn_dataSize, size);
+
+	if (0 != (ret = TCG_PassThroughToTPM(&OpOut, OpIn)))
+		return ret;
+
+	ld32BE(OpOut + OpOut_paramSize, &paramSize);
+	ld32BE(OpOut + OpOut_returnCode, &returnCode);
+	if (0 != (ret = returnCode))
+		goto fail;
+
+	ld16BE(OpOut + OpOut_tag, &tag);
+	ld32BE(OpOut + OpOut_respSize, &respSize);
+	if (TPM_TAG_RSP_COMMAND != tag ||
+	    size != respSize) {
+		ret = 0xffff0004;
+		goto fail;
+	}
+	if (size)
+		memcpy(dst, OpOut + OpOut_resp, size);
+
+fail:
+	bzero(OpOut, paramSize);
+	free(OpOut);
+	return ret;
+}
+
+/* [3] 20.4 TPM_NV_ReadValue */
+static uint32_t
+TPM_NV_ReadValue(uint8_t *dst, uint32_t nvIndex, uint32_t offset, uint32_t size)
+{
+	uint32_t ret;
+
+	while (MAX_respSize <= size) {
+		if (0 != (ret = TPM_NV_ReadValue_1(dst, nvIndex, offset, MAX_respSize)))
+			return ret;
+		size -= MAX_respSize;
+		offset += MAX_respSize;
+		dst += MAX_respSize;
+	}
+	return TPM_NV_ReadValue_1(dst, nvIndex, offset, size);
+}
+
+/* [4] 13.7 TCG_StatusCheck */
+static uint32_t
+TCG_StatusCheck(uint8_t *major, uint8_t *minor, uint8_t **event_log)
+{
+	v86.ctl = 0;
+	v86.addr = 0x1a;
+	v86.eax = 0xbb00;
+	v86int();
+
+	if (v86.eax)
+		return (v86.eax);
+
+	if (v86.ebx != TCG_TCPA)
+		return 0xffff0005;
+
+	if (NULL != major && NULL != minor) {
+		*major = (uint8_t)((v86.ecx & 0xff00) >> 8);
+		*minor = (uint8_t)(v86.ecx & 0xff);
+	}
+	if (NULL != event_log)
+		*event_log = (uint8_t *)PTOV(v86.esi);
+
+	return 0;
+}
+
+/* [3] 7.1 TPM_GetCapability */
+static uint32_t
+TPM_GetCapability(uint32_t *respSize, uint8_t **resp, uint32_t capArea,
+    uint32_t subCapSize, uint8_t *subCap)
+{
+	uint16_t tag;
+	uint32_t ret, returnCode, paramSize;
+	uint8_t *OpIn;
+	uint8_t *OpOut;
+
+	/* [3] 7.1 Incoming Parameters and Sizes */
+	enum {
+		OpIn_tag = 0,
+		OpIn_paramSize = 2,
+		OpIn_ordinal = 6,
+		OpIn_capArea = 10,
+		OpIn_subCapSize = 14,
+		OpIn_subCap = 18,
+	};
+
+	paramSize = subCapSize + OpIn_subCap;
+	if (NULL == (OpIn = (uint8_t *)malloc(paramSize)))
+		return 0xffff0006;
+
+	st16BE(OpIn + OpIn_tag, TPM_TAG_RQU_COMMAND);
+	st32BE(OpIn + OpIn_paramSize, paramSize);
+	st32BE(OpIn + OpIn_ordinal, TPM_ORD_GetCapability);
+	st32BE(OpIn + OpIn_capArea, capArea);
+	st32BE(OpIn + OpIn_subCapSize, subCapSize);
+	if (0 != subCapSize)
+		memcpy(OpIn + OpIn_subCap, subCap, subCapSize);
+
+	ret = TCG_PassThroughToTPM(&OpOut, OpIn);
+
+	free(OpIn);
+
+	if (TCG_PC_OK != ret)
+		return ret;
+
+	ld32BE(OpOut + OpOut_returnCode, &returnCode);
+	if (0 != (ret = returnCode))
+		goto fail;
+
+	ld32BE(OpOut + OpOut_paramSize, &paramSize);
+	if (paramSize < OpOut_respSize) {
+		ret = 0xffff0007;
+		goto fail;
+	}
+	ld32BE(OpOut + OpOut_respSize, respSize);
+	ld16BE(OpOut + OpOut_tag, &tag);
+	if (TPM_TAG_RSP_COMMAND != tag ||
+	    paramSize != *respSize + OpOut_resp) {
+		ret = 0xffff0008;
+		goto fail;
+	}
+	memmove(OpOut, OpOut + OpOut_resp, *respSize);
+	*resp = OpOut;
+	return ret;
+
+fail:
+	free(OpOut);
+	return ret;
+}
+
+static uint32_t
+NV_dataSize(uint32_t nvIndex, uint32_t *dataSize)
+{
+	uint8_t subCap[4];
+	uint8_t *resp;
+	uint32_t ret, respSize;
+
+	st32BE(subCap, nvIndex);
+	ret = TPM_GetCapability(&respSize, &resp, TPM_CAP_NV_INDEX,
+	    sizeof(subCap), subCap);
+	if (0 != ret)
+		return ret;
+
+	/*
+	 * [2] 19.3 TPM_NV_DATA_PUBLIC: we are only interested in the
+	 * 'dataSize' element (last four bytes of the response)
+	 */
+	ld32BE(resp + (respSize - 4), dataSize);
+
+	free(resp);
+	return ret;
+}
+
+uint32_t
+tpm_status()
+{
+	uint32_t ret;
+	uint8_t major, minor;
+
+	if (0 != (ret = TCG_StatusCheck(&major, &minor, NULL)))
+		return ret;
+
+	if (major != 1 || minor != 2)
+		return 0xffff0009;
+
+	return 0;
+}
+
+uint32_t
+key_read_stclear()
+{
+	uint8_t *dummy = NULL;
+	return  TPM_NV_ReadValue(dummy, NV_KEY_INDEX, NV_KEY_OFFSET, 0);
+}
+
+uint32_t
+key_from_tpm_nvram(uint8_t *key)
+{
+	return TPM_NV_ReadValue(key, NV_KEY_INDEX, NV_KEY_OFFSET, NV_KEY_SIZE);
+}
+
+uint32_t
+path_from_tpm_nvram(uint8_t **path)
+{
+	uint32_t ret;
+	uint32_t dataSize;
+
+	if (0 != (ret = NV_dataSize(NV_PATH_INDEX, &dataSize)))
+		return ret;
+
+	if (NULL == (*path = (uint8_t *)malloc(dataSize + 1)))
+		return 0xffff000a;
+
+	*path[dataSize] = 0;
+	if (0 != (ret = TPM_NV_ReadValue(*path, NV_PATH_INDEX, 0, dataSize)))
+		free(*path);
+	else
+		*path[dataSize] = 0;
+
+	return ret;
+}
diff --git sys/boot/i386/tgptboot/tpm.h sys/boot/i386/tgptboot/tpm.h
new file mode 100644
index 0000000..36fc9c8
--- /dev/null
+++ sys/boot/i386/tgptboot/tpm.h
@@ -0,0 +1,34 @@
+#ifndef _TPM_H_
+#define _TPM_H_
+
+
+/* see TPM MainPart 2 TPM Structures Specification version 1.2
+ * 19.1 TPM_NV_INDEX
+ */
+#ifndef NV_KEY_INDEX
+#define NV_KEY_INDEX 0x20002323L
+#endif
+
+#ifndef NV_PATH_INDEX
+#define NV_PATH_INDEX 0x20004242L
+#endif
+
+#ifndef NV_KEY_OFFSET
+#define NV_KEY_OFFSET 0x00000000L
+#endif
+
+#ifndef NV_KEY_SIZE
+#define NV_KEY_SIZE 0x00000020
+#endif
+
+uint32_t key_from_tpm_nvram(uint8_t *key);
+uint32_t key_read_stclear(void);
+
+/* success: return 0, *path points to null terminated data
+ * failure: return != 0
+ */
+uint32_t path_from_tpm_nvram(uint8_t **path);
+
+uint32_t tpm_status(void);
+
+#endif
diff --git sys/boot/i386/tpmbr/Makefile sys/boot/i386/tpmbr/Makefile
new file mode 100644
index 0000000..c9754ea
--- /dev/null
+++ sys/boot/i386/tpmbr/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG=	tpmbr
+STRIP=
+BINMODE=${NOBINMODE}
+NO_MAN=
+SRCS=	${PROG}.s
+
+ORG=	0x600
+
+TPMBR_PCR_INDEX?=	0x09
+
+AFLAGS+=--defsym PCR_INDEX=${TPMBR_PCR_INDEX}
+LDFLAGS=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary
+
+.include <bsd.prog.mk>
diff --git sys/boot/i386/tpmbr/tpmbr.s sys/boot/i386/tpmbr/tpmbr.s
new file mode 100644
index 0000000..bc62a71
--- /dev/null
+++ sys/boot/i386/tpmbr/tpmbr.s
@@ -0,0 +1,297 @@
+#-
+# Copyright (c) 2007 Yahoo!, Inc.
+# All rights reserved.
+# Written by: John Baldwin <jhb@FreeBSD.org>
+# TCG Support added by: Stefan Grundmann (GSMK mbh)
+#
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the author nor the names of any co-contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7
+
+# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition
+# and boots it.
+
+		.set LOAD,0x7c00		# Load address
+		.set EXEC,0x600 		# Execution address
+		.set MAGIC,0xaa55		# Magic: bootable
+		.set SECSIZE,0x200		# Size of a single disk sector
+		.set DISKSIG,440		# Disk signature offset
+		.set STACK,EXEC+SECSIZE*4	# Stack address
+		.set GPT_ADDR,STACK		# GPT header address
+		.set GPT_SIG,0
+		.set GPT_SIG_0,0x20494645	# "EFI "
+		.set GPT_SIG_1,0x54524150	# "PART"
+		.set GPT_MYLBA,24
+		.set GPT_PART_LBA,72
+		.set GPT_NPART,80
+		.set GPT_PART_SIZE,84
+		.set PART_ADDR,GPT_ADDR+SECSIZE	# GPT partition array address
+		.set PART_TYPE,0
+		.set PART_START_LBA,32
+		.set PART_END_LBA,40
+		.set DPBUF,PART_ADDR+SECSIZE
+		.set DPBUF_SEC,0x10		# Number of sectors
+
+		.set NHRDRV,0x475		# Number of hard drives
+
+                # TCG related constants:
+#                .set PCR_INDEX,0x09            # The PCR number to which the hashed boot_code is to be extended
+#                                               # set via --defsym
+                .set EVENT_INFO,0x00002342      # The information value to be placed into the event field
+
+		.globl start			# Entry point
+		.code16
+
+#
+# Setup the segment registers for flat addressing and setup the stack.
+#
+start:		cld				# String ops inc
+		xorw %ax,%ax			# Zero
+		movw %ax,%es			# Address
+		movw %ax,%ds			#  data
+		movw %ax,%ss			# Set up
+		movw $STACK,%sp			#  stack
+#
+# Relocate ourself to a lower address so that we have more room to load
+# other sectors.
+#
+		movw $main-EXEC+LOAD,%si	# Source
+		movw $main,%di			# Destination
+		movw $SECSIZE-(main-start),%cx	# Byte count
+		rep				# Relocate
+		movsb				#  code
+#
+# Jump to the relocated code.
+#
+		jmp main-LOAD+EXEC		# To relocated code
+#
+# Validate drive number in %dl.
+#
+main:	 	cmpb $0x80,%dl			# Drive valid?
+		jb main.1			# No
+		movb NHRDRV,%dh			# Calculate the highest
+		addb $0x80,%dh			#  drive number available
+		cmpb %dh,%dl			# Within range?
+		jb main.2			# Yes
+main.1: 	movb $0x80,%dl			# Assume drive 0x80
+#
+# Load the GPT header and verify signature.  Try LBA 1 for the primary one and
+# the last LBA for the backup if it is broken.
+#
+main.2:		call getdrvparams		# Read drive parameters
+		movb $1,%dh			# %dh := 1 (reading primary)
+main.2a:	movw $GPT_ADDR,%bx
+		movw $lba,%si
+		call read			# Read header and check GPT sig
+		cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
+		jnz main.2b
+		cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
+		jnz main.2b
+		jmp load_part
+main.2b:	cmpb $1,%dh			# Reading primary?
+		jne err_pt			# If no - invalid table found
+#
+# Try alternative LBAs from the last sector for the GPT header.
+#
+main.3:		movb $0,%dh			# %dh := 0 (reading backup)
+		movw $DPBUF+DPBUF_SEC,%si	# %si = last sector + 1
+		movw $lba,%di			# %di = $lba
+main.3a:	decl (%si)			# 0x0(%si) = last sec (0-31)
+		movw $2,%cx
+		rep
+		movsw				# $lastsec--, copy it to $lba
+		jmp main.2a			# Read the next sector
+#
+# Load a partition table sector from disk and look for a FreeBSD boot
+# partition.
+#
+load_part:	movw $GPT_ADDR+GPT_PART_LBA,%si
+		movw $PART_ADDR,%bx
+		call read
+scan:		movw %bx,%si			# Compare partition UUID
+		movw $boot_uuid,%di		#  with FreeBSD boot UUID
+		movb $0x10,%cl
+		repe cmpsb
+		jnz next_part			# Didn't match, next partition
+#
+# We found a boot partition.  Load it into RAM starting at 0x7c00.
+#
+		movw %bx,%di			# Save partition pointer in %di
+		leaw PART_START_LBA(%di),%si
+		movw $LOAD/16,%bx
+		movw %bx,%es
+		xorw %bx,%bx
+#
+#TCG_StatusCheck, set tpm to 0x01 if successful
+#
+                pushal
+                movw $0xbb00,%ax
+                int $0x1a
+                test %eax,%eax
+                jnz tcg_status.0
+		cmpl tpm_sig,%ebx  # "TCPA"
+                jnz tcg_status.0
+                cmp $0x0102,%cx    # we need 1.2
+                jnz tcg_status.0
+                movb $0x01,tpm
+tcg_status.0:   popal
+load_boot:	push %si			# Save %si
+		call read
+		pop %si				# Restore
+#
+# TCG_CompactHashLogExtendEvent the sector at %es:%bx with EVENT_INFO into PCR_INDEX
+#
+                cmpb $0x01,tpm                  # only if we have a working
+                jnz load_boot.1                 # tpm
+                pushal
+                mov $0xe2e,%ax                  #
+                movw $0x7,%bx                   # put .
+                int $0x10                       #
+                movw $0xbb07,%ax
+                mov %bx,%di
+                movl $EVENT_INFO,%esi
+                movl tpm_sig,%ebx
+                movl $SECSIZE,%ecx
+                movl $PCR_INDEX,%edx
+                int $0x1a
+                test %eax,%eax
+                jnz err_tpm
+                popal
+load_boot.1:    movl PART_END_LBA(%di),%eax	# See if this was the last LBA
+		cmpl (%si),%eax
+		jnz next_boot
+		movl PART_END_LBA+4(%di),%eax
+		cmpl 4(%si),%eax
+		jnz next_boot
+		mov %bx,%es			# Reset %es to zero
+		jmp LOAD			# Jump to boot code
+next_boot:	incl (%si)			# Next LBA
+		adcl $0,4(%si)
+		mov %es,%ax			# Adjust segment for next
+		addw $SECSIZE/16,%ax		#  sector
+		cmp $0x9000,%ax			# Don't load past 0x90000,
+		jae err_big			#  545k should be enough for
+		mov %ax,%es			#  any boot code. :)
+		jmp load_boot
+#
+# Move to the next partition.  If we walk off the end of the sector, load
+# the next sector.  We assume that partition entries are smaller than 64k
+# and that they won't span a sector boundary.
+#
+next_part:	decl GPT_ADDR+GPT_NPART		# Was this the last partition?
+		jnz next_part.1
+                int $0x18                       # Fail, -> ROM BASIC via int 0x18
+next_part.1:    movw GPT_ADDR+GPT_PART_SIZE,%ax
+		addw %ax,%bx			# Next partition
+		cmpw $PART_ADDR+0x200,%bx	# Still in sector?
+		jb scan
+		incl GPT_ADDR+GPT_PART_LBA	# Next sector
+		adcl $0,GPT_ADDR+GPT_PART_LBA+4
+		jmp load_part
+#
+# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating
+# a EDD packet on the stack and passing it to the BIOS.  Trashes %ax and %si.
+#
+read:           pushl 0x4(%si)			# Set the LBA
+		pushl 0x0(%si)			#  address
+		pushw %es			# Set the address of
+		pushw %bx			#  the transfer buffer
+		pushw $0x1			# Read 1 sector
+		pushw $0x10			# Packet length
+		movw %sp,%si			# Packer pointer
+		movw $0x4200,%ax		# BIOS:	LBA Read from disk
+		int $0x13			# Call the BIOS
+		add $0x10,%sp			# Restore stack
+		jc err_rd			# If error
+                ret
+
+#
+# Check the number of LBAs on the drive index %dx.  Trashes %ax and %si.
+#
+getdrvparams:
+		movw $DPBUF,%si			# Set the address of result buf
+		movw $0x001e,(%si)		# len
+		movw $0x4800,%ax		# BIOS: Read Drive Parameters
+		int $0x13			# Call the BIOS
+		jc err_rd			# "I/O error" if error
+		ret
+#
+# Various error message entry points.
+#
+err_tpm:        movw $msg_tpm,%si               # "TPM error"
+                jmp putstr
+
+err_big: 	movw $msg_big,%si		# "Boot loader too
+		jmp putstr			#  large"
+
+err_pt: 	movw $msg_pt,%si		# "Invalid partition
+		jmp putstr			#  table"
+
+err_rd: 	movw $msg_rd,%si		# "I/O error loading
+		jmp putstr			#  boot loader"
+
+#
+# Output an ASCIZ string to the console via the BIOS.
+#
+putstr.0:	movw $0x7,%bx	 		# Page:attribute
+		movb $0xe,%ah			# BIOS: Display
+		int $0x10			#  character
+putstr: 	lodsb				# Get character
+		testb %al,%al			# End of string?
+		jnz putstr.0			# No
+putstr.1:	jmp putstr.1			# Await reset
+
+msg_tpm:        .asciz "!tpm"
+msg_big: 	.asciz "!big"
+msg_pt: 	.asciz "!PT"
+msg_rd: 	.asciz "!I/O"
+
+lba:		.quad 1				# LBA of GPT header
+
+tpm_sig:        .long 0x41504354
+
+tpm:            .byte 0x00
+
+boot_uuid:	.long 0x83bd6b9d
+		.word 0x7f41
+		.word 0x11dc
+		.byte 0xbe
+		.byte 0x0b
+		.byte 0x00
+		.byte 0x15
+		.byte 0x60
+		.byte 0xb8
+		.byte 0x4f
+		.byte 0x0f
+
+		.org DISKSIG,0x90
+sig:		.long 0				# OS Disk Signature
+		.word 0				# "Unknown" in PMBR
+
+partbl: 	.fill 0x10,0x4,0x0		# Partition table
+		.word MAGIC			# Magic number
