Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 25 Mar 2015 09:03:48 +0000
From:      Stefan Grundmann <sg2342@googlemail.com>
To:        freebsd-hackers@freebsd.org
Subject:   trusted gptboot preview [patch]
Message-ID:  <20150325090348.GA17422@bath>

next in thread | raw e-mail | index | archive | help

--EeQfGwPcQSOJBaQU
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

hi, the promised minimal howto got a bit longer than planed.

best regards

Stefan Grundmann

--------------------------------------------------------------------------

SRTM (Static Root Of Trust) FreeBSD boot

* Motivation

 Anti Evil Maid
 http://theinvisiblethings.blogspot.de/2011/09/anti-evil-maid.html


* Existing SRTM solutions

** TrustedGRUB
 - http://trustedgrub.sourceforge.net/
 - ported by BSSSD
 - GNU GRUB 1.* based
 - no GPT support
 - insufficient UFS support
 - no easy way to decrypt disk keys with secrets from TPM

** GRUB-IMA
 - http://sourceforge.net/projects/trousers/files/Grub-IMA/
 - same limitations as TrustedGRUB plus never ported to FreeBSD


* Deliverables

The attached patch should work on any FreeBSD-10 or FreeBSD-11 amd64 or x86
system.
TPM 1.2 is required.

** sys/boot/i386/tpmbr
 - this is sys/boot/i386/pmbr plus TCG (Trusted Computing Group) support
 - had to sacrifice most of the error messages to make space for
   the TCG_CompactHashLogExtendEvent code
 - the entire 'freebsd-boot' partition is measured (up to 545k)

** sys/boot/i386/tgptboot
 - gptboot(8) extended with parts of loader(8) and TCG support
 - get Key and config data path from TPM
 - read and decrypt the config data from UFS
 - use config data to set kernel environment, load disk keys,
   verify and load kernel + modules (from UFS)
 - boot the kernel

** sbin/tgptbootdata
 - create tgptboot config_data files


* Use Case

For a system that has TPM 1.2 enabled and configured to have a KEY in TPM NVRAM.
The system performs a (legacy) BIOS boot from a GTP disk that has tpmbr as
 bootcode and tgptboot as partcode in the 'freebsd-boot' partition.

 boot sequence, see [TCG] section 3.2:

 1. S-CRTM (Static Core Root of Trust)
   - TPM Startup
   - load and measure BIOS
   - execute BIOS
 2. BIOS
   - measure configuration
   - load and measure PMBR (Protective Master Boot Record)
   - execute PMBR
 3. PMBR
   - locate 'freebsd-boot' partition
   - load and measure 'freebsd-boot' partition (extend PCR9)
   - transfer control to tgptboot
 4. tgptboot
   - select UFS partition (take gptboot(8) attributes into account)
   - get KEY from TPM NVRAM
   - get DATA_PATH from TPM NVRAM
   - read and decrypt DATA at DATA_PATH using KEY
   - set kernel environment (if specified in DATA)
   - load and checksum kernel and modules (path, size and hash in DATA)
   - load disk keys (if specified in DATA)
   - execute kernel


Since each measurement modifies TPM PCRs (Platform Configuration Registers),
the TPM NVRAM index that contains the KEY can not be read if the current PCRs
do not match the PCRs at write time (for the set of PCRs the TPM NVRAM index
 write was sealed under).

If tgptboot can not contiune because of an error, the next GPT UFS partion is
selected (gptboot(8) behaviour).

kernel tpm(4) support is only needed for provisioning.

changes to anything measured into the PCRs included in the TPM NVRAM index write
for the KEY (BIOS update, changes to tpmbr or tgptboot, ...) require
 reprovisioning.

kernel update does not require tpm interaction.

[TCG] 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


* Setup (step by step tutorial)

SRTM on an ThinkPad X60

** preparations
*** buildsystem
 - FreeBSD-10.1-p8 amd64
 - r280275 in /usr/src
 - results of successfull buildworld and buildkernel (GENERIC) for r280275 in
   /usr/obj
*** tpm-tools 1.3.8
 - the security/tpm-tools in the ports tree is too old
   (missing nvram write support)
 - get the tpm-tools 1.3.8 port from
   https://github.com/sg2342/freebsd_port-tpm-tools.git
 - build packages for tpm-tools and its runtime dependencies

** build deliverables
*** apply 0001-trusted-gptboot-preview.patch to /usr/src

 # cd /usr/src && patch -p0 < path_to/0001_trusted-gptboot-preview.patch

*** build tpmbr

 # make -C sys/boot/i386/tpmbr clean obj
 # make -C sys/boot/i386/tpmbr depend all

*** build tgptboot

 # make -C sys/boot/i386/tgptboot clean obj
 # make -C sys/boot/i386/tgptboot depend all

*** build tgptbootdata

 # make -C sbin/tgptbootdata clean obj
 # make -C sbin/tgptbootdata depend all


** create provisioning USB stick

 - usb stick is da0 in the buildsystem
 - usb stick size is at least 2GB

*** create GPT schema and partitions

 # gpart destroy -F da0
 # gpart create -s GPT da0
 # gpart bootcode -b /usr/obj/usr/src/sys/boot/i386/tpmbr/tpmbr da0
 # gpart add -b 40 -s 384 -t freebsd-boot -l tgptboot -i 1 da0
 # dd if=/dev/zero of=/dev/da0p1
 # gpart bootcode -p /usr/obj/usr/src/sys/boot/i386/tgptboot/tgptboot -i 1 da0
 # gpart add -a 4k -s 131072 -t freebsd-ufs -l bootA -i 2 da0
 # gpart add -a 4k -s 131072 -t freebsd-ufs -l bootB -i 3 da0
 # gpart add -a 4k -s 1G -t freebsd-ufs -l toolRoot -i 4 da0

*** newfs

 # newfs -m 0 -L bootA /dev/gpt/bootA
 # newfs -m 0 -L bootB /dev/gpt/bootB
 # newfs -m 0 -L toolRoot /dev/gpt/toolRoot

*** installworld and kernel in toolRoot

 # mkdir -p /tmp/toolRoot && mount -o noatime /dev/ufs/toolRoot /tmp/toolRoot
 # cd /usr/src
 # make installworld DESTDIR=/tmp/toolRoot
 # make distribution DESTDIR=/tmp/toolRoot
 # make installkernel DESTDIR=/tmp/toolRoot

*** install tpm-tools in toolRoot

 # mkdir -p /tmp/toolRoot/tmp/pkg
 # mount_nullfs -o ro path_to_package_directory /tmp/toolRoot/tmp/pkg
 # pkg -c /tmp/toolRoot add /tmp/pkg/All/tpm-tools-1.3.8.txz
 # umount /tmp/toolRoot/tmp/pkg

 install a the tgptbootdata tool as well
 # cp /usr/obj/usr/src/sbin/tgptbootdata/tgptbootdata /tmp/toolRoot/sbin/

*** fix fstab in toolRoot

 # printf "/dev/ufs/toolRoot\t/\tufs\trw\t1 1\n" > /tmp/toolRoot/etc/fstab

*** install kernel in bootA

 # mkdir -p /tmp/bootA && mount -o noatime /dev/ufs/bootA /tmp/bootA
 # mkdir -p /tmp/bootA/boot/kernel
 # cp /tmp/toolRoot/boot/kernel/kernel /tmp/bootA/boot/kernel

*** create tgptbootdata file in bootA

 there is no key and data_path in the TPM NVRAM yet, we force tgptboot
 to use key and data_file from well known override locations

 # mkdir -p /tmp/bootA/boot/magic

 - existence of /boot/magic/key on UFS boot partition will make
   tgptboot to use the (all zero in this case) key to decrypt the data file
 # dd if=/dev/zero of=/tmp/bootA/boot/magic/key bs=32 count=1

 - existence of /boot/magic/data_force will make tgptboot to use the
   tgptboot data_file at /boot/magic/data
 # touch /tmp/bootA/boot/magic/data_force

 - create minimal tgptbootdata config file:
 # cat >> /tmp/magic_toolRoot.conf << EOF
 {provision,[
  {env,[{"vfs.root.mountfrom","ufs:/dev/ufs/toolRoot"}]},
  {load, [{"/tmp/bootA/boot/kernel/kernel", "/boot/kernel/kernel",""}]},
  {key, []}]}.
EOF

 - create the data_file
 # /usr/obj/10.1/usr/src/10.1/sbin/tgptbootdata/tgptbootdata \
   -f /tmp/magic_toolRoot.conf -k /tmp/bootA/boot/magic/key \
   -o /tmp/bootA/boot/magic/data

*** set bootme on bootA

 # gpart set -a bootme -i 2 da0

*** umount file systems, eject usb stic

 # umount /tmp/toolRoot
 # umount /tmp/bootA
 # camcontrol eject da0


** test the provisioning USB stick

 - the USB stick should boot the target system regardless of TPM configuration
   in the ThinkPad BIOS
 - actually the USB stick should boot anything amd64 system that supports
   BIOS legacy boot with GENERIC kernel and empty kernel environment

** setup target system TPM in BIOS

  - poweroff the ThinkPad X66
  - press power button while holding F1 (TPM physical presence indicator in
    Thinkpad BIOS, required to clear TPM) until the BIOS Setup Utility is shown
  - in > Security > Security Chip
    - set 'Security Chip' to 'Active'
    - clear security chip
  - in > Security > Security Chip > Security Reporting Options
    several Reporting Options can be set which will together with the choice
    of PCR set used to seal the KEY determine which changes to BIOS options and
    system configuration will require TPM reprovisioning. selection of
    the right PCR selection is out of scope, we will use only PCR9 in this
    tutorial.

** provision target system TPM

  - boot ThinkPad with the provisioning USB stick
  - log in as root

  - start tcsd
  # kldload tpm
  # service tcsd onestart

  - take tpm ownership
  # tpm_takeownership -y -z

  - define NVRAM area for the AES key, read selection PCR 9
  - 0x020002323 is the index tgptboot will use when looking for the KEY
  - the READ_STCLEAR permission will make this nvram index unreadable
    after size 0 read (which is done by tgptboot right before executing the
    kernel)
  # tpm_nvdefine -i 0x020002323 -s 32 -y -r 9 -p "OWNERWRITE|READ_STCLEAR"

  - create AES key and write NV RAM
  # tpm_nvwrite -i 0x020002323 -z -s 32 -d "012345678901234567890123456789012345"

  - define NV RAM area for the tgptboot data file path
  - 0x020004242 is the index tgptboot will use when looking for the data file
    path
  # tpm_nvdefine -i 0x020004242 -s 128 -y -p OWNERWRITE

  - fill the NV RAM area for the tgptboot data file path with zeros
  # tpm_nvwrite -i 0x020004242 -z -s 128 -m 0x00

  - write the path "/boot/x60"
  # tpm_nvwrite -i 0x020004242 -z -d "/boot/x60"


** install kernel, and one modules and tgptboot data file to /dev/gpt/bootB

  # mkdir -p /tmp/bootB
  # mount -o noatime /dev/ufs/bootB /tmp/bootB
  # mkdir -p /tmp/bootB/boot/kernel
  # cp /boot/kernel/kernel /tmp/bootB/boot/kernel/
  # cp /boot/kernel/tpm.ko /tmp/bootB/boot/kernel/

  - create tgptbootdata config file, kern + one module, no disk key, only
     vsf.root.mountfrom in env:
 # cat >> /tmp/tgptbootdata.conf << EOF
 {provision,[
  {env,[{"vfs.root.mountfrom","ufs:/dev/ufs/toolRoot"}]},
  {load, [{"/tmp/bootB/boot/kernel/kernel", "/boot/kernel/kernel",""},
          {"/tmp/bootB/boot/kernel/tmp.ko", "/boot/kernel/tpm.ko",undefined}]}
  {key, []}]}.
EOF

 - create the data_file
 # echo -n "012345678901234567890123456789012345" > /tmp/aes.key
 # tgptbootdata/tgptbootdata \
   -f /tmp/tgptbootdata.conf -k /tpm/aes.key \
   -o /tmp/bootB/boot/x60

 - set gptboot(8) attribute bootme to bootB remove it from bootA
 # gpart set -a bootme -i 3 da0
 # gpart unset -a bootme -i 2 da0

** reboot
  target system will now boot using key and path from TPM nvram


-------------------------------------------------------------------------------------------

--EeQfGwPcQSOJBaQU
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="0001-trusted-gptboot-preview.patch"

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

--EeQfGwPcQSOJBaQU--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20150325090348.GA17422>