diff --git ./src/Kconfig ./src/Kconfig
index 49f8672..4ca811d 100644
--- ./src/Kconfig
+++ ./src/Kconfig
@@ -259,6 +259,21 @@ config BOOTSPLASH_FILE
 	  The path and filename of the file to use as graphical bootsplash
 	  screen. The file format has to be jpg.
 
+config MEASURED_BOOT
+	bool "Enable TPM measured boot"
+	default n
+	select TPM
+	depends on MAINBOARD_HAS_LPC_TPM
+	depends on !VBOOT
+	help
+	  Enable this option to measure the bootblock, romstage and
+	  CBFS files into TPM PCRs.  This does not verify these values
+	  (that is the job of something like vboot), but makes it possible
+	  for the payload to validate the boot path and allow something
+	  like Heads to attest to the user that the system is likely safe.
+
+	  You probably want to say N.
+
 endmenu
 
 menu "Mainboard"
diff --git ./src/include/program_loading.h ./src/include/program_loading.h
index 416e2e9..40486cd 100644
--- ./src/include/program_loading.h
+++ ./src/include/program_loading.h
@@ -24,6 +24,8 @@ enum {
 	/* Last segment of program. Can be used to take different actions for
 	 * cache maintenance of a program load. */
 	SEG_FINAL = 1 << 0,
+	/* Indicate that the program segment should not be measured */
+	SEG_NO_MEASURE = 1 << 1,
 };
 
 enum prog_type {
diff --git ./src/include/sha1.h ./src/include/sha1.h
new file mode 100644
index 0000000..e7e28e6
--- /dev/null
+++ ./src/include/sha1.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* SHA-1 functions */
+
+#ifndef _sha1_h_
+#define _sha1_h_
+
+#include <stdint.h>
+#include <commonlib/helpers.h>
+
+#define SHA1_DIGEST_SIZE 20
+#define SHA1_BLOCK_SIZE 64
+
+/* SHA-1 context */
+struct sha1_ctx {
+	uint32_t count;
+	uint32_t state[5];
+	union {
+		uint8_t b[SHA1_BLOCK_SIZE];
+		uint32_t w[DIV_ROUND_UP(SHA1_BLOCK_SIZE, sizeof(uint32_t))];
+	} buf;
+};
+
+void sha1_init(struct sha1_ctx *ctx);
+void sha1_update(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len);
+uint8_t *sha1_final(struct sha1_ctx *ctx);
+
+#endif  /* _sha1_h_ */
diff --git ./src/include/tpm_lite/tlcl.h ./src/include/tpm_lite/tlcl.h
index 8dd5d80..15fbebf 100644
--- ./src/include/tpm_lite/tlcl.h
+++ ./src/include/tpm_lite/tlcl.h
@@ -147,6 +147,11 @@ uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest,
 		     uint8_t *out_digest);
 
 /**
+ * Perform a SHA1 hash on a region and extend a PCR with the hash.
+ */
+uint32_t tlcl_measure(int pcr_num, const void * start, size_t len);
+
+/**
  * Get the entire set of permanent flags.
  */
 uint32_t tlcl_get_permanent_flags(TPM_PERMANENT_FLAGS *pflags);
diff --git ./src/lib/Makefile.inc ./src/lib/Makefile.inc
index 8f92b29..1822daf 100644
--- ./src/lib/Makefile.inc
+++ ./src/lib/Makefile.inc
@@ -54,8 +54,13 @@ verstage-$(CONFIG_TPM) += tlcl.c
 verstage-$(CONFIG_TPM2) += tpm2_marshaling.c
 verstage-$(CONFIG_TPM2) += tpm2_tlcl.c
 
-ifeq ($(CONFIG_VBOOT_SEPARATE_VERSTAGE),y)
+# Add the TPM support into the ROM stage for measuring the bootblock
 romstage-$(CONFIG_TPM) += tlcl.c
+romstage-$(CONFIG_TPM) += sha1.c
+ramstage-$(CONFIG_TPM) += tlcl.c
+ramstage-$(CONFIG_TPM) += sha1.c
+
+ifeq ($(CONFIG_VBOOT_SEPARATE_VERSTAGE),y)
 romstage-$(CONFIG_TPM2) += tpm2_marshaling.c
 romstage-$(CONFIG_TPM2) += tpm2_tlcl.c
 endif # CONFIG_VBOOT_SEPARATE_VERSTAGE
diff --git ./src/lib/cbfs.c ./src/lib/cbfs.c
index 11bce2c..8428245 100644
--- ./src/lib/cbfs.c
+++ ./src/lib/cbfs.c
@@ -69,7 +69,11 @@ void *cbfs_boot_map_with_leak(const char *name, uint32_t type, size_t *size)
 	if (size != NULL)
 		*size = fsize;
 
-	return rdev_mmap(&fh.data, 0, fsize);
+	void * buffer = rdev_mmap(&fh.data, 0, fsize);
+
+	prog_segment_loaded((uintptr_t)buffer, fsize, 0);
+
+	return buffer;
 }
 
 int cbfs_locate_file_in_region(struct cbfsf *fh, const char *region_name,
@@ -97,7 +101,8 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
 			return 0;
 		if (rdev_readat(rdev, buffer, offset, in_size) != in_size)
 			return 0;
-		return in_size;
+		out_size = in_size;
+		break;
 
 	case CBFS_COMPRESS_LZ4:
 		if ((ENV_BOOTBLOCK || ENV_VERSTAGE) &&
@@ -115,7 +120,7 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
 		timestamp_add_now(TS_START_ULZ4F);
 		out_size = ulz4fn(compr_start, in_size, buffer, buffer_size);
 		timestamp_add_now(TS_END_ULZ4F);
-		return out_size;
+		break;
 
 	case CBFS_COMPRESS_LZMA:
 		if (ENV_BOOTBLOCK || ENV_VERSTAGE)
@@ -134,11 +139,15 @@ size_t cbfs_load_and_decompress(const struct region_device *rdev, size_t offset,
 
 		rdev_munmap(rdev, map);
 
-		return out_size;
+		break;
 
 	default:
 		return 0;
 	}
+
+	prog_segment_loaded((uintptr_t)buffer, out_size, 0);
+
+	return out_size;
 }
 
 static inline int tohex4(unsigned int c)
diff --git ./src/lib/hardwaremain.c ./src/lib/hardwaremain.c
index a56d68e..8c13f5f 100644
--- ./src/lib/hardwaremain.c
+++ ./src/lib/hardwaremain.c
@@ -32,6 +32,7 @@
 #include <reset.h>
 #include <boot/tables.h>
 #include <program_loading.h>
+#include <tpm_lite/tlcl.h>
 #include <lib.h>
 #if CONFIG_HAVE_ACPI_RESUME
 #include <arch/acpi.h>
@@ -544,3 +545,13 @@ void boot_state_current_unblock(void)
 {
 	boot_state_unblock(current_phase.state_id, current_phase.seq);
 }
+
+// ramstage measurements go into PCR3 if we are doing measured boot
+void platform_segment_loaded(uintptr_t start, size_t size, int flags)
+{
+	if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !(flags & SEG_NO_MEASURE))
+	{
+		tlcl_measure(3, (const void*) start, size);
+	}
+}
+
diff --git ./src/lib/rmodule.c ./src/lib/rmodule.c
index a3a74ac..bb99e5f 100644
--- ./src/lib/rmodule.c
+++ ./src/lib/rmodule.c
@@ -198,7 +198,7 @@ int rmodule_load(void *base, struct rmodule *module)
 	rmodule_clear_bss(module);
 
 	prog_segment_loaded((uintptr_t)module->location,
-				rmodule_memory_size(module), SEG_FINAL);
+				rmodule_memory_size(module), SEG_FINAL | SEG_NO_MEASURE);
 
 	return 0;
 }
diff --git ./src/lib/sha1.c ./src/lib/sha1.c
new file mode 100644
index 0000000..506907f
--- /dev/null
+++ ./src/lib/sha1.c
@@ -0,0 +1,175 @@
+/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * SHA-1 implementation largely based on libmincrypt in the the Android
+ * Open Source Project (platorm/system/core.git/libmincrypt/sha.c
+ */
+
+#include "sha1.h"
+#include <string.h>
+
+static uint32_t ror27(uint32_t val)
+{
+	return (val >> 27) | (val << 5);
+}
+static uint32_t ror2(uint32_t val)
+{
+	return (val >> 2) | (val << 30);
+}
+static uint32_t ror31(uint32_t val)
+{
+	return (val >> 31) | (val << 1);
+}
+
+static void sha1_transform(struct sha1_ctx *ctx)
+{
+	uint32_t W[80];
+	register uint32_t A, B, C, D, E;
+	int t;
+
+	A = ctx->state[0];
+	B = ctx->state[1];
+	C = ctx->state[2];
+	D = ctx->state[3];
+	E = ctx->state[4];
+
+#define SHA_F1(A, B, C, D, E, t)					\
+	E += ror27(A) +							\
+			(W[t] = __builtin_bswap32(ctx->buf.w[t])) +	\
+			(D^(B&(C^D))) + 0x5A827999;			\
+	B = ror2(B);
+
+	for (t = 0; t < 15; t += 5) {
+		SHA_F1(A, B, C, D, E, t + 0);
+		SHA_F1(E, A, B, C, D, t + 1);
+		SHA_F1(D, E, A, B, C, t + 2);
+		SHA_F1(C, D, E, A, B, t + 3);
+		SHA_F1(B, C, D, E, A, t + 4);
+	}
+	SHA_F1(A, B, C, D, E, t + 0);  /* 16th one, t == 15 */
+
+#undef SHA_F1
+
+#define SHA_F1(A, B, C, D, E, t)					      \
+	E += ror27(A) +							      \
+			(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
+			(D^(B&(C^D))) + 0x5A827999;			      \
+	B = ror2(B);
+
+	SHA_F1(E, A, B, C, D, t + 1);
+	SHA_F1(D, E, A, B, C, t + 2);
+	SHA_F1(C, D, E, A, B, t + 3);
+	SHA_F1(B, C, D, E, A, t + 4);
+
+#undef SHA_F1
+
+#define SHA_F2(A, B, C, D, E, t)					      \
+	E += ror27(A) +							      \
+			(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
+			(B^C^D) + 0x6ED9EBA1;				      \
+	B = ror2(B);
+
+	for (t = 20; t < 40; t += 5) {
+		SHA_F2(A, B, C, D, E, t + 0);
+		SHA_F2(E, A, B, C, D, t + 1);
+		SHA_F2(D, E, A, B, C, t + 2);
+		SHA_F2(C, D, E, A, B, t + 3);
+		SHA_F2(B, C, D, E, A, t + 4);
+	}
+
+#undef SHA_F2
+
+#define SHA_F3(A, B, C, D, E, t)					      \
+	E += ror27(A) +							      \
+			(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
+			((B&C)|(D&(B|C))) + 0x8F1BBCDC;			      \
+	B = ror2(B);
+
+	for (; t < 60; t += 5) {
+		SHA_F3(A, B, C, D, E, t + 0);
+		SHA_F3(E, A, B, C, D, t + 1);
+		SHA_F3(D, E, A, B, C, t + 2);
+		SHA_F3(C, D, E, A, B, t + 3);
+		SHA_F3(B, C, D, E, A, t + 4);
+	}
+
+#undef SHA_F3
+
+#define SHA_F4(A, B, C, D, E, t)					      \
+	E += ror27(A) +							      \
+			(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
+			(B^C^D) + 0xCA62C1D6;				      \
+	B = ror2(B);
+
+	for (; t < 80; t += 5) {
+		SHA_F4(A, B, C, D, E, t + 0);
+		SHA_F4(E, A, B, C, D, t + 1);
+		SHA_F4(D, E, A, B, C, t + 2);
+		SHA_F4(C, D, E, A, B, t + 3);
+		SHA_F4(B, C, D, E, A, t + 4);
+	}
+
+#undef SHA_F4
+
+	ctx->state[0] += A;
+	ctx->state[1] += B;
+	ctx->state[2] += C;
+	ctx->state[3] += D;
+	ctx->state[4] += E;
+}
+
+void sha1_update(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len)
+{
+	int i = ctx->count % sizeof(ctx->buf);
+	const uint8_t *p = (const uint8_t *)data;
+
+	ctx->count += len;
+
+	while (len > sizeof(ctx->buf) - i) {
+		memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i);
+		len -= sizeof(ctx->buf) - i;
+		p += sizeof(ctx->buf) - i;
+		sha1_transform(ctx);
+		i = 0;
+	}
+
+	while (len--) {
+		ctx->buf.b[i++] = *p++;
+		if (i == sizeof(ctx->buf)) {
+			sha1_transform(ctx);
+			i = 0;
+		}
+	}
+}
+
+
+uint8_t *sha1_final(struct sha1_ctx *ctx)
+{
+	uint32_t cnt = ctx->count * 8;
+	int i;
+
+	sha1_update(ctx, (uint8_t *)"\x80", 1);
+	while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8))
+		sha1_update(ctx, (uint8_t *)"\0", 1);
+
+	for (i = 0; i < 8; ++i) {
+		uint8_t tmp = cnt >> ((7 - i) * 8);
+		sha1_update(ctx, &tmp, 1);
+	}
+
+	for (i = 0; i < 5; i++)
+		ctx->buf.w[i] = __builtin_bswap32(ctx->state[i]);
+
+	return ctx->buf.b;
+}
+
+void sha1_init(struct sha1_ctx *ctx)
+{
+	ctx->state[0] = 0x67452301;
+	ctx->state[1] = 0xEFCDAB89;
+	ctx->state[2] = 0x98BADCFE;
+	ctx->state[3] = 0x10325476;
+	ctx->state[4] = 0xC3D2E1F0;
+	ctx->count = 0;
+}
diff --git ./src/lib/tlcl.c ./src/lib/tlcl.c
index 49854cb..32eb128 100644
--- ./src/lib/tlcl.c
+++ ./src/lib/tlcl.c
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <tpm_lite/tlcl.h>
 #include <tpm.h>
+#include <sha1.h>
 #include <vb2_api.h>
 #include "tlcl_internal.h"
 #include "tlcl_structures.h"
@@ -351,3 +352,23 @@ uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest,
 		       kPcrDigestLength);
 	return result;
 }
+
+
+uint32_t tlcl_measure(int pcr_num, const void * start, size_t len)
+{
+	VBDEBUG("TPM: pcr %d measure %p @ %zu: ", pcr_num, start, len);
+
+	struct sha1_ctx sha;
+	sha1_init(&sha);
+	sha1_update(&sha, start, len);
+
+	const uint8_t * hash = sha1_final(&sha);
+	for(unsigned i = 0 ; i < SHA1_DIGEST_SIZE ; i++)
+		VBDEBUG("%02x", hash[i]);
+	VBDEBUG("\n");
+
+	//hexdump(start, 128);
+
+	return tlcl_extend(pcr_num, hash, NULL);
+}
+
diff --git ./src/northbridge/intel/sandybridge/romstage.c ./src/northbridge/intel/sandybridge/romstage.c
index 738e285..c7c0c62 100644
--- ./src/northbridge/intel/sandybridge/romstage.c
+++ ./src/northbridge/intel/sandybridge/romstage.c
@@ -29,6 +29,8 @@
 #include <device/device.h>
 #include <halt.h>
 #include <tpm.h>
+#include <tpm_lite/tlcl.h>
+#include <program_loading.h>
 #include <northbridge/intel/sandybridge/chip.h>
 #include "southbridge/intel/bd82x6x/pch.h"
 #include <southbridge/intel/common/gpio.h>
@@ -72,6 +74,18 @@ void mainboard_romstage_entry(unsigned long bist)
 	/* Initialize superio */
 	mainboard_config_superio();
 
+	if (IS_ENABLED(CONFIG_MEASURED_BOOT) && IS_ENABLED(CONFIG_LPC_TPM)) {
+		// we don't know if we are coming out of a resume
+		// at this point, but want to setup the tpm ASAP
+		init_tpm(0);
+		const void * const bootblock = (const void*) 0xFFFFF800;
+		const unsigned bootblock_size = 0x800;
+		tlcl_measure(0, bootblock, bootblock_size);
+
+		extern char _romstage, _eromstage;
+		tlcl_measure(1, &_romstage, &_eromstage - &_romstage);
+	}
+
 	/* USB is initialized in MRC if MRC is used.  */
 	if (CONFIG_USE_NATIVE_RAMINIT) {
 		early_usb_init(mainboard_usb_ports);
@@ -116,9 +130,23 @@ void mainboard_romstage_entry(unsigned long bist)
 
 	northbridge_romstage_finalize(s3resume);
 
-	if (IS_ENABLED(CONFIG_LPC_TPM)) {
+	// the normal TPM init happens here, if we haven't already
+	// set it up as part of the measured boot.
+	if (!IS_ENABLED(CONFIG_MEASURED_BOOT) && IS_ENABLED(CONFIG_LPC_TPM)) {
 		init_tpm(s3resume);
 	}
 
+	printk(BIOS_DEBUG, "%s: romstage complete\n", __FILE__);
+
 	post_code(0x3f);
 }
+
+
+void platform_segment_loaded(uintptr_t start, size_t size, int flags)
+{
+	if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !(flags & SEG_NO_MEASURE))
+	{
+		tlcl_measure(2, (const void*) start, size);
+	}
+}
+
diff --git ./util/cbfstool/lz4/lib/lz4frame.c ./util/cbfstool/lz4/lib/lz4frame.c
index e5458bb..58fb68f 100644
--- ./util/cbfstool/lz4/lib/lz4frame.c
+++ ./util/cbfstool/lz4/lib/lz4frame.c
@@ -1091,7 +1091,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext,
                 dctxPtr->tmpInTarget = minFHSize;   /* minimum to attempt decode */
                 dctxPtr->dStage = dstage_storeHeader;
             }
-
+            /* Falls through. */
         case dstage_storeHeader:
             {
                 size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;