Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177555053
D59141.1794707937.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
19 KB
Referenced Files
None
Subscribers
None
D59141.1794707937.diff
View Options
diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h
--- a/stand/common/bootstrap.h
+++ b/stand/common/bootstrap.h
@@ -349,6 +349,9 @@
ssize_t (*arch_copyout)(const vm_offset_t, void *, const size_t);
/* Read from file to module address space, same semantics as read() */
ssize_t (*arch_readin)(readin_handle_t, vm_offset_t, const size_t);
+ /* Measure an initialized portion of a preloaded file, if supported. */
+ void (*arch_file_measure)(struct preloaded_file *, vm_offset_t,
+ const size_t);
/* Perform ISA byte port I/O (only for systems with ISA) */
int (*arch_isainb)(int port);
void (*arch_isaoutb)(int port, int value);
diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c
--- a/stand/common/load_elf.c
+++ b/stand/common/load_elf.c
@@ -928,6 +928,18 @@
}
ret = lastaddr - firstaddr;
+ /*
+ * Measure initialized load segments, not the span between them. ELF
+ * images can contain gaps whose contents are not defined by the file.
+ */
+ if (archsw.arch_file_measure != NULL) {
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0)
+ continue;
+ archsw.arch_file_measure(fp, phdr[i].p_vaddr + off,
+ phdr[i].p_memsz);
+ }
+ }
out:
if (dp)
diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c
--- a/stand/common/load_elf_obj.c
+++ b/stand/common/load_elf_obj.c
@@ -367,6 +367,8 @@
ret = lastaddr - firstaddr;
fp->f_addr = firstaddr;
+ if (archsw.arch_file_measure != NULL)
+ archsw.arch_file_measure(fp, firstaddr, ret);
if (module_verbose > MODULE_VERBOSE_SILENT)
printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr);
diff --git a/stand/common/module.c b/stand/common/module.c
--- a/stand/common/module.c
+++ b/stand/common/module.c
@@ -789,6 +789,8 @@
TSEXIT();
return (NULL);
}
+ if (archsw.arch_file_measure != NULL)
+ archsw.arch_file_measure(fp, fp->f_addr, fp->f_size);
/* recognise space consumption */
loadaddr = laddr;
diff --git a/stand/efi/include/efilib.h b/stand/efi/include/efilib.h
--- a/stand/efi/include/efilib.h
+++ b/stand/efi/include/efilib.h
@@ -119,6 +119,10 @@
void efi_time_init(void);
void efi_time_fini(void);
+struct preloaded_file;
+void efi_tpm_measure_file(struct preloaded_file *, vm_offset_t, size_t);
+void efi_tpm_measure_boot(void);
+
int parse_uefi_con_out(void);
EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
diff --git a/stand/efi/libefi/Makefile b/stand/efi/libefi/Makefile
--- a/stand/efi/libefi/Makefile
+++ b/stand/efi/libefi/Makefile
@@ -22,6 +22,7 @@
errno.c \
handles.c \
libefi.c \
+ tpm.c \
wchar.c
.PATH: ${SYSDIR}/teken
@@ -55,6 +56,7 @@
CFLAGS.efi_console.c+= -I${SRCTOP}/sys/teken -I${SRCTOP}/contrib/pnglite
CFLAGS.efi_console.c+= -I${.CURDIR}/../loader
CFLAGS.teken.c+= -I${SRCTOP}/sys/teken
+CFLAGS.tpm.c+= -I${.CURDIR}/../loader
.if ${MK_LOADER_ZFS} != "no"
CFLAGS+= -I${SAZFSSRC}
CFLAGS+= -I${SYSDIR}/cddl/boot/zfs
diff --git a/stand/efi/libefi/tpm.c b/stand/efi/libefi/tpm.c
new file mode 100644
--- /dev/null
+++ b/stand/efi/libefi/tpm.c
@@ -0,0 +1,462 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 BBOX.io
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+#include <stand.h>
+#include <bootstrap.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <Protocol/Tcg2Protocol.h>
+#include <Protocol/TcgService.h>
+
+#include "loader_efi.h"
+
+#define FREEBSD_LOADER_CONFIG_PCR 8
+#define FREEBSD_LOADER_FILE_PCR 9
+#define EV_IPL ((TCG_EVENTTYPE)0x0000000d)
+
+static const char efi_tpm_environment_domain[] =
+ "FreeBSD loader environment v1";
+static const char efi_tpm_file_identity_domain[] =
+ "FreeBSD loader file identity v1";
+
+struct efi_tpm_file_measurement {
+ vm_offset_t addr;
+ size_t len;
+};
+
+enum efi_tpm_protocol {
+ EFI_TPM_UNKNOWN,
+ EFI_TPM_NONE,
+ EFI_TPM_1,
+ EFI_TPM_2,
+};
+
+static enum efi_tpm_protocol tpm_protocol;
+static EFI_TCG_PROTOCOL *tcg;
+static EFI_TCG2_PROTOCOL *tcg2;
+
+static void
+efi_tpm_disable(const char *description, EFI_STATUS status)
+{
+
+ printf("TPM measurement of %s failed: %lu\n", description,
+ DECODE_ERROR(status));
+ tpm_protocol = EFI_TPM_NONE;
+}
+
+static bool
+efi_tpm_sensitive_variable(const char *name, size_t len)
+{
+
+ /*
+ * The measured environment is recorded in the public firmware event
+ * log. Add future loader variables containing secrets here before
+ * storing them in the kernel environment.
+ */
+ return ((len == sizeof("password") - 1 &&
+ strncmp(name, "password", len) == 0) ||
+ (len == sizeof("bootlock_password") - 1 &&
+ strncmp(name, "bootlock_password", len) == 0) ||
+ (len == sizeof("kern.geom.eli.passphrase") - 1 &&
+ strncmp(name, "kern.geom.eli.passphrase", len) == 0));
+}
+
+static bool
+efi_tpm_measureable(const struct preloaded_file *fp)
+{
+
+ /*
+ * Entropy is deliberately different on every boot and key material must
+ * not acquire a public event-log digest. Neither is executable boot
+ * content suitable for an attestation baseline.
+ */
+ if (strcmp(fp->f_type, "boot_entropy_cache") == 0 ||
+ strcmp(fp->f_type, "boot_entropy_platform") == 0 ||
+ strcmp(fp->f_type, "TSLOG data") == 0 ||
+ strstr(fp->f_type, ":geli_keyfile") != NULL)
+ return (false);
+ return (true);
+}
+
+static bool
+efi_tpm2_available(void)
+{
+ static EFI_GUID tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
+ EFI_TCG2_BOOT_SERVICE_CAPABILITY cap;
+ EFI_STATUS status;
+
+ status = BS->LocateProtocol(&tcg2_guid, NULL, (void **)&tcg2);
+ if (EFI_ERROR(status))
+ return (false);
+
+ bzero(&cap, sizeof(cap));
+ cap.Size = sizeof(cap);
+ status = tcg2->GetCapability(tcg2, &cap);
+ return (!EFI_ERROR(status) && cap.TPMPresentFlag);
+}
+
+static bool
+efi_tpm1_available(void)
+{
+ static EFI_GUID tcg_guid = EFI_TCG_PROTOCOL_GUID;
+ TCG_EFI_BOOT_SERVICE_CAPABILITY cap;
+ EFI_PHYSICAL_ADDRESS log, last;
+ EFI_STATUS status;
+ UINT32 features;
+
+ status = BS->LocateProtocol(&tcg_guid, NULL, (void **)&tcg);
+ if (EFI_ERROR(status))
+ return (false);
+
+ bzero(&cap, sizeof(cap));
+ cap.Size = sizeof(cap);
+ status = tcg->StatusCheck(tcg, &cap, &features, &log, &last);
+ return (!EFI_ERROR(status) && cap.TPMPresentFlag &&
+ !cap.TPMDeactivatedFlag);
+}
+
+static enum efi_tpm_protocol
+efi_tpm_probe(void)
+{
+
+ if (tpm_protocol != EFI_TPM_UNKNOWN)
+ return (tpm_protocol);
+ if (efi_tpm2_available())
+ tpm_protocol = EFI_TPM_2;
+ else if (efi_tpm1_available())
+ tpm_protocol = EFI_TPM_1;
+ else
+ tpm_protocol = EFI_TPM_NONE;
+ return (tpm_protocol);
+}
+
+static EFI_STATUS
+efi_tpm2_measure(UINT32 pcr, const void *buf, size_t len,
+ const void *event_data, size_t event_len)
+{
+ EFI_TCG2_EVENT *event;
+ EFI_STATUS status;
+ size_t eventsz;
+
+ if (event_len > UINT32_MAX - offsetof(EFI_TCG2_EVENT, Event))
+ return (EFI_BAD_BUFFER_SIZE);
+ eventsz = offsetof(EFI_TCG2_EVENT, Event) + event_len;
+ event = malloc(eventsz);
+ if (event == NULL)
+ return (EFI_OUT_OF_RESOURCES);
+
+ event->Size = (UINT32)eventsz;
+ event->Header.HeaderSize = sizeof(event->Header);
+ event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION;
+ event->Header.PCRIndex = pcr;
+ event->Header.EventType = EV_IPL;
+ bcopy(event_data, event->Event, event_len);
+ status = tcg2->HashLogExtendEvent(tcg2, 0,
+ (EFI_PHYSICAL_ADDRESS)(uintptr_t)buf, len, event);
+ free(event);
+ return (status);
+}
+
+static EFI_STATUS
+efi_tpm1_measure(UINT32 pcr, const void *buf, size_t len,
+ const void *event_data, size_t event_len)
+{
+ TCG_PCR_EVENT *event;
+ EFI_PHYSICAL_ADDRESS last;
+ EFI_STATUS status;
+ size_t eventsz;
+ UINT32 event_number;
+
+ if (event_len > UINT32_MAX - offsetof(TCG_PCR_EVENT, Event))
+ return (EFI_BAD_BUFFER_SIZE);
+ eventsz = offsetof(TCG_PCR_EVENT, Event) + event_len;
+ event = calloc(1, eventsz);
+ if (event == NULL)
+ return (EFI_OUT_OF_RESOURCES);
+
+ event->PCRIndex = pcr;
+ event->EventType = EV_IPL;
+ event->EventSize = (UINT32)event_len;
+ bcopy(event_data, event->Event, event_len);
+ event_number = 0;
+ status = tcg->HashLogExtendEvent(tcg,
+ (EFI_PHYSICAL_ADDRESS)(uintptr_t)buf, len, TPM_ALG_SHA, event,
+ &event_number, &last);
+ free(event);
+ return (status);
+}
+
+static void
+efi_tpm_measure(UINT32 pcr, const void *buf, size_t len,
+ const void *event_data, size_t event_len, const char *description)
+{
+ EFI_STATUS status;
+
+ if (len == 0 || efi_tpm_probe() == EFI_TPM_NONE)
+ return;
+ if (tpm_protocol == EFI_TPM_2)
+ status = efi_tpm2_measure(pcr, buf, len, event_data,
+ event_len);
+ else
+ status = efi_tpm1_measure(pcr, buf, len, event_data,
+ event_len);
+ if (EFI_ERROR(status))
+ efi_tpm_disable(description, status);
+}
+
+static int
+efi_tpm_file_compare(const void *a, const void *b)
+{
+ const struct preloaded_file *fa, *fb;
+ int error;
+
+ fa = *(const struct preloaded_file * const *)a;
+ fb = *(const struct preloaded_file * const *)b;
+ error = strcmp(fa->f_name, fb->f_name);
+ if (error == 0)
+ error = strcmp(fa->f_type, fb->f_type);
+ return (error);
+}
+
+static int
+efi_tpm_measurement_compare(const void *a, const void *b)
+{
+ const struct efi_tpm_file_measurement *ma, *mb;
+
+ ma = a;
+ mb = b;
+ if (ma->addr < mb->addr)
+ return (-1);
+ if (ma->addr > mb->addr)
+ return (1);
+ return (0);
+}
+
+static struct preloaded_file **
+efi_tpm_sorted_files(size_t *countp)
+{
+ struct preloaded_file **files, *fp, *key;
+ size_t count, i, j;
+
+ count = 0;
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next)
+ count++;
+ files = malloc(count * sizeof(*files));
+ if (files == NULL) {
+ efi_tpm_disable("loaded-file list", EFI_OUT_OF_RESOURCES);
+ return (NULL);
+ }
+ i = 0;
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next)
+ files[i++] = fp;
+ for (i = 1; i < count; i++) {
+ key = files[i];
+ for (j = i; j > 0 &&
+ efi_tpm_file_compare(&files[j - 1], &key) > 0; j--)
+ files[j] = files[j - 1];
+ files[j] = key;
+ }
+ *countp = count;
+ return (files);
+}
+
+static void
+efi_tpm_measure_file_identity(struct preloaded_file *fp)
+{
+ char description[256];
+ char *identity, *cp;
+ size_t alen, len, nlen, tlen;
+
+ if (!efi_tpm_measureable(fp))
+ return;
+ tlen = strlen(fp->f_type);
+ nlen = strlen(fp->f_name);
+ alen = fp->f_args == NULL ? 0 : strlen(fp->f_args);
+ if (tlen > SIZE_MAX - sizeof(efi_tpm_file_identity_domain) - 3 ||
+ nlen > SIZE_MAX - sizeof(efi_tpm_file_identity_domain) - tlen - 3 ||
+ alen > SIZE_MAX - sizeof(efi_tpm_file_identity_domain) - tlen -
+ nlen - 3) {
+ efi_tpm_disable("loaded-file identity", EFI_BAD_BUFFER_SIZE);
+ return;
+ }
+ len = sizeof(efi_tpm_file_identity_domain) + tlen + nlen + alen + 3;
+ identity = malloc(len);
+ if (identity == NULL) {
+ efi_tpm_disable("loaded-file identity", EFI_OUT_OF_RESOURCES);
+ return;
+ }
+ cp = identity;
+ bcopy(efi_tpm_file_identity_domain, cp,
+ sizeof(efi_tpm_file_identity_domain));
+ cp += sizeof(efi_tpm_file_identity_domain);
+ bcopy(fp->f_type, cp, tlen + 1);
+ cp += tlen + 1;
+ bcopy(fp->f_name, cp, nlen + 1);
+ cp += nlen + 1;
+ if (fp->f_args != NULL)
+ bcopy(fp->f_args, cp, alen);
+ cp[alen] = '\0';
+ snprintf(description, sizeof(description), "FreeBSD loaded file: %s",
+ fp->f_name);
+ efi_tpm_measure(FREEBSD_LOADER_CONFIG_PCR, identity, len,
+ identity, len, description);
+ free(identity);
+}
+
+static void
+efi_tpm_measure_file_contents(struct preloaded_file *fp)
+{
+ struct efi_tpm_file_measurement *measurements;
+ struct efi_tpm_file_measurement key;
+ struct file_metadata *md;
+ char description[256];
+ size_t count, i, j;
+ void *buf;
+
+ count = 0;
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (md->md_type == MODINFOMD_TPM_MEASURE)
+ count++;
+ }
+ if (count == 0)
+ return;
+ measurements = malloc(count * sizeof(*measurements));
+ if (measurements == NULL) {
+ efi_tpm_disable(fp->f_name, EFI_OUT_OF_RESOURCES);
+ return;
+ }
+ i = 0;
+ for (md = fp->f_metadata; md != NULL; md = md->md_next) {
+ if (md->md_type != MODINFOMD_TPM_MEASURE)
+ continue;
+ bcopy(md->md_data, &measurements[i++],
+ sizeof(measurements[0]));
+ }
+ for (i = 1; i < count; i++) {
+ key = measurements[i];
+ for (j = i; j > 0 && efi_tpm_measurement_compare(
+ &measurements[j - 1], &key) > 0; j--)
+ measurements[j] = measurements[j - 1];
+ measurements[j] = key;
+ }
+ for (i = 0; i < count; i++) {
+ snprintf(description, sizeof(description),
+ "FreeBSD %s: %s+%#jx", fp->f_type, fp->f_name,
+ (uintmax_t)(measurements[i].addr - fp->f_addr));
+ buf = efi_translate(measurements[i].addr);
+ efi_tpm_measure(FREEBSD_LOADER_FILE_PCR, buf,
+ measurements[i].len, description,
+ strlen(description) + 1, description);
+ }
+ free(measurements);
+}
+
+void
+efi_tpm_measure_boot(void)
+{
+ struct env_var *ep;
+ struct preloaded_file **files;
+ struct preloaded_file *fp;
+ char *config, *cp;
+ size_t i, len, nfiles, nlen, vlen;
+
+ if (efi_tpm_probe() == EFI_TPM_NONE)
+ return;
+
+ /*
+ * Commit PCR 8 to the final environment passed to the kernel. The
+ * environment list is sorted, making this representation deterministic.
+ * Loader passwords and the GELI passphrase are omitted to avoid exposing a
+ * useful digest of a low-entropy secret.
+ */
+ len = sizeof(efi_tpm_environment_domain) + 1;
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ if ((ep->ev_flags & EV_NOKENV) != 0 ||
+ efi_tpm_sensitive_variable(ep->ev_name,
+ strlen(ep->ev_name)))
+ continue;
+ nlen = strlen(ep->ev_name);
+ vlen = ep->ev_value == NULL ? 0 : strlen(ep->ev_value);
+ if (nlen > SIZE_MAX - 2 || vlen > SIZE_MAX - nlen - 2 ||
+ len > SIZE_MAX - nlen - vlen - 2) {
+ efi_tpm_disable("kernel environment",
+ EFI_BAD_BUFFER_SIZE);
+ return;
+ }
+ len += nlen + vlen + 2;
+ }
+ config = malloc(len);
+ if (config == NULL) {
+ efi_tpm_disable("kernel environment", EFI_OUT_OF_RESOURCES);
+ return;
+ }
+ cp = config;
+ bcopy(efi_tpm_environment_domain, cp,
+ sizeof(efi_tpm_environment_domain));
+ cp += sizeof(efi_tpm_environment_domain);
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ if ((ep->ev_flags & EV_NOKENV) != 0 ||
+ efi_tpm_sensitive_variable(ep->ev_name,
+ strlen(ep->ev_name)))
+ continue;
+ nlen = strlen(ep->ev_name);
+ bcopy(ep->ev_name, cp, nlen);
+ cp += nlen;
+ *cp++ = '=';
+ if (ep->ev_value != NULL) {
+ vlen = strlen(ep->ev_value);
+ bcopy(ep->ev_value, cp, vlen);
+ cp += vlen;
+ }
+ *cp++ = '\0';
+ }
+ *cp++ = '\0';
+ efi_tpm_measure(FREEBSD_LOADER_CONFIG_PCR, config, cp - config,
+ config, cp - config, "kernel environment");
+ free(config);
+
+ /*
+ * The Lua module table has no defined traversal order. Sort the final
+ * files so both PCRs describe effective boot policy, not hash-table order.
+ */
+ files = efi_tpm_sorted_files(&nfiles);
+ if (files == NULL)
+ return;
+ for (i = 0; i < nfiles; i++)
+ efi_tpm_measure_file_identity(files[i]);
+ for (i = 0; i < nfiles; i++) {
+ fp = files[i];
+ efi_tpm_measure_file_contents(fp);
+ }
+ free(files);
+}
+
+void
+efi_tpm_measure_file(struct preloaded_file *fp, vm_offset_t addr, size_t len)
+{
+ struct efi_tpm_file_measurement measurement;
+ struct file_metadata *md;
+
+ if (len == 0 || !efi_tpm_measureable(fp) ||
+ efi_tpm_probe() == EFI_TPM_NONE)
+ return;
+ measurement.addr = addr;
+ measurement.len = len;
+ md = malloc(sizeof(*md) - sizeof(md->md_data) + sizeof(measurement));
+ if (md == NULL) {
+ efi_tpm_disable(fp->f_name, EFI_OUT_OF_RESOURCES);
+ return;
+ }
+ md->md_size = sizeof(measurement);
+ md->md_type = MODINFOMD_TPM_MEASURE;
+ bcopy(&measurement, md->md_data, sizeof(measurement));
+ md->md_next = fp->f_metadata;
+ fp->f_metadata = md;
+}
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -433,6 +433,9 @@
/* Copy our environment. */
envp = addr;
addr = md_copyenv(addr);
+#ifdef EFI
+ efi_tpm_measure_boot();
+#endif
/* Pad to a page boundary. */
addr = md_align(addr);
diff --git a/stand/efi/loader/loader_efi.h b/stand/efi/loader/loader_efi.h
--- a/stand/efi/loader/loader_efi.h
+++ b/stand/efi/loader/loader_efi.h
@@ -47,6 +47,8 @@
extern EFI_LOADED_IMAGE *boot_img;
+struct preloaded_file;
+
int efi_autoload(void);
int efi_copy_init(void);
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -85,6 +85,7 @@
.arch_getdev = efi_getdev,
.arch_copyin = efi_copyin,
.arch_copyout = efi_copyout,
+ .arch_file_measure = efi_tpm_measure_file,
#if defined(__amd64__) || defined(__i386__)
.arch_hypervisor = x86_hypervisor,
#endif
diff --git a/stand/man/loader.efi.8 b/stand/man/loader.efi.8
--- a/stand/man/loader.efi.8
+++ b/stand/man/loader.efi.8
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd June 14, 2026
+.Dd August 23, 2026
.Dt LOADER.EFI 8
.Os
.Sh NAME
@@ -65,6 +65,42 @@
process.
.Xr boot1.efi 8
is deprecated for new installations.
+.Ss Measured Boot
+When the firmware provides the EFI TCG or TCG2 protocol,
+.Nm
+measures the final kernel environment and the type, name, and arguments of
+each successfully loaded kernel, module, and raw boot input into TPM PCR 8.
+Loaded inputs are measured in a stable name order.
+Loader password variables and the GELI passphrase are deliberately excluded.
+The event data contains the measured environment or file-identity record,
+allowing an event-log consumer to inspect the values committed to PCR 8.
+The environment record begins with the NUL-terminated discriminator
+.Dq FreeBSD loader environment v1 ,
+followed by a sequence of NUL-terminated
+.Dq name=value
+strings followed by an additional NUL.
+File-identity records begin with the NUL-terminated discriminator
+.Dq FreeBSD loader file identity v1 ,
+followed by three consecutive NUL-terminated strings holding the file type,
+name, and arguments.
+Other environment values and loaded-file arguments are therefore visible in
+the firmware event log.
+.Nm
+measures the initialized contents of each successfully loaded kernel,
+kernel module, and raw boot input into TPM PCR 9.
+Each measurement is recorded as an
+.Dv EV_IPL
+event in the firmware event log.
+For ELF files, only loaded segments are measured;
+undefined gaps and debugging information are excluded.
+PCR 9 event data contains a descriptive label rather than a copy of the
+measured file contents.
+Cached and firmware-provided entropy, loader timing data, and GELI key files
+are deliberately not measured.
+.Pp
+Measurement is best effort.
+A failure is reported on the loader console and disables further measurements,
+but does not prevent booting.
.Ss Console Considerations
The UEFI firmware provides a generic console.
In
@@ -522,6 +558,7 @@
# umount /boot/efi
.Ed
.Sh SEE ALSO
+.Xr tpm 4 ,
.Xr loader 8 ,
.Xr uefi 8
.Sh BUGS
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -263,6 +263,7 @@
#define MODINFOMD_NOCOPY 0x8000 /* don't copy this metadata to the kernel */
#define MODINFOMD_DEPLIST (0x4001 | MODINFOMD_NOCOPY) /* depends on */
+#define MODINFOMD_TPM_MEASURE (0x4002 | MODINFOMD_NOCOPY) /* TPM input */
#ifdef _KERNEL
#define MD_FETCH(mdp, info, type) ({ \
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 15, 1:58 AM (9 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41733918
Default Alt Text
D59141.1794707937.diff (19 KB)
Attached To
Mode
D59141: loader.efi: Measure boot policy and loaded contents with a TPM
Attached
Detach File
Event Timeline