diff --git a/docs/grub.texi b/docs/grub.texi index 096a3cde0..99552af66 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6775,10 +6775,10 @@ grub-mkimage -O x86_64-efi -o grubx64.efi -p '(tftp)/grub' --sbat sbat.csv efine @node Measured Boot @section Measuring boot components -If the tpm module is loaded and the platform has a Trusted Platform Module -installed, GRUB will log each command executed and each file loaded into the -TPM event log and extend the PCR values in the TPM correspondingly. All events -will be logged into the PCR described below with a type of EV_IPL and an +If the tpm_verifier module is loaded and the platform has a Trusted Platform +Module installed, GRUB will log each command executed and each file loaded into +the TPM event log and extend the PCR values in the TPM correspondingly. All +events will be logged into the PCR described below with a type of EV_IPL and an event description as described below. @multitable @columnfractions 0.3 0.1 0.6 @@ -6803,9 +6803,10 @@ corresponding to the filename. GRUB will not measure its own @file{core.img} - it is expected that firmware will carry this out. GRUB will also not perform any measurements until the -tpm module is loaded. As such it is recommended that the tpm module be built -into @file{core.img} in order to avoid a potential gap in measurement between -@file{core.img} being loaded and the tpm module being loaded. +tpm_verifier module is loaded. As such it is recommended that the tpm_verifier +module be built into @file{core.img} in order to avoid a potential gap in +measurement between @file{core.img} being loaded and the tpm_verifier module +being loaded. Measured boot is currently only supported on EFI and IBM IEEE1275 PowerPC platforms. diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index aa172391f..fae4d8819 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -105,6 +105,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h endif if COND_i386_xen_pvh @@ -124,6 +126,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h @@ -186,6 +190,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 6ff483534..5fdd922a7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1912,6 +1912,16 @@ module = { common = lib/cmdline.c; }; +module = { + name = slaunch; + x86 = loader/i386/slaunch.c; + x86 = loader/i386/txt/txt.c; + x86 = loader/i386/txt/acmod.c; + x86 = loader/i386/txt/verify.c; + x86 = loader/i386/skinit.c; + enable = x86; +}; + module = { name = fdt; efi = loader/efi/fdt.c; @@ -2621,7 +2631,13 @@ module = { module = { name = tpm; - common = commands/tpm.c; + x86 = commands/i386/tpm.c; + enable = x86; +}; + +module = { + name = tpm_verifier; + common = commands/tpm_verifier.c; efi = commands/efi/tpm.c; enable = efi; }; diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c index 46c4346da..2e42f6197 100644 --- a/grub-core/commands/i386/rdmsr.c +++ b/grub-core/commands/i386/rdmsr.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include GRUB_MOD_LICENSE("GPLv3+"); @@ -42,27 +42,16 @@ static const struct grub_arg_option options[] = static grub_err_t grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv) { - grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr; + grub_err_t err; + grub_uint32_t addr; grub_uint64_t value; const char *ptr; char buf[sizeof("1122334455667788")]; - /* - * The CPUID instruction should be used to determine whether MSRs - * are supported. (CPUID.01H:EDX[5] = 1) - */ - if (! grub_cpu_is_cpuid_supported ()) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + err = grub_cpu_is_msr_supported (); - grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); - - if (max_cpuid < 1) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); - - grub_cpuid (1, a, b, c, features); - - if (!(features & (1 << 5))) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("RDMSR is unsupported")); if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); @@ -76,7 +65,7 @@ grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv) if (*ptr != '\0') return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); - value = grub_msr_read (addr); + value = grub_rdmsr (addr); if (ctxt->state[0].set) { diff --git a/grub-core/commands/i386/tpm.c b/grub-core/commands/i386/tpm.c new file mode 100644 index 000000000..598c88680 --- /dev/null +++ b/grub-core/commands/i386/tpm.c @@ -0,0 +1,151 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * TPM TIS and CRB driver. + * + * Note: It is suggested to not use this driver together with UEFI TPM driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + * Code based on TCG PC Client Platform TPM Profile Specification for TPM 2.0, + * Version 1.05 Revision 14 released September 4, 2020. + */ + +#define TPM_MMIO_BASE 0xfed40000 + +/* 6.3.2 Register Space Addresses */ +/* TIS registers. */ +#define TPM_ACCESS 0x0000 +#define TPM_INTF_CAPABILITY 0x0014 +#define INTF_CAP_INTERFACE_VERSION_SHIFT 28 +#define INTF_CAP_INTERFACE_VERSION_MASK 7 +#define TPM_INTERFACE_ID 0x0030 +#define INTERFACE_ID_INTERFACE_TYPE_SHIFT 0 +#define INTERFACE_ID_INTERFACE_TYPE_MASK 0xF + +/* CRB registers. */ +#define TPM_LOC_CTRL 0x0008 + + +#define TIS_RELINQUISH_LCL 0x20 +#define CRB_RELINQUISH_LCL 0x0002 + +/* 6.4.2 Interface Identifier Register */ +#define TPM_CRB_INTF_ACTIVE 0x1 + +/* 6.5.2.7 Interface Capability */ +#define TPM_12_TIS_INTF_12 0x0 +#define TPM_12_TIS_INTF_13 0x2 +#define TPM_20_TIS_INTF_13 0x3 + +typedef enum + { + TPM_INTF_NONE = 0, + TPM_INTF_TIS, + TPM_INTF_CRB + } +tpm_intf_t; + +static grub_tpm_ver_t tpm_ver = GRUB_TPM_NONE; +static tpm_intf_t tpm_intf = TPM_INTF_NONE; + +grub_tpm_ver_t +grub_get_tpm_ver (void) +{ + return tpm_ver; +} + +/* Localities 0-4 are supported only. */ +void +grub_tpm_relinquish_locality (grub_uint8_t lcl) +{ + grub_addr_t addr = TPM_MMIO_BASE + lcl * GRUB_PAGE_SIZE; + + if (tpm_intf == TPM_INTF_TIS) + grub_write8 (TIS_RELINQUISH_LCL, addr + TPM_ACCESS); + else if (tpm_intf == TPM_INTF_CRB) + grub_write32 (CRB_RELINQUISH_LCL, addr + TPM_LOC_CTRL); +} + +static grub_err_t +grub_cmd_tpm_type (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + const char *tpm_ver_s = "NONE"; + const char *tpm_intf_s = "NONE"; + + if (tpm_ver == GRUB_TPM_12) + tpm_ver_s = "1.2"; + else if (tpm_ver == GRUB_TPM_20) + tpm_ver_s = "2.0"; + + if (tpm_intf == TPM_INTF_TIS) + tpm_intf_s = "TIS"; + else if (tpm_intf == TPM_INTF_CRB) + tpm_intf_s = "CRB"; + + grub_printf ("TPM family: %s\nTPM interface: %s\n", tpm_ver_s, tpm_intf_s); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_tpm_type; + +GRUB_MOD_INIT (tpm) +{ + grub_uint32_t intf_id; + grub_uint32_t intf_cap; + + cmd_tpm_type = grub_register_command ("tpm_type", grub_cmd_tpm_type, + NULL, N_("Show TPM version and interface type.")); + + tpm_ver = GRUB_TPM_20; + + intf_id = grub_read32 (TPM_MMIO_BASE + TPM_INTERFACE_ID); + intf_id >>= INTERFACE_ID_INTERFACE_TYPE_SHIFT; + intf_id &= INTERFACE_ID_INTERFACE_TYPE_MASK; + + tpm_intf = (intf_id == TPM_CRB_INTF_ACTIVE) ? TPM_INTF_CRB : TPM_INTF_TIS; + + /* CRB exists only in TPM 2.0 */ + if (tpm_intf == TPM_INTF_CRB) + return; + + intf_cap = grub_read32 (TPM_MMIO_BASE + TPM_INTF_CAPABILITY); + intf_cap >>= INTF_CAP_INTERFACE_VERSION_SHIFT; + intf_cap &= INTF_CAP_INTERFACE_VERSION_MASK; + + if (intf_cap == TPM_12_TIS_INTF_12 || intf_cap == TPM_12_TIS_INTF_13) + tpm_ver = GRUB_TPM_12; +} + +GRUB_MOD_FINI (tpm) +{ + grub_unregister_command (cmd_tpm_type); +} diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c index 1b143b888..7fbedaed9 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include GRUB_MOD_LICENSE("GPLv3+"); @@ -36,26 +36,15 @@ static grub_command_t cmd_write; static grub_err_t grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char **argv) { - grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr; + grub_err_t err; + grub_uint32_t addr; grub_uint64_t value; const char *ptr; - /* - * The CPUID instruction should be used to determine whether MSRs - * are supported. (CPUID.01H:EDX[5] = 1) - */ - if (!grub_cpu_is_cpuid_supported ()) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + err = grub_cpu_is_msr_supported (); - grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); - - if (max_cpuid < 1) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); - - grub_cpuid (1, a, b, c, features); - - if (!(features & (1 << 5))) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("WRMSR is unsupported")); if (argc != 2) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); @@ -77,7 +66,7 @@ grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char if (*ptr != '\0') return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); - grub_msr_write (addr, value); + grub_wrmsr (addr, value); return GRUB_ERR_NONE; } diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm_verifier.c similarity index 97% rename from grub-core/commands/tpm.c rename to grub-core/commands/tpm_verifier.c index 324423ef8..51419518e 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm_verifier.c @@ -88,13 +88,13 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type) } struct grub_file_verifier grub_tpm_verifier = { - .name = "tpm", + .name = "tpm_verifier", .init = grub_tpm_verify_init, .write = grub_tpm_verify_write, .verify_string = grub_tpm_verify_string, }; -GRUB_MOD_INIT (tpm) +GRUB_MOD_INIT (tpm_verifier) { /* * Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(), @@ -107,7 +107,7 @@ GRUB_MOD_INIT (tpm) grub_verifier_register (&grub_tpm_verifier); } -GRUB_MOD_FINI (tpm) +GRUB_MOD_FINI (tpm_verifier) { if (!grub_tpm_present()) return; diff --git a/grub-core/lib/i386/relocator32.S b/grub-core/lib/i386/relocator32.S index 09ce56ad0..2bdc07018 100644 --- a/grub-core/lib/i386/relocator32.S +++ b/grub-core/lib/i386/relocator32.S @@ -24,6 +24,8 @@ #include "relocator_common.S" +#include + .p2align 4 /* force 16-byte alignment */ VARIABLE(grub_relocator32_start) @@ -110,11 +112,23 @@ VARIABLE(grub_relocator32_edx) payload and makes this implementation easier. */ cld + cmpl $SLP_INTEL_TXT, %edi + je LOCAL(intel_txt) + + cmpl $SLP_AMD_SKINIT, %edi + je LOCAL(amd_skinit) + .byte 0xea VARIABLE(grub_relocator32_eip) .long 0 .word CODE_SEGMENT +LOCAL(intel_txt): + getsec + +LOCAL(amd_skinit): + skinit + /* GDT. Copied from loader/i386/linux.c. */ .p2align 4 LOCAL(gdt): diff --git a/grub-core/lib/i386/xen/relocator.S b/grub-core/lib/i386/xen/relocator.S index 96e51b59a..dab4d8ace 100644 --- a/grub-core/lib/i386/xen/relocator.S +++ b/grub-core/lib/i386/xen/relocator.S @@ -75,10 +75,10 @@ VARIABLE(grub_relocator_xen_mfn_list) .long 0 movl 0(%eax, %ebp, 4), %ecx /* mfn */ movl %ebp, %ebx - shll $PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ + shll $GRUB_PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ movl %ecx, %edx - shll $PAGE_SHIFT, %ecx /* prepare pte low part */ - shrl $(32 - PAGE_SHIFT), %edx /* pte high part */ + shll $GRUB_PAGE_SHIFT, %ecx /* prepare pte low part */ + shrl $(32 - GRUB_PAGE_SHIFT), %edx /* pte high part */ orl $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %ecx /* pte low */ movl $UVMF_INVLPG, %esi movl $__HYPERVISOR_update_va_mapping, %eax diff --git a/grub-core/lib/x86_64/xen/relocator.S b/grub-core/lib/x86_64/xen/relocator.S index f5364ed0f..852cd40aa 100644 --- a/grub-core/lib/x86_64/xen/relocator.S +++ b/grub-core/lib/x86_64/xen/relocator.S @@ -60,9 +60,9 @@ LOCAL(cont): jz 3f 2: movq %r12, %rdi - shlq $PAGE_SHIFT, %rdi /* virtual address (1:1 mapping) */ + shlq $GRUB_PAGE_SHIFT, %rdi /* virtual address (1:1 mapping) */ movq (%rbx, %r12, 8), %rsi /* mfn */ - shlq $PAGE_SHIFT, %rsi + shlq $GRUB_PAGE_SHIFT, %rsi orq $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %rsi /* Build pte */ movq $UVMF_INVLPG, %rdx movq %rcx, %r9 /* %rcx clobbered by hypercall */ diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c index 8075ac9b0..a8f558587 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -793,6 +794,7 @@ grub_freebsd_boot (void) #endif grub_memcpy (&stack[9], &bi, sizeof (bi)); + state.edi = SLP_NONE; state.eip = entry; state.esp = stack_target; state.ebp = stack_target; @@ -908,6 +910,7 @@ grub_openbsd_boot (void) return err; #endif + state.edi = SLP_NONE; state.eip = entry; state.ebp = state.esp = ((grub_uint8_t *) stack - (grub_uint8_t *) buf0) + buf_target; @@ -1230,6 +1233,7 @@ grub_netbsd_boot (void) return err; #endif + state.edi = SLP_NONE; state.eip = entry; state.esp = stack_target; state.ebp = stack_target; diff --git a/grub-core/loader/i386/coreboot/chainloader.c b/grub-core/loader/i386/coreboot/chainloader.c index 4a5179806..215356a0a 100644 --- a/grub-core/loader/i386/coreboot/chainloader.c +++ b/grub-core/loader/i386/coreboot/chainloader.c @@ -33,6 +33,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -47,6 +48,7 @@ grub_chain_boot (void) grub_video_set_mode ("text", 0, 0); state.eip = entry; + state.edi = SLP_NONE; return grub_relocator32_boot (relocator, state, 0); } diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 90121e9bc..0fdb2d2e7 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -34,10 +34,14 @@ #include #include #include +#include +#include +#include #include #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -64,18 +68,36 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define ACCEPTS_PURE_TEXT 1 #endif +/* See kernel_info in Documentation/arch/x86/boot.rst in the kernel tree */ +#define KERNEL_INFO_HEADER "LToP" +#define KERNEL_INFO_MIN_SIZE_TOTAL 12 + +struct linux_params_efi_info +{ + grub_uint32_t efi_signature; + grub_uint32_t efi_system_table; + grub_uint32_t efi_mem_desc_size; + grub_uint32_t efi_mem_desc_version; + grub_uint32_t efi_mmap; + grub_uint32_t efi_mmap_size; + grub_uint32_t efi_system_table_hi; + grub_uint32_t efi_mmap_hi; +}; + static grub_dl_t my_mod; static grub_size_t linux_mem_size; static int loaded; static void *prot_mode_mem; static grub_addr_t prot_mode_target; +static grub_size_t prot_file_size; static void *initrd_mem; static grub_addr_t initrd_mem_target; static grub_size_t prot_init_space; static struct grub_relocator *relocator = NULL; static void *efi_mmap_buf; static grub_size_t maximal_cmdline_size; +static struct linux_kernel_info *linux_info; static struct linux_kernel_params linux_params; static char *linux_cmdline; #ifdef GRUB_MACHINE_EFI @@ -99,6 +121,8 @@ static struct idt_descriptor idt_desc = }; #endif +#define OFFSET_OF(x, y) ((grub_size_t) ((grub_uint8_t *) (&(y)->x) - (grub_uint8_t *) (y))) + static inline grub_size_t page_align (grub_size_t size) { @@ -151,11 +175,38 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, grub_uint64_t preferred_address) { grub_err_t err; + grub_size_t total_size; + struct grub_slaunch_params *slparams = grub_slaunch_params(); if (prot_size == 0) prot_size = 1; - prot_size = page_align (prot_size); + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + if (prot_size > GRUB_TXT_MLE_MAX_SIZE) + { + err = GRUB_ERR_OUT_OF_RANGE; + goto fail; + } + + /* Check performed above makes sure that this doesn't overflow. */ + prot_size = ALIGN_UP (prot_size, GRUB_TXT_PMR_ALIGN); + + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (prot_size); + slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); + /* Do not go below GRUB_TXT_PMR_ALIGN. */ + preferred_address = (preferred_address > slparams->mle_ptab_size) ? + (preferred_address - slparams->mle_ptab_size) : GRUB_TXT_PMR_ALIGN; + preferred_address = ALIGN_UP (preferred_address, GRUB_TXT_PMR_ALIGN); + } + else + { + prot_size = page_align (prot_size); + slparams->mle_ptab_size = 0; + } + + /* Protected mode code immediately follows MLE page table. */ + total_size = slparams->mle_ptab_size + prot_size; /* Initialize the memory pointers with NULL for convenience. */ free_pages (); @@ -177,15 +228,15 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, err = grub_relocator_alloc_chunk_align (relocator, &ch, preferred_address, preferred_address, - prot_size, 1, + total_size, 1, GRUB_RELOCATOR_PREFERENCE_LOW, 1); for (; err && *align + 1 > min_align; (*align)--) { grub_errno = GRUB_ERR_NONE; err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, - UP_TO_TOP32 (prot_size), - prot_size, 1 << *align, + UP_TO_TOP32 (total_size), + total_size, 1 << *align, GRUB_RELOCATOR_PREFERENCE_LOW, 1); } @@ -195,11 +246,87 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, else err = grub_relocator_alloc_chunk_addr (relocator, &ch, preferred_address, - prot_size); + total_size); if (err) goto fail; prot_mode_mem = get_virtual_current_address (ch); prot_mode_target = get_physical_target_address (ch); + + if (grub_slaunch_platform_type () != SLP_NONE) + { + /* Zero out memory to get stable MLE measurements. */ + grub_memset (prot_mode_mem, 0, total_size); + + slparams->mle_ptab_mem = prot_mode_mem; + slparams->mle_ptab_target = prot_mode_target; + + prot_mode_mem = (char *)prot_mode_mem + slparams->mle_ptab_size; + prot_mode_target += slparams->mle_ptab_size; + + slparams->mle_start = prot_mode_target; + slparams->mle_size = prot_size; + slparams->mle_mem = prot_mode_mem; + + grub_dprintf ("linux", "mle_ptab_mem = %p, mle_ptab_target = %lx, mle_ptab_size = %x\n", + slparams->mle_ptab_mem, (unsigned long) slparams->mle_ptab_target, + (unsigned) slparams->mle_ptab_size); + + /* + * For AMD SKINIT, SLRT is part of SLB and it will be initialized by + * grub_skinit_boot_prepare(). + */ + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_PAGE_SIZE, + GRUB_PAGE_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + goto fail; + + slparams->slr_table_base = get_physical_target_address (ch); + slparams->slr_table_size = GRUB_PAGE_SIZE; + slparams->slr_table_mem = get_virtual_current_address (ch); + + grub_memset (slparams->slr_table_mem, 0, slparams->slr_table_size); + + grub_dprintf ("linux", "slr_table_base = %lx, slr_table_size = %x\n", + (unsigned long) slparams->slr_table_base, + (unsigned) slparams->slr_table_size); + } + + err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, + GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + goto fail; + + slparams->tpm_evt_log_base = get_physical_target_address (ch); + slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; + + grub_txt_init_tpm_event_log (get_virtual_current_address (ch), + slparams->tpm_evt_log_size); + + grub_dprintf ("linux", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", + (unsigned long) slparams->tpm_evt_log_base, + (unsigned) slparams->tpm_evt_log_size); + + if (grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_MLE_AP_WAKE_BLOCK_SIZE, + GRUB_MLE_AP_WAKE_BLOCK_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1)) + goto fail; + + slparams->ap_wake_block = get_physical_target_address (ch); + slparams->ap_wake_block_size = GRUB_MLE_AP_WAKE_BLOCK_SIZE; + + grub_memset (get_virtual_current_address (ch), 0, slparams->ap_wake_block_size); + + grub_dprintf ("linux", "ap_wake_block = %lx, ap_wake_block_size = %lx\n", + (unsigned long) slparams->ap_wake_block, + (unsigned long) slparams->ap_wake_block_size); + } } grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n", @@ -399,6 +526,63 @@ grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size, return 0; } +static void +grub_linux_setup_slr_table (struct grub_slaunch_params *slparams) +{ + struct linux_kernel_params *boot_params = (void *) (grub_addr_t) slparams->boot_params_addr; + struct linux_params_efi_info *efi_info; + + /* A bit of work to extract the v2.08 EFI info from the linux params */ + efi_info = (void *)((grub_uint8_t *)&boot_params->v0208 + 2*sizeof(grub_uint32_t)); + + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_BOOT_PARAMS, + /*flags=*/0, + (grub_addr_t) boot_params, + GRUB_PAGE_SIZE, + "Measured boot parameters"); + + if (boot_params->setup_data) + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_SETUP_DATA, + GRUB_SLR_POLICY_IMPLICIT_SIZE, + boot_params->setup_data, + /*size=*/0, + "Measured Kernel setup_data"); + + /* The cmdline ptr can have hi bits but GRUB puts it always < 4G */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_CMDLINE, + /*flags=*/0, + boot_params->cmd_line_ptr, + boot_params->cmdline_size, + "Measured Kernel command line"); + + if (!grub_memcmp(&efi_info->efi_signature, "EL64", sizeof(grub_uint32_t))) + { + grub_uint64_t mmap_addr = + ((grub_uint64_t) efi_info->efi_mmap_hi << 32) | efi_info->efi_mmap; + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_UEFI_MEMMAP, + /*flags=*/0, + mmap_addr, + efi_info->efi_mmap_size, + "Measured EFI memory map"); + } + + if (boot_params->ramdisk_image) + /* + * The initrd image and size can have hi bits but in GRUB it is always + * < 4G, see GRUB_LINUX_INITRD_MAX_ADDRESS in grub_cmd_initrd(). + */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_CODE_PCR, + GRUB_SLR_ET_RAMDISK, + /*flags=*/0, + boot_params->ramdisk_image, + boot_params->ramdisk_size, + "Measured Kernel initrd"); +} + static grub_err_t grub_linux_boot (void) { @@ -412,6 +596,7 @@ grub_linux_boot (void) }; grub_size_t mmap_size; grub_size_t cl_offset; + struct grub_slaunch_params *slparams = grub_slaunch_params(); #ifdef GRUB_MACHINE_IEEE1275 { @@ -588,6 +773,8 @@ grub_linux_boot (void) ctx.params->secure_boot = grub_efi_get_secureboot (); + grub_dprintf ("linux", "EFI exit boot services\n"); + err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL, &efi_desc_size, &efi_desc_version); if (err) @@ -637,12 +824,53 @@ grub_linux_boot (void) } #endif - /* FIXME. */ - /* asm volatile ("lidt %0" : : "m" (idt_desc)); */ - state.ebp = state.edi = state.ebx = 0; - state.esi = ctx.real_mode_target; - state.esp = ctx.real_mode_target; - state.eip = ctx.params->code32_start; + state.edi = grub_slaunch_platform_type (); + + if (state.edi == SLP_INTEL_TXT) + { + slparams->boot_params_addr = ctx.real_mode_target; + + err = grub_txt_boot_prepare (slparams); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "TXT boot preparation failed"); + + grub_slaunch_add_slrt_policy_entries (); + grub_txt_add_slrt_policy_entries (); + grub_linux_setup_slr_table (slparams); + grub_slaunch_finish_slr_table (); + + /* Configure relocator GETSEC[SENTER] call. */ + state.eax = GRUB_SMX_LEAF_SENTER; + state.ebx = slparams->dce_base; + state.ecx = slparams->dce_size; + state.edx = 0; + } + else if (state.edi == SLP_AMD_SKINIT) + { + slparams->boot_params_addr = ctx.real_mode_target; + + err = grub_skinit_boot_prepare (relocator, slparams); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "SKINIT preparations have failed"); + + grub_slaunch_add_slrt_policy_entries (); + grub_linux_setup_slr_table (slparams); + grub_slaunch_finish_slr_table (); + + state.eax = slparams->dce_base; + } + else + { + /* FIXME. */ + /* asm volatile ("lidt %0" : : "m" (idt_desc)); */ + state.ebp = state.edi = state.ebx = 0; + state.esi = ctx.real_mode_target; + state.esp = ctx.real_mode_target; + state.eip = ctx.params->code32_start; + } + return grub_relocator32_boot (relocator, state, 0); } @@ -664,13 +892,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), struct linux_i386_kernel_header lh; grub_uint8_t *linux_params_ptr; grub_uint8_t setup_sects; - grub_size_t real_size, prot_size, prot_file_size, kernel_offset; + grub_size_t real_size, prot_size, kernel_offset; grub_ssize_t len; int i; grub_size_t align, min_align; int relocatable; grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR; grub_uint8_t *kernel = NULL; + struct grub_slaunch_params *slparams = grub_slaunch_params(); grub_dl_ref (my_mod); @@ -786,6 +1015,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), prot_init_space = page_align (prot_size) * 3; } + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + /* PMRs require GRUB_TXT_PMR_ALIGN_SHIFT aligments. */ + min_align = grub_max (min_align, GRUB_TXT_PMR_ALIGN_SHIFT); + align = grub_max (align, GRUB_TXT_PMR_ALIGN_SHIFT); + } + if (allocate_pages (prot_size, &align, min_align, relocatable, preferred_address)) @@ -793,6 +1029,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memset (&linux_params, 0, sizeof (linux_params)); + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + grub_txt_setup_mle_ptab (slparams); + /* * The Linux 32-bit boot protocol defines the setup header end * to be at 0x202 + the byte value at 0x201. @@ -822,6 +1061,80 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X); + /* Read the kernel_info struct. */ + if (grub_le_to_cpu16 (lh.version) >= 0x020f) + { + if (grub_file_seek (file, (grub_off_t) grub_le_to_cpu32 (lh.kernel_info_offset) + + real_size + GRUB_DISK_SECTOR_SIZE) == ((grub_off_t) -1)) + goto fail; + + linux_info = grub_malloc (KERNEL_INFO_MIN_SIZE_TOTAL); + + if (!linux_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate memory for kernel_info")); + goto fail; + } + + /* Load minimal kernel_info struct. */ + if (grub_file_read (file, linux_info, + KERNEL_INFO_MIN_SIZE_TOTAL) != KERNEL_INFO_MIN_SIZE_TOTAL) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + if (grub_memcmp (&linux_info->header, KERNEL_INFO_HEADER, sizeof (linux_info->header))) + { + grub_error (GRUB_ERR_BAD_OS, N_("incorrect kernel_info header")); + goto fail; + } + + linux_info->size_total = grub_le_to_cpu32 (linux_info->size_total); + if (linux_info->size_total < KERNEL_INFO_MIN_SIZE_TOTAL) + { + grub_error (GRUB_ERR_BAD_OS, N_("incorrect kernel_info size")); + goto fail; + } + + linux_info = grub_realloc (linux_info, linux_info->size_total); + + if (!linux_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot reallocate memory for kernel_info")); + goto fail; + } + + /* Load the rest of kernel_info struct. */ + if (grub_file_read (file, &linux_info->setup_type_max, + linux_info->size_total - KERNEL_INFO_MIN_SIZE_TOTAL) != + (grub_ssize_t)(linux_info->size_total - KERNEL_INFO_MIN_SIZE_TOTAL)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + if (grub_slaunch_platform_type () != SLP_NONE) + { + if (OFFSET_OF (mle_header_offset, linux_info) >= + grub_le_to_cpu32 (linux_info->size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: lack of mle_header_offset")); + goto fail; + } + + slparams->mle_header_offset = grub_le_to_cpu32 (linux_info->mle_header_offset); + } + } + else if (grub_slaunch_platform_type () != SLP_NONE) + { + grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: boot protocol too old")); + goto fail; + } + linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; linux_params.kernel_alignment = (1 << align); linux_params.ps_mouse = linux_params.padding11 = 0; @@ -855,9 +1168,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), #ifdef GRUB_MACHINE_EFI #ifdef __x86_64__ if (grub_le_to_cpu16 (linux_params.version) < 0x0208 && - ((grub_addr_t) grub_efi_system_table >> 32) != 0) - return grub_error(GRUB_ERR_BAD_OS, - "kernel does not support 64-bit addressing"); + ((grub_addr_t) grub_efi_system_table >> 32) != 0) { + grub_errno = grub_error(GRUB_ERR_BAD_OS, + "kernel does not support 64-bit addressing"); + goto fail; + } #endif if (grub_le_to_cpu16 (linux_params.version) >= 0x0208) diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index 11a6e224f..f915386e8 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -450,7 +450,7 @@ retrieve_video_parameters (struct multiboot_info *mbi, } grub_err_t -grub_multiboot_make_mbi (grub_uint32_t *target) +grub_multiboot_make_mbi (grub_uint32_t *target, grub_uint32_t *size) { struct multiboot_info *mbi; struct multiboot_mod_list *modlist; @@ -618,6 +618,8 @@ grub_multiboot_make_mbi (grub_uint32_t *target) return err; #endif + *size = (char *) ptrorig - (char *) mbi; + return GRUB_ERR_NONE; } diff --git a/grub-core/loader/i386/pc/plan9.c b/grub-core/loader/i386/pc/plan9.c index 960e866f4..9ec461c75 100644 --- a/grub-core/loader/i386/pc/plan9.c +++ b/grub-core/loader/i386/pc/plan9.c @@ -34,6 +34,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -84,7 +85,7 @@ grub_plan9_boot (void) .ebx = 0, .ecx = 0, .edx = 0, - .edi = 0, + .edi = SLP_NONE, .esp = 0, .ebp = 0, .esi = 0 diff --git a/grub-core/loader/i386/skinit.c b/grub-core/loader/i386/skinit.c new file mode 100644 index 000000000..0011f3ece --- /dev/null +++ b/grub-core/loader/i386/skinit.c @@ -0,0 +1,156 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SLRT_SIZE GRUB_PAGE_SIZE + +/* Offset to entry point. */ +#define SLB_ENTRY(slb) ((const grub_uint16_t *) (slb))[0] +/* Amount of data actually measured for DRTM. */ +#define SLB_MEASURED(slb) ((const grub_uint16_t *) (slb))[1] +/* Offset to structure with extra info. */ +#define SLB_INFO(slb) ((const grub_uint16_t *) (slb))[2] +/* Offset to area for passing data to SKL. */ +#define SLB_PARAM(slb) ((const grub_uint16_t *) (slb))[3] + +int +grub_skinit_is_slb (const void *slb_base, grub_uint32_t slb_size) +{ + const grub_uint8_t skl_uuid[16] = { + 0x78, 0xf1, 0x26, 0x8e, 0x04, 0x92, 0x11, 0xe9, + 0x83, 0x2a, 0xc8, 0x5b, 0x76, 0xc4, 0xcc, 0x02, + }; + /* We need space after SLB to pass SLRT to it. */ + const grub_ssize_t max_size = GRUB_SKINIT_SLB_SIZE - SLRT_SIZE; + + const grub_uint8_t *uuid; + + if (slb_size > max_size) + { + grub_dprintf ("slaunch", "SLB is too large: %d > %zd\n", + slb_size, max_size); + return 0; + } + + if (SLB_MEASURED (slb_base) > slb_size) + { + grub_dprintf ("slaunch", "SLB measured size is too large: %d > %d\n", + SLB_MEASURED (slb_base), slb_size); + return 0; + } + + if (SLB_ENTRY (slb_base) >= SLB_MEASURED (slb_base)) + { + grub_dprintf ("slaunch", "SLB entry is not measured: %d >= %d\n", + SLB_ENTRY (slb_base), SLB_MEASURED (slb_base)); + return 0; + } + + if (SLB_INFO (slb_base) > SLB_MEASURED (slb_base) - sizeof(skl_uuid)) + { + grub_dprintf ("slaunch", "SLB info is not measured: %d > %zd\n", + SLB_INFO (slb_base), + SLB_MEASURED (slb_base) - sizeof(skl_uuid)); + return 0; + } + + if (SLB_PARAM (slb_base) > max_size) + { + grub_dprintf ("slaunch", "SLB bootloader data offset is too large: %d > %zd\n", + SLB_PARAM (slb_base), max_size); + return 0; + } + + uuid = (const grub_uint8_t *) slb_base + SLB_INFO (slb_base); + if (grub_memcmp (uuid, skl_uuid, sizeof(skl_uuid)) != 0) + { + grub_dprintf ("slaunch", "SLB has unexpected UUID\n"); + return 0; + } + + return 1; +} + +grub_err_t +grub_skinit_boot_prepare (struct grub_relocator *rel, + struct grub_slaunch_params *slparams) +{ + grub_uint32_t *apic = (grub_uint32_t *) 0xfee00300ULL; + const void *slb = grub_slaunch_module (); + grub_relocator_chunk_t ch; + grub_err_t err; + void *dce_mem; + + if (slb == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "SLB module is missing"); + + /* + * Contrary to the TXT, on AMD we do not have vendor-provided blobs in + * reserved memory, we are using normal RAM + */ + err = grub_relocator_alloc_chunk_align (rel, &ch, 0, + 0xffffffff - GRUB_SKINIT_SLB_SIZE, + GRUB_SKINIT_SLB_SIZE, + GRUB_SKINIT_SLB_ALIGN, + GRUB_RELOCATOR_PREFERENCE_LOW, 1); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "cannot alloc memory for SLB"); + + /* + * Send INIT IPI to all APs (CPUs other than this one). + * + * "AMD64 Architecture Programmer’s Manual, Rev. 3.42, Vol. 2" says "Depending + * on processor implementation, a fixed delay of no more than 1000 processor + * cycles may be necessary before executing SKINIT to ensure reliable sensing + * of APIC INIT state by the SKINIT." + * + * However, in tests it wasn't always enough (sometimes up to 7000 cycles + * were necessary), so send the IPIs before grub_memcpy()/grub_memset() to + * increase the delay before SKINIT is executed. + */ + grub_dprintf ("slaunch", "broadcasting INIT\r\n"); + *apic = 0x000c0500; + + slparams->dce_base = get_physical_target_address (ch); + slparams->dce_size = SLB_MEASURED (slb); + + dce_mem = get_virtual_current_address (ch); + grub_memcpy (dce_mem, slb, slparams->dce_size); + + slparams->slr_table_base = slparams->dce_base + SLB_PARAM (slb); + slparams->slr_table_size = SLRT_SIZE; + slparams->slr_table_mem = (grub_uint8_t *) dce_mem + SLB_PARAM (slb); + grub_memset (slparams->slr_table_mem, 0, SLRT_SIZE); + + grub_slaunch_init_slrt_storage (GRUB_SLR_AMD_SKINIT); + + grub_dprintf ("slaunch", "grub_tpm_relinquish_locality\r\n"); + grub_tpm_relinquish_locality (0); + + grub_dprintf ("slaunch", "Invoke SKINIT\r\n"); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c new file mode 100644 index 000000000..806dd8c8d --- /dev/null +++ b/grub-core/loader/i386/slaunch.c @@ -0,0 +1,337 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint32_t slp = SLP_NONE; + +static void *slaunch_module = NULL; + +static struct grub_slaunch_params slparams; + +/* Area to collect and build SLR Table information. */ +static struct grub_slr_entry_dl_info slr_dl_info_staging; +static struct grub_slr_entry_log_info slr_log_info_staging; +static grub_uint8_t slr_policy_buf[GRUB_PAGE_SIZE]; +static struct grub_slr_entry_policy *slr_policy_staging = + (struct grub_slr_entry_policy *)slr_policy_buf; + +grub_uint32_t +grub_slaunch_platform_type (void) +{ + return slp; +} + +void * +grub_slaunch_module (void) +{ + return slaunch_module; +} + +struct grub_slaunch_params * +grub_slaunch_params (void) +{ + return &slparams; +} + +void +grub_slaunch_init_slrt_storage (int arch) +{ + struct grub_txt_mle_header *mle_header = + (void *) ((grub_uint8_t *) slparams.mle_mem + slparams.mle_header_offset); + + /* Setup the generic bits of the SLRT. */ + grub_slr_init_table(slparams.slr_table_mem, arch, slparams.slr_table_size); + + /* Setup DCE and DLME information. */ + slr_dl_info_staging.hdr.tag = GRUB_SLR_ENTRY_DL_INFO; + slr_dl_info_staging.hdr.size = sizeof(struct grub_slr_entry_dl_info); + slr_dl_info_staging.bl_context.bootloader = GRUB_SLR_BOOTLOADER_GRUB; + slr_dl_info_staging.bl_context.context = slparams.boot_params_addr; + slr_dl_info_staging.dce_base = slparams.dce_base; + slr_dl_info_staging.dce_size = slparams.dce_size; + slr_dl_info_staging.dlme_base = slparams.mle_start; + slr_dl_info_staging.dlme_size = slparams.mle_size; + slr_dl_info_staging.dlme_entry = mle_header->entry_point; + + slr_log_info_staging.hdr.tag = GRUB_SLR_ENTRY_LOG_INFO; + slr_log_info_staging.hdr.size = sizeof(struct grub_slr_entry_log_info); + slr_log_info_staging.addr = slparams.tpm_evt_log_base; + slr_log_info_staging.size = slparams.tpm_evt_log_size; + slr_log_info_staging.format = + (grub_get_tpm_ver () == GRUB_TPM_20) ? + GRUB_SLR_DRTM_TPM20_LOG : GRUB_SLR_DRTM_TPM12_LOG; + + slr_policy_staging->hdr.tag = GRUB_SLR_ENTRY_DRTM_POLICY; + slr_policy_staging->hdr.size = sizeof(struct grub_slr_entry_policy); + slr_policy_staging->revision = GRUB_SLR_TABLE_REVISION; + slr_policy_staging->nr_entries = 0; +} + +void grub_slaunch_add_slrt_policy_entries (void) +{ + /* The SLR table should be measured too, at least parts of it. */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_SLRT, + GRUB_SLR_POLICY_IMPLICIT_SIZE, + slparams.slr_table_base, + /*size=*/0, + "Measured SLR Table"); +} + +void +grub_slaunch_add_slrt_policy_entry (grub_uint16_t pcr, + grub_uint16_t entity_type, + grub_uint16_t flags, + grub_uint64_t entity, + grub_uint64_t size, + const char *evt_info) +{ + struct grub_slr_policy_entry *entry = + (void *)((grub_uint8_t *) slr_policy_staging + + sizeof(struct grub_slr_entry_policy) + + slr_policy_staging->nr_entries*sizeof(*entry)); + + if (slr_policy_staging->hdr.size > sizeof(slr_policy_buf) - sizeof(*entry)) + grub_fatal ("Not enough space for adding policy entry: %s! The buffer is full.", + evt_info); + + entry->pcr = pcr; + entry->entity_type = entity_type; + entry->flags = flags; + entry->entity = entity; + entry->size = size; + + grub_strncpy (entry->evt_info, evt_info, sizeof(entry->evt_info) - 1); + entry->evt_info[sizeof(entry->evt_info) - 1] = '\0'; + + slr_policy_staging->hdr.size += sizeof(*entry); + ++slr_policy_staging->nr_entries; +} + +void +grub_slaunch_finish_slr_table (void) +{ + struct grub_slr_table *slr_table = slparams.slr_table_mem; + + grub_slr_add_entry (slr_table, &slr_dl_info_staging.hdr); + grub_slr_add_entry (slr_table, &slr_log_info_staging.hdr); + grub_slr_add_entry (slr_table, &slr_policy_staging->hdr); +} + +static grub_err_t +grub_cmd_slaunch (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_uint32_t manufacturer[3]; + grub_uint32_t eax, ebx, ecx, edx; + grub_uint64_t msr_value; + grub_err_t err; + + if (!grub_cpu_is_cpuid_supported ()) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPUID is unsupported")); + + err = grub_cpu_is_msr_supported (); + + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("MSRs are unsupported")); + + grub_cpuid (0, eax, manufacturer[0], manufacturer[2], manufacturer[1]); + + if (!grub_memcmp (manufacturer, "GenuineIntel", 12)) + { + err = grub_txt_init (); + + if (err != GRUB_ERR_NONE) + return err; + + slp = SLP_INTEL_TXT; + } + else if (!grub_memcmp (manufacturer, "AuthenticAMD", 12)) + { + grub_cpuid (GRUB_AMD_CPUID_FEATURES, eax, ebx, ecx, edx); + if (! (ecx & GRUB_AMD_CPUID_FEATURES_ECX_SVM) ) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPU does not support AMD SVM")); + + /* Check whether SVM feature is disabled in BIOS */ + msr_value = grub_rdmsr (GRUB_MSR_AMD64_VM_CR); + if (msr_value & GRUB_MSR_SVM_VM_CR_SVM_DISABLE) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("BIOS has AMD SVM disabled")); + + slp = SLP_AMD_SKINIT; + } + else + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("CPU is unsupported")); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + grub_ssize_t size; + void *new_module = NULL; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected: filename")); + + if (slp == SLP_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("secure launch not enabled")); + + if (slp > SLP_AMD_SKINIT) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unknown secure launch platform type: %d"), slp); + + grub_errno = GRUB_ERR_NONE; + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_SLAUNCH_MODULE); + + if (file == NULL) + return grub_errno; + + size = grub_file_size (file); + + if (!size) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("file size is zero")); + goto fail; + } + + new_module = grub_malloc (size); + + if (new_module == NULL) + goto fail; + + if (grub_file_read (file, new_module, size) != size) + { + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file: %s"), + argv[0]); + goto fail; + } + + if (slp == SLP_INTEL_TXT) + { + if (!grub_txt_is_sinit_acmod (new_module, size)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SINIT ACM")); + goto fail; + } + + if (!grub_txt_acmod_match_platform (new_module)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("SINIT ACM does not match platform")); + goto fail; + } + } + else if (slp == SLP_AMD_SKINIT) + { + if (!grub_skinit_is_slb (new_module, size)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SLB")); + goto fail; + } + } + + grub_file_close (file); + + grub_free (slaunch_module); + slaunch_module = new_module; + + return GRUB_ERR_NONE; + +fail: + grub_error_push (); + + grub_free (new_module); + grub_file_close (file); + + grub_error_pop (); + + return grub_errno; +} + +static grub_err_t +grub_cmd_slaunch_state (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + if (slp == SLP_NONE) + grub_printf ("Secure launcher: Disabled\n"); + else if (slp == SLP_INTEL_TXT) + { + grub_printf ("Secure launcher: Intel TXT\n"); + grub_txt_state_show (); + } + else if (slp == SLP_AMD_SKINIT) + { + grub_printf ("Secure launcher: AMD SKINIT\n"); + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Unknown secure launcher platform type: %d\n"), slp); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_slaunch, cmd_slaunch_module, cmd_slaunch_state; + +GRUB_MOD_INIT (slaunch) +{ + cmd_slaunch = grub_register_command ("slaunch", grub_cmd_slaunch, + NULL, N_("Enable secure launcher")); + cmd_slaunch_module = grub_register_command ("slaunch_module", grub_cmd_slaunch_module, + NULL, N_("Load secure launcher module from file")); + cmd_slaunch_state = grub_register_command ("slaunch_state", grub_cmd_slaunch_state, + NULL, N_("Display secure launcher state")); +} + +GRUB_MOD_FINI (slaunch) +{ + if (cmd_slaunch_state) + grub_unregister_command (cmd_slaunch_state); + + if (cmd_slaunch_module) + grub_unregister_command (cmd_slaunch_module); + + if (cmd_slaunch) + grub_unregister_command (cmd_slaunch); + + if (slp == SLP_INTEL_TXT) + grub_txt_shutdown (); +} diff --git a/grub-core/loader/i386/txt/acmod.c b/grub-core/loader/i386/txt/acmod.c new file mode 100644 index 000000000..55675a2fd --- /dev/null +++ b/grub-core/loader/i386/txt/acmod.c @@ -0,0 +1,605 @@ +/* + * acmod.c: support functions for use of Intel(r) TXT Authenticated + * Code (AC) Modules + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the Intel Corporation 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Macro that returns value of it->field if it's inside info table, 0 otherwise. + * Fields at or below 'length' ('uuid', 'chipset_acm_type', 'version') don't + * benefit from this macro, because it requires that 'length' (and by extension + * fields below that) is valid. + */ +#define info_table_get(it, field) \ + (__builtin_offsetof(struct grub_txt_acm_info_table, field) + sizeof(it->field)\ + <= it->length ? it->field : 0) + +/* + * Returns hdr + offset if [offset, offset + count * size) is within the bounds + * of the ACM, NULL otherwise. + */ +static void * +n_fit_in_acm (struct grub_txt_acm_header *hdr, grub_uint32_t offset, + grub_uint32_t size, grub_uint32_t count) +{ + grub_uint32_t total_size, elem_end; + /* ACM size overflow was checked in is_acmod() */ + grub_uint32_t acm_len = hdr->size * 4; + + /* + * `offset` will often come from `info_table_get`, and this is the most + * convenient place to check for the macro returning zero. This is fine, since + * there is no legitimate reason to access the zero offset in this manner. + */ + if (offset == 0) + return NULL; + + if (grub_mul (size, count, &total_size)) + return NULL; + + if (grub_add (offset, total_size, &elem_end)) + return NULL; + + if (elem_end > acm_len) + return NULL; + + /* + * Not checking if (hdr + elem_end) overflows. We know that (hdr + acm_len) + * doesn't, and that elem_end <= acm_len. For the same reason we don't have + * to check if (hdr + offset) overflows. + */ + + return (void *) ((unsigned long) hdr + offset); +} + +static void * +fits_in_acm (struct grub_txt_acm_header *hdr, grub_uint32_t offset, + grub_uint32_t size) +{ + return n_fit_in_acm(hdr, offset, size, 1); +} + +/* + * Returns pointer to ACM information table. If the table is located outside of + * ACM or its reported size is too small to cover at least 'length' field, + * NULL is returned instead. + */ +static struct grub_txt_acm_info_table* +get_acmod_info_table (struct grub_txt_acm_header* hdr) +{ + grub_uint32_t user_area_off, info_table_size; + struct grub_txt_acm_info_table *ptr = NULL; + /* Minimum size required to read full size of table */ + info_table_size = __builtin_offsetof (struct grub_txt_acm_info_table, length) + + sizeof(ptr->length); + + /* Overflow? */ + if (grub_add (hdr->header_len, hdr->scratch_size, &user_area_off)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length plus scratch size overflows")); + return NULL; + } + + if (grub_mul (user_area_off, 4, &user_area_off)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length and scratch size in bytes overflows")); + return NULL; + } + + ptr = fits_in_acm(hdr, user_area_off, info_table_size); + + if (ptr != NULL) + { + if (info_table_get (ptr, length) < info_table_size) + return NULL; + + info_table_size = info_table_get (ptr, length); + ptr = fits_in_acm(hdr, user_area_off, info_table_size); + } + + return ptr; +} + +/* + * Function returns pointer to chipset ID list, after checking that + * grub_txt_acm_chipset_id_list and all grub_txt_acm_chipset_id structures are + * within ACM. Otherwise, NULL is returned. + */ +static struct grub_txt_acm_chipset_id_list* +get_acmod_chipset_list (struct grub_txt_acm_header *hdr) +{ + struct grub_txt_acm_info_table *info_table; + grub_uint32_t id_entries_off; + struct grub_txt_acm_chipset_id_list *chipset_id_list; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table (hdr); + if (info_table == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table out of bounds")); + return NULL; + } + + chipset_id_list = fits_in_acm(hdr, info_table_get (info_table, chipset_id_list), + sizeof(struct grub_txt_acm_chipset_id_list)); + if (chipset_id_list == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset ID list out of bounds")); + return NULL; + } + + /* Overflows were checked by fits_in_acm() */ + id_entries_off = info_table->chipset_id_list + sizeof(*chipset_id_list); + + if (n_fit_in_acm (hdr, id_entries_off, sizeof(struct grub_txt_acm_chipset_id), + chipset_id_list->count) == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset ID entries out of bounds")); + return NULL; + } + + return chipset_id_list; +} + +/* + * Function returns pointer to processor ID list, after checking that + * grub_txt_acm_processor_id_list and all grub_txt_acm_processor_id structures + * are within ACM. Otherwise, NULL is returned. + */ +static struct grub_txt_acm_processor_id_list * +get_acmod_processor_list (struct grub_txt_acm_header* hdr) +{ + struct grub_txt_acm_info_table *info_table; + grub_uint32_t id_entries_off; + struct grub_txt_acm_processor_id_list *proc_id_list; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table (hdr); + if (info_table == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table out of bounds")); + return NULL; + } + + proc_id_list = fits_in_acm (hdr, info_table_get (info_table, processor_id_list), + sizeof(*proc_id_list)); + if (proc_id_list == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor ID list out of bounds")); + return NULL; + } + + /* Overflows were checked by fits_in_acm() */ + id_entries_off = info_table->processor_id_list + sizeof(*proc_id_list); + + if (n_fit_in_acm (hdr, id_entries_off, sizeof(*proc_id_list), + proc_id_list->count) == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor ID entries out of bounds")); + return NULL; + } + + return proc_id_list; +} + +static int +is_acmod (const void *acmod_base, grub_uint32_t acmod_size, + grub_uint8_t *type_out) +{ + struct grub_txt_acm_header *acm_hdr = (struct grub_txt_acm_header *)acmod_base; + struct grub_txt_acm_info_table *info_table; + grub_uint32_t size_from_hdr; + + /* First check size */ + if (acmod_size < sizeof (*acm_hdr)) + return 0; + + /* Then check overflow */ + if (grub_mul (acm_hdr->size, 4, &size_from_hdr)) + return 0; + + /* Then check size equivalency */ + if (acmod_size != size_from_hdr) + return 0; + + /* Then check type, sub-type and vendor */ + if ((acm_hdr->module_type != GRUB_TXT_ACM_MODULE_TYPE) || + (acm_hdr->module_sub_type != GRUB_TXT_ACM_MODULE_SUB_TYPE_TXT_ACM) || + (acm_hdr->module_vendor != GRUB_TXT_ACM_MODULE_VENDOR_INTEL)) + return 0; + + info_table = get_acmod_info_table (acm_hdr); + if (info_table == NULL) + return 0; + + /* Check if ACM UUID is present */ + if (grub_memcmp (&(info_table->uuid), GRUB_TXT_ACM_UUID, 16)) + return 0; + + /* + * TXT specification doesn't give clear mapping of info table size to version, + * so just warn if the size is different than expected but try to use it + * anyway. info_table_get() macro does enough testing to not read outside + * of info table. + */ + if (info_table->length < sizeof(*info_table)) + grub_dprintf ("slaunch", "Info table size (%x) smaller than expected (%" + PRIxGRUB_SIZE ")\n", + info_table->length, sizeof(*info_table)); + + if (type_out) + *type_out = info_table_get (info_table, chipset_acm_type); + + return 1; +} + +static struct grub_txt_acm_header * +get_bios_sinit (void *sinit_region_base) +{ + grub_uint8_t *txt_heap = grub_txt_get_heap (); + struct grub_txt_bios_data *bios_data = grub_txt_bios_data_start (txt_heap); + struct grub_txt_acm_header *bios_sinit; + grub_uint32_t tmp; + + if (sinit_region_base == NULL) + return NULL; + + if (bios_data->bios_sinit_size == 0) + return NULL; + + /* Check if ACM crosses 4G */ + if (grub_add ((unsigned long)sinit_region_base, bios_data->bios_sinit_size, + &tmp)) + return NULL; + + /* BIOS has loaded an SINIT module, so verify that it is valid */ + grub_dprintf ("slaunch", "BIOS has already loaded an SINIT module\n"); + + bios_sinit = (struct grub_txt_acm_header *) sinit_region_base; + + /* Is it a valid SINIT module? */ + if (!grub_txt_is_sinit_acmod (sinit_region_base, bios_data->bios_sinit_size) || + !grub_txt_acmod_match_platform (bios_sinit)) + { + grub_dprintf("slaunch", "BIOS SINIT module did not pass reasonableness checks"); + return NULL; + } + + return bios_sinit; +} + +grub_uint32_t +grub_txt_supported_os_sinit_data_ver (struct grub_txt_acm_header *hdr) +{ + static struct grub_txt_acm_info_table *info_table; + + /* Assumes that it passed grub_txt_is_sinit_acmod() */ + info_table = get_acmod_info_table (hdr); + + if (info_table == NULL) + return 0; + + return info_table_get (info_table, os_sinit_data_ver); +} + +grub_uint32_t +grub_txt_get_sinit_capabilities (struct grub_txt_acm_header *hdr) +{ + static struct grub_txt_acm_info_table *info_table; + + /* Assumes that it passed grub_txt_is_sinit_acmod() */ + info_table = get_acmod_info_table (hdr); + + if (info_table == NULL || info_table->version < 3) + return 0; + + return info_table_get (info_table, capabilities); +} + +int +grub_txt_is_sinit_acmod (const void *acmod_base, grub_uint32_t acmod_size) +{ + grub_uint8_t type; + + if (!is_acmod (acmod_base, acmod_size, &type)) + return 0; + + if (type != GRUB_TXT_ACM_CHIPSET_TYPE_SINIT) + return 0; + + return 1; +} + +static int +didvid_matches(union grub_txt_didvid didvid, + struct grub_txt_acm_chipset_id *chipset_id) +{ + if (didvid.vid != chipset_id->vendor_id) + return 0; + + if (didvid.did != chipset_id->device_id) + return 0; + + /* + * If RevisionIdMask is 0, the RevisionId field must exactly match the + * TXT.DIDVID.RID field. + */ + if ((chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) == 0 && + (didvid.rid == chipset_id->revision_id)) + return 1; + + /* + * If RevisionIdMask is 1, the RevisionId field is a bitwise mask that can be + * used to test for any bits set in the TXT.DIDVID.RID field. If any bits are + * set, the RevisionId is a match. + */ + if ((chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) != 0 && + (didvid.rid & chipset_id->revision_id) != 0) + return 1; + + return 0; +} + +int +grub_txt_acmod_match_platform (struct grub_txt_acm_header *hdr) +{ + union grub_txt_didvid didvid; + grub_uint32_t fms, ign, i, ver; + grub_uint64_t platform_id; + struct grub_txt_acm_chipset_id_list *chipset_id_list; + struct grub_txt_acm_chipset_id *chipset_id; + struct grub_txt_acm_processor_id_list *proc_id_list; + struct grub_txt_acm_processor_id *proc_id; + struct grub_txt_acm_info_table *info_table; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + info_table = get_acmod_info_table (hdr); + if (info_table == NULL) + return 0; + + /* Get chipset fusing, device, and vendor id info */ + didvid.value = grub_txt_reg_pub_read64 (GRUB_TXT_DIDVID); + + ver = grub_txt_reg_pub_read32 (GRUB_TXT_VER_QPIIF); + if (ver == 0xffffffff || ver == 0x00) /* Old CPU, need to use VER.FSBIF */ + ver = grub_txt_reg_pub_read32 (GRUB_TXT_VER_FSBIF); + + grub_dprintf ("slaunch", "chipset production fused: %s, " + "chipset vendor: 0x%x, device: 0x%x, revision: 0x%x\n", + (ver & GRUB_TXT_VERSION_PROD_FUSED) ? "yes" : "no" , didvid.vid, + didvid.did, didvid.rid); + + grub_cpuid (1, fms, ign, ign, ign); + platform_id = grub_rdmsr (GRUB_MSR_X86_PLATFORM_ID); + + grub_dprintf ("slaunch", "processor family/model/stepping: 0x%x, " + "platform id: 0x%" PRIxGRUB_UINT64_T "\n", fms, platform_id); + + /* + * Check if chipset fusing is same. Note the DEBUG.FUSE bit in the version + * is 0 when debug fused so the logic below checking a mismatch is valid. + */ + if (!!(ver & GRUB_TXT_VERSION_PROD_FUSED) == + !!(hdr->flags & GRUB_TXT_ACM_FLAG_DEBUG_SIGNED)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("production/debug mismatch between chipset and ACM")); + return 0; + } + + /* Check if chipset vendor/device/revision IDs match */ + chipset_id_list = get_acmod_chipset_list (hdr); + if (chipset_id_list == NULL) + return 0; + + grub_dprintf ("slaunch", "%d SINIT ACM chipset id entries:\n", chipset_id_list->count); + + chipset_id = (struct grub_txt_acm_chipset_id *) ((grub_addr_t) chipset_id_list + sizeof (chipset_id_list->count)); + for (i = 0; i < chipset_id_list->count; i++, chipset_id++) + { + grub_dprintf ("slaunch", " vendor: 0x%x, device: 0x%x, flags: 0x%x, " + "revision: 0x%x, extended: 0x%x\n", chipset_id->vendor_id, + chipset_id->device_id, chipset_id->flags, + chipset_id->revision_id, chipset_id->extended_id); + + if (didvid_matches (didvid, chipset_id)) + break; + } + + if (i >= chipset_id_list->count) + { + /* + * Version 9 introduces flexible ACM information table format, not yet + * supported by this code. + * + * TXT spec says that 9 will be the final version and further changes will + * be reflected elsewhere, but check for higher values too in case they + * change their mind. + */ + if (info_table->version >= 9) + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("chipset id mismatch, flexible ACM info list may contain" + " matching entry but it isn't yet supported by code")); + else + grub_error (GRUB_ERR_BAD_DEVICE, N_("chipset id mismatch")); + + return 0; + } + + /* + * Unfortunately the spec isn't too clear on what the changes to the info + * table were, across the different versions, but an old version of the entire + * spec document shows that the processor table field didn't exist when the + * latest version of the info table was 3. + */ + if (info_table->version < 4) + return 1; + + /* Check if processor family/model/stepping and platform IDs match */ + proc_id_list = get_acmod_processor_list(hdr); + if (proc_id_list == NULL) + return 0; + + grub_dprintf ("slaunch", "%d SINIT ACM processor id entries:\n", proc_id_list->count); + + proc_id = (struct grub_txt_acm_processor_id *) ((grub_addr_t) proc_id_list + sizeof (proc_id_list->count)); + for (i = 0; i < proc_id_list->count; i++, proc_id++) + { + grub_dprintf ("slaunch", " fms: 0x%x, fms_mask: 0x%x, platform_id: 0x%" PRIxGRUB_UINT64_T + ", platform_mask: 0x%" PRIxGRUB_UINT64_T "\n", proc_id->fms, proc_id->fms_mask, + proc_id->platform_id, proc_id->platform_mask); + + if ((proc_id->fms == (fms & proc_id->fms_mask)) && + (proc_id->platform_id == (platform_id & proc_id->platform_mask))) + break; + } + + if (i >= proc_id_list->count) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("processor id mismatch")); + return 0; + } + + return 1; +} + +/* + * Choose between the BIOS-provided and user-provided SINIT ACMs, and copy the + * chosen module to the SINIT memory. + */ +struct grub_txt_acm_header * +grub_txt_sinit_select (struct grub_txt_acm_header *sinit) +{ + struct grub_txt_acm_header *bios_sinit; + void *sinit_region_base; + grub_uint32_t sinit_size, sinit_region_size; + + sinit_region_base = (void *)(grub_addr_t) grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_BASE); + sinit_region_size = (grub_uint32_t) grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_SIZE); + + grub_dprintf ("slaunch", "TXT.SINIT.BASE: %p\nTXT.SINIT.SIZE: 0x%" + PRIxGRUB_UINT32_T "\n", sinit_region_base, sinit_region_size); + + if (sinit_region_base == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("no SINIT ACM final resting place")); + return NULL; + } + + if (((grub_addr_t) sinit_region_base & ((1 << GRUB_PAGE_SHIFT) - 1)) != 0) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM base not properly aligned")); + return NULL; + } + + if (sinit != NULL) + grub_dprintf ("slaunch", "SINIT ACM date: %" PRIxGRUB_UINT32_T "\n", sinit->date); + + bios_sinit = get_bios_sinit (sinit_region_base); + + /* Does BIOS provide SINIT ACM? */ + if (bios_sinit != NULL) + { + grub_dprintf ("slaunch", "BIOS SINIT ACM date: %" PRIxGRUB_UINT32_T "\n", + bios_sinit->date); + + if (sinit == NULL) + { + grub_dprintf ("slaunch", "no SINIT ACM provided. Using BIOS SINIT ACM\n"); + return bios_sinit; + } + + if (bios_sinit->date >= sinit->date) + { + grub_dprintf ("slaunch", "BIOS provides newer or same SINIT ACM, so, using BIOS one\n"); + return bios_sinit; + } + + grub_dprintf ("slaunch", "BIOS provides older SINIT ACM, so, ignoring BIOS one\n"); + } + + /* Fail if there is no SINIT ACM. */ + if (sinit == NULL) + return NULL; + + /* Our SINIT ACM is newer than BIOS one or BIOS does not have one. */ + + if (grub_mul (sinit->size, 4, &sinit_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM size in bytes overflows")); + return NULL; + } + + if (sinit_size > sinit_region_size) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("SINIT ACM does not fit into final resting place: 0x%" + PRIxGRUB_UINT32_T "\n"), sinit_size); + return NULL; + } + + grub_memcpy (sinit_region_base, sinit, sinit_size); + + return sinit_region_base; +} diff --git a/grub-core/loader/i386/txt/txt.c b/grub-core/loader/i386/txt/txt.c new file mode 100644 index 000000000..6a9bca685 --- /dev/null +++ b/grub-core/loader/i386/txt/txt.c @@ -0,0 +1,1110 @@ +/* + * txt.c: Intel(r) TXT support functions, including initiating measured + * launch, post-launch, AP wakeup, etc. + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the Intel Corporation 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OS_SINIT_DATA_TPM_12_VER 6 +#define OS_SINIT_DATA_TPM_20_VER 7 + +#define OS_SINIT_DATA_MIN_VER OS_SINIT_DATA_TPM_12_VER + +static struct grub_slr_entry_intel_info slr_intel_info_staging = {0}; + +static grub_err_t +enable_smx_mode (void) +{ + grub_uint32_t caps; + grub_uint64_t feat_ctrl = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if (!(feat_ctrl & GRUB_MSR_X86_FEATURE_CTRL_LOCK)) + { + grub_dprintf ("slaunch", "Firmware didn't lock FEATURE_CONTROL MSR," + "locking it now\n"); + /* Not setting SENTER_FUNCTIONS and SENTER_ENABLE because they were tested + * in grub_txt_verify_platform() */ + feat_ctrl |= GRUB_MSR_X86_ENABLE_VMX_OUT_SMX | + GRUB_MSR_X86_ENABLE_VMX_IN_SMX | + GRUB_MSR_X86_FEATURE_CTRL_LOCK; + grub_wrmsr (GRUB_MSR_X86_FEATURE_CONTROL, feat_ctrl); + } + + /* Enable SMX mode. */ + grub_write_cr4 (grub_read_cr4 () | GRUB_CR4_X86_SMXE); + + caps = grub_txt_getsec_capabilities (0); + + if (!(caps & GRUB_SMX_CAPABILITY_CHIPSET_PRESENT)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("TXT-capable chipset is not present")); + goto fail; + } + + if (!(caps & GRUB_SMX_CAPABILITY_SENTER)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[SENTER] is not available")); + goto fail; + } + + if (!(caps & GRUB_SMX_CAPABILITY_PARAMETERS)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[PARAMETERS] is not available")); + goto fail; + } + + return GRUB_ERR_NONE; + + fail: + /* Disable SMX mode on failure. */ + grub_write_cr4 (grub_read_cr4 () & ~GRUB_CR4_X86_SMXE); + + return grub_errno; +} + +static grub_err_t +grub_txt_smx_parameters (struct grub_smx_parameters *params) +{ + grub_uint32_t index = 0, eax, ebx, ecx, param_type; + + grub_memset (params, 0, sizeof(*params)); + + params->max_acm_size = GRUB_SMX_DEFAULT_MAX_ACM_SIZE; + params->acm_memory_types = GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE; + params->senter_controls = GRUB_SMX_DEFAULT_SENTER_CONTROLS; + + do + { + grub_txt_getsec_parameters (index, &eax, &ebx, &ecx); + param_type = eax & GRUB_SMX_PARAMETER_TYPE_MASK; + + switch (param_type) + { + case GRUB_SMX_PARAMETER_NULL: + break; /* This means done. */ + + case GRUB_SMX_PARAMETER_ACM_VERSIONS: + if (params->version_count >= GRUB_SMX_PARAMETER_MAX_VERSIONS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Too many ACM versions")); + params->versions[params->version_count].mask = ebx; + params->versions[params->version_count++].version = ecx; + break; + + case GRUB_SMX_PARAMETER_MAX_ACM_SIZE: + params->max_acm_size = GRUB_SMX_GET_MAX_ACM_SIZE (eax); + break; + + case GRUB_SMX_PARAMETER_ACM_MEMORY_TYPES: + params->acm_memory_types = GRUB_SMX_GET_ACM_MEMORY_TYPES (eax); + break; + + case GRUB_SMX_PARAMETER_SENTER_CONTROLS: + params->senter_controls = GRUB_SMX_GET_SENTER_CONTROLS (eax); + break; + + case GRUB_SMX_PARAMETER_TXT_EXTENSIONS: + params->txt_feature_ext_flags = GRUB_SMX_GET_TXT_EXT_FEATURES (eax); + break; + + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SMX parameter")); + } + + ++index; + + } while (param_type != GRUB_SMX_PARAMETER_NULL); + + /* If no ACM versions were found, set the default one. */ + if (!params->version_count) + { + params->versions[0].mask = GRUB_SMX_DEFAULT_VERSION_MASK; + params->versions[0].version = GRUB_SMX_DEFAULT_VERSION; + params->version_count++; + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_txt_prepare_cpu (void) +{ + struct grub_smx_parameters params; + grub_uint32_t i; + grub_uint64_t mcg_cap, mcg_stat; + unsigned long cr0; + grub_err_t err; + + cr0 = grub_read_cr0 (); + + /* Cache must be enabled (CR0.CD = CR0.NW = 0). */ + cr0 &= ~(GRUB_CR0_X86_CD | GRUB_CR0_X86_NW); + + /* Native FPU error reporting must be enabled for proper interaction behavior. */ + cr0 |= GRUB_CR0_X86_NE; + + grub_write_cr0 (cr0); + + /* Disable virtual-8086 mode (EFLAGS.VM = 0). */ + grub_write_flags_register (grub_read_flags_register () & ~GRUB_EFLAGS_X86_VM); + + /* + * Verify all machine check status registers are clear (unless + * support preserving them). + */ + + /* Is machine check in progress? */ + if ( grub_rdmsr (GRUB_MSR_X86_MCG_STATUS) & GRUB_MSR_MCG_STATUS_MCIP ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("machine check in progress during secure launch")); + + err = grub_txt_smx_parameters (¶ms); + if (err != GRUB_ERR_NONE) + return err; + + if (params.txt_feature_ext_flags & GRUB_SMX_PROCESSOR_BASE_SCRTM) + grub_dprintf ("slaunch", "CPU supports processor-based S-CRTM\n"); + + if (params.txt_feature_ext_flags & GRUB_SMX_MACHINE_CHECK_HANDLING) + grub_dprintf ("slaunch", "CPU supports preserving machine check errors\n"); + else + { + grub_dprintf ("slaunch", "CPU does not support preserving machine check errors\n"); + + /* Check if all machine check registers are clear. */ + mcg_cap = grub_rdmsr (GRUB_MSR_X86_MCG_CAP); + for (i = 0; i < (mcg_cap & GRUB_MSR_MCG_BANKCNT_MASK); ++i) + { + mcg_stat = grub_rdmsr (GRUB_MSR_X86_MC0_STATUS + i * 4); + if (mcg_stat & (1ULL << 63)) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("secure launch MCG[%u] = %" PRIxGRUB_UINT64_T " ERROR"), i, + mcg_stat); + } + } + + return GRUB_ERR_NONE; +} + +static void +save_mtrrs (struct grub_slr_txt_mtrr_state *saved_bsp_mtrrs) +{ + grub_uint64_t i; + + saved_bsp_mtrrs->default_mem_type = + grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + + saved_bsp_mtrrs->mtrr_vcnt = + grub_rdmsr (GRUB_MSR_X86_MTRRCAP) & GRUB_MSR_X86_VCNT_MASK; + + if (saved_bsp_mtrrs->mtrr_vcnt > GRUB_TXT_VARIABLE_MTRRS_LENGTH) + { + /* Print warning but continue saving what we can... */ + grub_printf ("WARNING: Actual number of variable MTRRs (%" PRIuGRUB_UINT64_T + ") > GRUB_SL_MAX_VARIABLE_MTRRS (%d)\n", + saved_bsp_mtrrs->mtrr_vcnt, + GRUB_TXT_VARIABLE_MTRRS_LENGTH); + saved_bsp_mtrrs->mtrr_vcnt = GRUB_TXT_VARIABLE_MTRRS_LENGTH; + } + + for (i = 0; i < saved_bsp_mtrrs->mtrr_vcnt; ++i) + { + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = + grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (i)); + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = + grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (i)); + } + /* Zero unused array items. */ + for ( ; i < GRUB_TXT_VARIABLE_MTRRS_LENGTH; ++i) + { + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = 0; + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = 0; + } +} + +static void +set_all_mtrrs (int enable) +{ + grub_uint64_t mtrr_def_type; + + mtrr_def_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + + if ( enable ) + mtrr_def_type |= GRUB_MSR_X86_MTRR_ENABLE; + else + mtrr_def_type &= ~GRUB_MSR_X86_MTRR_ENABLE; + + grub_wrmsr (GRUB_MSR_X86_MTRR_DEF_TYPE, mtrr_def_type); +} + +#define SINIT_MTRR_MASK 0xFFFFFF /* SINIT requires 36b mask */ + +/* + * Note: bitfields in following structures are assumed to work on x86 and + * nothing else. All compilers supported by GRUB agree when it comes to layout + * of bits that is consistent with hardware implementation. It was decided to + * use bitfields for better readability instead of manual shifting and masking. + */ +union mtrr_physbase_t +{ + grub_uint64_t raw; + struct + { + grub_uint64_t type : 8; + grub_uint64_t reserved1 : 4; + grub_uint64_t base : 52; /* Define as max width and mask w/ */ + /* MAXPHYADDR when using */ + }; +} GRUB_PACKED; + +union mtrr_physmask_t +{ + grub_uint64_t raw; + struct + { + grub_uint64_t reserved1 : 11; + grub_uint64_t v : 1; /* valid */ + grub_uint64_t mask : 52; /* define as max width and mask w/ */ + /* MAXPHYADDR when using */ + }; +} GRUB_PACKED; + +static inline grub_uint32_t +bsrl (grub_uint32_t mask) +{ + grub_uint32_t result; + + asm ("bsrl %1,%0" : "=r" (result) : "rm" (mask) : "cc"); + + return result; +} + +static inline int +fls (int mask) +{ + return (mask == 0 ? mask : (int)bsrl ((grub_uint32_t)mask) + 1); +} + +/* + * Set the memory type for specified range (base to base+size) + * to mem_type and everything else to UC + */ +static grub_err_t +set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, + grub_uint32_t mem_type) +{ + grub_uint64_t mtrr_def_type; + grub_uint64_t mtrr_cap; + union mtrr_physbase_t mtrr_physbase; + union mtrr_physmask_t mtrr_physmask; + grub_uint32_t vcnt, pages_in_range; + unsigned long ndx, base_v; + int i = 0, j, num_pages, mtrr_s; + + /* Disable all fixed MTRRs, set default type to UC */ + mtrr_def_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + mtrr_def_type &= ~(GRUB_MSR_X86_MTRR_ENABLE_FIXED | GRUB_MSR_X86_DEF_TYPE_MASK); + mtrr_def_type |= GRUB_MTRR_MEMORY_TYPE_UC; + grub_wrmsr (GRUB_MSR_X86_MTRR_DEF_TYPE, mtrr_def_type); + + /* Initially disable all variable MTRRs (we'll enable the ones we use) */ + mtrr_cap = grub_rdmsr (GRUB_MSR_X86_MTRRCAP); + vcnt = (mtrr_cap & GRUB_MSR_X86_VCNT_MASK); + + for ( ndx = 0; ndx < vcnt; ndx++ ) + { + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.v = 0; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + } + + /* Map all AC module pages as mem_type */ + num_pages = GRUB_PAGE_UP(size) >> GRUB_PAGE_SHIFT; + + grub_dprintf ("slaunch", "setting MTRRs for acmod: base=%p, size=%x, num_pages=%d\n", + base, size, num_pages); + + /* + * Each VAR MTRR base must be a multiple of that MTRR's Size. + * grub_txt_sinit_select() made sure that base is at least 4K-aligned. + */ + base_v = (unsigned long)base; + /* MTRR size in pages */ + mtrr_s = 1; + + while ( (base_v & 0x01) == 0 ) + { + i++; + base_v = base_v >> 1; + } + + for (j = i - 12; j > 0; j--) + mtrr_s = mtrr_s*2; /* mtrr_s = mtrr_s << 1 */ + + grub_dprintf ("slaunch", "The maximum allowed MTRR range size=%d Pages \n", mtrr_s); + + ndx = 0; + + while ( num_pages >= mtrr_s ) + { + mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx)); + mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & + SINIT_MTRR_MASK; + mtrr_physbase.type = mem_type; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx), mtrr_physbase.raw); + + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.mask = ~(mtrr_s - 1) & SINIT_MTRR_MASK; + mtrr_physmask.v = 1; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + + base += (mtrr_s * GRUB_PAGE_SIZE); + num_pages -= mtrr_s; + ndx++; + if ( ndx == vcnt ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("exceeded number of var MTRRs when mapping range")); + } + + while ( num_pages > 0 ) + { + /* Set the base of the current MTRR */ + mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx)); + mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & + SINIT_MTRR_MASK; + mtrr_physbase.type = mem_type; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx), mtrr_physbase.raw); + + /* + * Calculate MTRR mask + * MTRRs can map pages in power of 2 + * may need to use multiple MTRRS to map all of region + */ + pages_in_range = 1 << (fls (num_pages) - 1); + + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.mask = ~(pages_in_range - 1) & SINIT_MTRR_MASK; + mtrr_physmask.v = 1; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + + /* + * Prepare for the next loop depending on number of pages + * We figure out from the above how many pages could be used in this + * mtrr. Then we decrement the count, increment the base, + * increment the mtrr we are dealing with, and if num_pages is + * still not zero, we do it again. + */ + base += (pages_in_range * GRUB_PAGE_SIZE); + num_pages -= pages_in_range; + ndx++; + if ( ndx == vcnt ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("exceeded number of var MTRRs when mapping range")); + } + + return GRUB_ERR_NONE; +} + +/* + * This must be done for each processor so that all have the same + * memory types + */ +static grub_err_t +set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) +{ + unsigned long eflags; + unsigned long cr0, cr4; + grub_err_t err; + + /* + * Need to do some things before we start changing MTRRs. + * + * Since this will modify some of the MTRRs, they should be saved first + * so that they can be restored once the AC mod is done. + */ + + /* Disable interrupts */ + eflags = grub_read_flags_register (); + grub_write_flags_register (eflags & ~GRUB_EFLAGS_X86_IF); + + /* Save CR0 then disable cache (CRO.CD=1, CR0.NW=0) */ + cr0 = grub_read_cr0 (); + grub_write_cr0 ( (cr0 & ~GRUB_CR0_X86_NW) | GRUB_CR0_X86_CD ); + + /* Flush caches */ + asm volatile ("wbinvd"); + + /* Save CR4 and disable global pages (CR4.PGE=0) */ + cr4 = grub_read_cr4 (); + grub_write_cr4 (cr4 & ~GRUB_CR4_X86_PGE); + + /* Disable MTRRs */ + set_all_mtrrs (0); + + /* Set MTRRs for AC mod and rest of memory */ + err = set_mtrr_mem_type ((grub_uint8_t*)hdr, hdr->size*4, + GRUB_MTRR_MEMORY_TYPE_WB); + + /* Undo some of earlier changes and enable our new settings */ + + /* Flush caches */ + asm volatile ("wbinvd"); + + /* Enable MTRRs */ + set_all_mtrrs (1); + + /* Restore CR0 (caching) */ + grub_write_cr0 (cr0); + + /* Restore CR4 (global pages) */ + grub_write_cr4 (cr4); + + /* Restore flags */ + grub_write_flags_register (eflags); + + return err; +} + +void +grub_txt_init_tpm_event_log (void *buf, grub_size_t size) +{ + struct grub_txt_event_log_container *elog; + + if (buf == NULL || size == 0) + return; + + /* For TPM 2.0 just clear the area, only TPM 1.2 requires initialization. */ + grub_memset (buf, 0, size); + + if (grub_get_tpm_ver () != GRUB_TPM_12) + return; + + elog = (struct grub_txt_event_log_container *) buf; + + grub_memcpy ((void *) elog->signature, EVTLOG_SIGNATURE, sizeof (elog->signature)); + elog->container_ver_major = EVTLOG_CNTNR_MAJOR_VER; + elog->container_ver_minor = EVTLOG_CNTNR_MINOR_VER; + elog->pcr_event_ver_major = EVTLOG_EVT_MAJOR_VER; + elog->pcr_event_ver_minor = EVTLOG_EVT_MINOR_VER; + elog->size = size; + elog->pcr_events_offset = sizeof (*elog); + elog->next_event_offset = sizeof (*elog); +} + +static void +setup_txt_slrt_entry (struct grub_slaunch_params *slparams, + struct grub_txt_os_mle_data *os_mle_data) +{ + struct grub_slr_table *slr_table = slparams->slr_table_mem; + struct grub_slr_entry_hdr *txt_info; + + grub_slr_add_entry (slr_table, &slr_intel_info_staging.hdr); + + txt_info = grub_slr_next_entry_by_tag (slr_table, NULL, GRUB_SLR_ENTRY_INTEL_INFO); + os_mle_data->txt_info = (grub_addr_t) slparams->slr_table_base + + ((grub_addr_t) txt_info - (grub_addr_t) slparams->slr_table_mem); +} + +/* + * Adds new element to the end. `size` does not include common header. + * Assume that heap was cleared and there is enough space to add the element. + */ +static inline struct grub_txt_heap_ext_data_element * +add_ext_data_elt (struct grub_txt_os_sinit_data *os_sinit_data, + grub_uint32_t type, grub_uint32_t size) +{ + struct grub_txt_heap_ext_data_element *elt = + (struct grub_txt_heap_ext_data_element *) os_sinit_data->ext_data_elts; + + while (elt->type != GRUB_TXT_HEAP_EXTDATA_TYPE_END) + elt = (struct grub_txt_heap_ext_data_element *)((grub_uint8_t *)elt + elt->size); + + elt->type = type; + elt->size = size + GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE; + + return elt; +} + +static grub_err_t +init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header *sinit) +{ + grub_uint8_t *txt_heap; + grub_uint32_t os_sinit_data_ver, sinit_caps; + grub_uint64_t *size; + grub_uint64_t size_total; + struct grub_txt_os_mle_data *os_mle_data; + struct grub_txt_os_sinit_data *os_sinit_data; + struct grub_txt_heap_ext_data_element *elt; +#ifdef GRUB_MACHINE_EFI + struct grub_acpi_rsdp_v20 *rsdp; +#endif + + /* BIOS data already verified in grub_txt_verify_platform(). */ + + txt_heap = grub_txt_get_heap (); + + grub_dprintf ("slaunch", "TXT heap %p\n", txt_heap); + + /* OS/loader to MLE data. */ + + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + grub_dprintf ("slaunch", "OS MLE data: %p\n", os_mle_data); + size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t); + + if (slparams->slr_table_base == GRUB_SLAUNCH_STORE_IN_OS2MLE) + { + /* SLRT needs to be at least 4-byte aligned per specification. */ + slparams->slr_table_base = + ALIGN_UP ((grub_addr_t) os_mle_data + sizeof (*os_mle_data), 4); + + /* Recompute size including SLRT table in it. */ + *size = (slparams->slr_table_base + slparams->slr_table_size) + - ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + + /* Size of heap sections should be a multiple of 8. */ + *size = ALIGN_UP (*size, 8); + } + + if (grub_add (grub_txt_bios_data_size (txt_heap), *size, &size_total) || + (size_total > grub_txt_get_heap_size ())) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsMleData")); + } + + grub_memset (os_mle_data, 0, sizeof (*os_mle_data)); + + os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION; + os_mle_data->boot_params_addr = slparams->boot_params_addr; + os_mle_data->slrt = slparams->slr_table_base; + + os_mle_data->ap_wake_block = slparams->ap_wake_block; + os_mle_data->ap_wake_block_size = slparams->ap_wake_block_size; + + /* Setup the TXT specific SLR information */ + slr_intel_info_staging.hdr.tag = GRUB_SLR_ENTRY_INTEL_INFO; + slr_intel_info_staging.hdr.size = sizeof(struct grub_slr_entry_intel_info); + slr_intel_info_staging.saved_misc_enable_msr = + grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE); + + /* Save the BSPs MTRR state so post launch can restore it. */ + grub_dprintf ("slaunch", "Saving MTRRs to OS MLE data\n"); + save_mtrrs (&slr_intel_info_staging.saved_bsp_mtrrs); + + /* OS/loader to SINIT data. */ + grub_dprintf ("slaunch", "Get supported OS SINIT data version\n"); + os_sinit_data_ver = grub_txt_supported_os_sinit_data_ver (sinit); + + if (os_sinit_data_ver < OS_SINIT_DATA_MIN_VER) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("unsupported OS to SINIT data version in SINIT ACM: %d" + " expected >= %d"), os_sinit_data_ver, OS_SINIT_DATA_MIN_VER); + + os_sinit_data = grub_txt_os_sinit_data_start (txt_heap); + grub_dprintf ("slaunch", "OS SINIT data: %p\n", os_sinit_data); + size = (grub_uint64_t *) ((grub_addr_t) os_sinit_data - sizeof (grub_uint64_t)); + + *size = sizeof(grub_uint64_t) + sizeof (struct grub_txt_os_sinit_data) + + GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE /* End element */; + + if (grub_get_tpm_ver () == GRUB_TPM_12) + *size += GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_tpm_event_log_element); + else if (grub_get_tpm_ver () == GRUB_TPM_20) + *size += GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_event_log_pointer2_1_element); + else + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM version")); + + if (grub_add (size_total, *size, &size_total) || + (size_total > grub_txt_get_heap_size ())) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsSinitData")); + } + + grub_memset (os_sinit_data, 0, *size); + +#ifdef GRUB_MACHINE_EFI + rsdp = grub_acpi_get_rsdpv2 (); + + if (rsdp == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("ACPI RSDP 2.0 missing\n")); + + os_sinit_data->efi_rsdp_ptr = (grub_uint64_t)(grub_addr_t) rsdp; +#endif + + os_sinit_data->mle_ptab = slparams->mle_ptab_target; + os_sinit_data->mle_size = slparams->mle_size; + + os_sinit_data->mle_hdr_base = slparams->mle_header_offset; + + /* TODO: Check low PMR with RMRR. Look at relevant tboot code too. */ + /* TODO: Kernel should not allocate any memory outside of PMRs regions!!! */ + os_sinit_data->vtd_pmr_lo_base = 0; + os_sinit_data->vtd_pmr_lo_size = ALIGN_DOWN (grub_mmap_get_highest (0x100000000), + GRUB_TXT_PMR_ALIGN); + + os_sinit_data->vtd_pmr_hi_base = ALIGN_UP (grub_mmap_get_lowest (0x100000000), + GRUB_TXT_PMR_ALIGN); + os_sinit_data->vtd_pmr_hi_size = ALIGN_DOWN (grub_mmap_get_highest (0xffffffffffffffff), + GRUB_TXT_PMR_ALIGN); + os_sinit_data->vtd_pmr_hi_size -= os_sinit_data->vtd_pmr_hi_base; + + grub_dprintf ("slaunch", + "vtd_pmr_lo_base: 0x%" PRIxGRUB_UINT64_T " vtd_pmr_lo_size: 0x%" + PRIxGRUB_UINT64_T " vtd_pmr_hi_base: 0x%" PRIxGRUB_UINT64_T + " vtd_pmr_hi_size: 0x%" PRIxGRUB_UINT64_T "\n", + os_sinit_data->vtd_pmr_lo_base, os_sinit_data->vtd_pmr_lo_size, + os_sinit_data->vtd_pmr_hi_base, os_sinit_data->vtd_pmr_hi_size); + + sinit_caps = grub_txt_get_sinit_capabilities (sinit); + + grub_dprintf ("slaunch", "SINIT capabilities %08x\n", sinit_caps); + + /* + * In the latest TXT Software Development Guide as of now (April 2023, + * Revision 017.4) bits 4 and 5 (used to be "no legacy PCR usage" and + * "auth PCR usage" respectively) of capabilities field bit are reserved. + * Bit 4 is ignored, but it is returned as 0 by SINIT[CAPABILITIES], + * while bit 5 is forced to 1 for compatibility reasons. This is related + * to support for TPM 1.2 devices, which always had this bit set. + * + * There are TPM 2.0 platforms that will have both bits set. TXT + * specification doesn't specify when this has changed, so we can't + * reliably test for those. + */ + os_sinit_data->capabilities = GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE; + + if (grub_get_tpm_ver () == GRUB_TPM_20) + { + if ((sinit_caps & os_sinit_data->capabilities) != os_sinit_data->capabilities) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Details/authorities PCR usage is not supported")); + } + else + { + if (!(sinit_caps & GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE)) + { + grub_dprintf ("slaunch", "Details/authorities PCR usage is not supported. Trying legacy"); + if (sinit_caps & GRUB_TXT_CAPS_TPM_12_NO_LEGACY_PCR_USAGE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Not a single PCR usage available in SINIT capabilities")); + + os_sinit_data->capabilities = 0; + } + } + + /* + * APs (application processors) can't be brought up by usual INIT-SIPI-SIPI + * sequence after Measured Launch, otherwise the MLE integrity is lost. + * Choose monitor RLP (responding logical processor, fancy name for AP) wakeup + * mechanism first, if that isn't supported fall back to GETSEC[WAKEUP]. + */ + if (sinit_caps & GRUB_TXT_CAPS_MONITOR_SUPPORT) + os_sinit_data->capabilities |= GRUB_TXT_CAPS_MONITOR_SUPPORT; + else if (sinit_caps & GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT) + os_sinit_data->capabilities |= GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("lack of RLP wakeup mechanism")); + + if (sinit_caps & GRUB_TXT_CAPS_ECX_PT_SUPPORT) + os_sinit_data->capabilities |= GRUB_TXT_CAPS_ECX_PT_SUPPORT; + + if (grub_get_tpm_ver () == GRUB_TPM_12) + { + grub_dprintf ("slaunch", "TPM 1.2 detected\n"); + grub_dprintf ("slaunch", "Setting up TXT HEAP TPM event log element\n"); + os_sinit_data->flags = GRUB_TXT_PCR_EXT_MAX_PERF_POLICY; + os_sinit_data->version = OS_SINIT_DATA_TPM_12_VER; + + elt = add_ext_data_elt(os_sinit_data, + GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR, + sizeof (struct grub_txt_heap_tpm_event_log_element)); + elt->tpm_event_log.event_log_phys_addr = slparams->tpm_evt_log_base; + } + else + { + grub_dprintf ("slaunch", "TPM 2.0 detected\n"); + grub_dprintf ("slaunch", "Setting up TXT HEAP TPM event log element\n"); + if (!(sinit_caps & GRUB_TXT_CAPS_TPM_20_EVTLOG_SUPPORT)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("original TXT TPM 2.0 event log format is not supported")); + + os_sinit_data->capabilities |= GRUB_TXT_CAPS_TPM_20_EVTLOG_SUPPORT; + + os_sinit_data->flags = GRUB_TXT_PCR_EXT_MAX_PERF_POLICY; + + os_sinit_data->version = OS_SINIT_DATA_TPM_20_VER; + + elt = add_ext_data_elt(os_sinit_data, + GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1, + sizeof (struct grub_txt_heap_event_log_pointer2_1_element)); + elt->event_log_pointer2_1.phys_addr = slparams->tpm_evt_log_base; + elt->event_log_pointer2_1.allocated_event_container_size = slparams->tpm_evt_log_size; + } + + elt = add_ext_data_elt(os_sinit_data, GRUB_TXT_HEAP_EXTDATA_TYPE_END, 0); + + if ((grub_uint8_t *)elt + elt->size > + (grub_uint8_t *)grub_txt_sinit_mle_data_start (txt_heap) - sizeof(grub_uint64_t)) + return grub_error (GRUB_ERR_BUG, + N_("error in OsSinitData size requirements calculation")); + + /* SinitMleDataSize isn't known at this point, it is crafted by SINIT ACM. + * We can only test if size field fits and hope that ACM checks the rest. */ + if (grub_add (size_total, sizeof (grub_uint64_t), &size_total) || + (size_total > grub_txt_get_heap_size ())) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for SinitMleDataSize")); + + grub_dprintf ("slaunch", "TXT HEAP init done\n"); + + return GRUB_ERR_NONE; +} + +/* + * TODO: Why 1 GiB limit? It does not seem that it is required by TXT spec. + * If there is a limit then it should be checked before allocation and image load. + * + * If enough room is available in front of the MLE, the maximum size of an + * MLE that can be covered is 1G. This is due to having 512 PDEs pointing + * to 512 page tables with 512 PTEs each. + */ +grub_uint32_t +grub_txt_get_mle_ptab_size (grub_uint32_t mle_size) +{ + /* + * #PT + 1 PT + #PD + 1 PD + 1 PDT + * + * Why do we need 2 extra PTEs and PDEs? Because MLE image may not + * start and end at PTE (page) and PDE (2 MiB) boundary... + */ + return ((((mle_size / GRUB_PAGE_SIZE) + 2) / 512) /* Number of PTs */ + + 1 /* PT */ + + (((mle_size / (512 * GRUB_PAGE_SIZE)) + 2) / 512) /* Number of PDs */ + + 1 /* PD */ + + 1) /* PDT */ + * GRUB_PAGE_SIZE; +} + +/* Page directory and table entries only need Present set */ +#define MAKE_PT_MLE_ENTRY(addr) (((grub_uint64_t)(grub_addr_t)(addr) & GRUB_PAGE_MASK) | 0x01) + +/* + * The MLE page tables have to be below the MLE and have no special regions in + * between them and the MLE (this is a bit of an unwritten rule). + * 20 pages are carved out of memory below the MLE. That leave 18 page table + * pages that can cover up to 36M . + * can only contain 4k pages + * + * TODO: TXT Spec p.32; List section name and number with PT MLE requirements here. + * + * TODO: This function is not able to cover MLEs larger than 1 GiB. Fix it!!! + * After fixing increase GRUB_TXT_MLE_MAX_SIZE too. + */ +void +grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams) +{ + grub_uint8_t *pg_dir, *pg_dir_ptr_tab = slparams->mle_ptab_mem, *pg_tab; + grub_uint32_t mle_off = 0, pd_off = 0; + grub_uint64_t *pde, *pte; + + grub_memset (pg_dir_ptr_tab, 0, slparams->mle_ptab_size); + + pg_dir = pg_dir_ptr_tab + GRUB_PAGE_SIZE; + pg_tab = pg_dir + GRUB_PAGE_SIZE; + + /* Only use first entry in page dir ptr table */ + *(grub_uint64_t *)pg_dir_ptr_tab = MAKE_PT_MLE_ENTRY(pg_dir); + + /* Start with first entry in page dir */ + *(grub_uint64_t *)pg_dir = MAKE_PT_MLE_ENTRY(pg_tab); + + pte = (grub_uint64_t *)pg_tab; + pde = (grub_uint64_t *)pg_dir; + + do + { + /* mle_start may be unaligned, handled by mask in MAKE_PT_MLE_ENTRY */ + *pte = MAKE_PT_MLE_ENTRY(slparams->mle_start + mle_off); + + pte++; + mle_off += GRUB_PAGE_SIZE; + + if (!(++pd_off % 512)) + { + /* Break if we don't need any additional page entries */ + if (mle_off >= slparams->mle_size) + break; + pde++; + *pde = MAKE_PT_MLE_ENTRY(pte); + } + /* Add one page in case mle_start isn't aligned */ + } while (mle_off - GRUB_PAGE_SIZE + 1 < slparams->mle_size); +} + +grub_err_t +grub_txt_init (void) +{ + grub_err_t err; + + err = grub_txt_verify_platform (); + + if (err != GRUB_ERR_NONE) + return err; + + err = enable_smx_mode (); + + if (err != GRUB_ERR_NONE) + return err; + + return GRUB_ERR_NONE; +} + +void +grub_txt_shutdown (void) +{ + /* Disable SMX mode. */ + grub_write_cr4 (grub_read_cr4 () & ~GRUB_CR4_X86_SMXE); +} + +void +grub_txt_state_show (void) +{ + union { + grub_uint64_t d64; + grub_uint32_t d32; + grub_uint8_t d8; + grub_uint8_t a8[8]; + } data; + int i; + union grub_txt_didvid didvid; + + data.d64 = grub_txt_reg_pub_read64 (GRUB_TXT_STS); + grub_printf (" TXT.STS: 0x%016" PRIxGRUB_UINT64_T "\n" + " SENTER.DONE.STS: %d\n" + " SEXIT.DONE.STS: %d\n" + " MEM-CONFIGLOCK.STS: %d\n" + " PRIVATEOPEN.STS: %d\n" + " TXT.LOCALITY1.OPEN.STS: %d\n" + " TXT.LOCALITY2.OPEN.STS: %d\n", + data.d64, !!(data.d64 & GRUB_TXT_STS_SENTER_DONE), + !!(data.d64 & GRUB_TXT_STS_SEXIT_DONE), + !!(data.d64 & GRUB_TXT_STS_MEM_CONFIG_LOCK), + !!(data.d64 & GRUB_TXT_STS_PRIVATE_OPEN), + !!(data.d64 & GRUB_TXT_STS_LOCALITY1_OPEN), + !!(data.d64 & GRUB_TXT_STS_LOCALITY2_OPEN)); + + /* Only least significant byte has a meaning. */ + data.d8 = grub_txt_reg_pub_read8 (GRUB_TXT_ESTS); + grub_printf (" TXT.ESTS: 0x%02x\n" + " TXT_RESET.STS: %d\n", data.d8, + !!(data.d8 & GRUB_TXT_ESTS_TXT_RESET)); + + data.d64 = grub_txt_reg_pub_read64 (GRUB_TXT_E2STS); + grub_printf (" TXT.E2STS: 0x%016" PRIxGRUB_UINT64_T "\n" + " SECRETS.STS: %d\n", data.d64, + !!(data.d64 & GRUB_TXT_E2STS_SECRETS)); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_ERRORCODE); + grub_printf (" TXT.ERRORCODE: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + didvid.value = grub_txt_reg_pub_read64 (GRUB_TXT_DIDVID); + grub_printf (" TXT.DIDVID: 0x%016" PRIxGRUB_UINT64_T "\n" + " VID: 0x%04x\n" + " DID: 0x%04x\n" + " RID: 0x%04x\n" + " ID-EXT: 0x%04x\n", + didvid.value, didvid.vid, didvid.did, didvid.rid, didvid.id_ext); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_VER_FSBIF); + grub_printf (" TXT.VER.FSBIF: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + if ((data.d32 != 0x00000000) && (data.d32 != 0xffffffff)) + grub_printf (" DEBUG.FUSE: %d\n", !!(data.d32 & GRUB_TXT_VER_FSBIF_DEBUG_FUSE)); + else + { + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_VER_QPIIF); + grub_printf (" TXT.VER.QPIIF: 0x%08" PRIxGRUB_UINT32_T "\n" + " DEBUG.FUSE: %d\n", data.d32, + !!(data.d32 & GRUB_TXT_VER_QPIIF_DEBUG_FUSE)); + } + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_BASE); + grub_printf (" TXT.SINIT.BASE: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_SIZE); + grub_printf (" TXT.SINIT.SIZE: %" PRIuGRUB_UINT32_T + " B (0x%" PRIxGRUB_UINT32_T ")\n", data.d32, data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); + grub_printf (" TXT.HEAP.BASE: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); + grub_printf (" TXT.HEAP.SIZE: %" PRIuGRUB_UINT32_T + " B (0x%" PRIxGRUB_UINT32_T ")\n", data.d32, data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_DPR); + grub_printf (" TXT.DPR: 0x%08" PRIxGRUB_UINT32_T "\n" + " LOCK: %d\n" + " TOP: 0x%08" PRIxGRUB_UINT32_T "\n" + " SIZE: %" PRIuGRUB_UINT32_T " MiB\n", + data.d32, !!(data.d32 & (1 << 0)), (data.d32 & 0xfff00000), + (data.d32 & 0x00000ff0) >> 4); + + grub_printf (" TXT.PUBLIC.KEY:\n"); + + for (i = 0; i < 4; ++i) + { + /* TODO: Check relevant MSRs on SGX platforms. */ + data.d64 = grub_txt_reg_pub_read64 (GRUB_TXT_PUBLIC_KEY + i * sizeof (grub_uint64_t)); + grub_printf (" %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x%s", data.a8[0], data.a8[1], + data.a8[2], data.a8[3], data.a8[4], data.a8[5], data.a8[6], data.a8[7], + (i < 3) ? ":\n" : "\n"); + } +} + +grub_err_t +grub_txt_boot_prepare (struct grub_slaunch_params *slparams) +{ + grub_err_t err; + grub_uint8_t *txt_heap; + struct grub_txt_os_mle_data *os_mle_data; + struct grub_txt_acm_header *sinit_base; + + sinit_base = grub_txt_sinit_select (grub_slaunch_module ()); + + if (sinit_base == NULL) + return grub_errno; + + grub_dprintf ("slaunch", "Init TXT heap\n"); + err = init_txt_heap (slparams, sinit_base); + + if (err != GRUB_ERR_NONE) + return err; + + grub_dprintf ("slaunch", "TXT heap successfully prepared\n"); + + slparams->dce_base = (grub_uint32_t)(grub_addr_t) sinit_base; + slparams->dce_size = sinit_base->size * 4; + + /* Setup of SLR table. */ + grub_slaunch_init_slrt_storage (GRUB_SLR_INTEL_TXT); + txt_heap = grub_txt_get_heap (); + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + setup_txt_slrt_entry (slparams, os_mle_data); + + grub_tpm_relinquish_locality (0); + grub_dprintf ("slaunch", "Relinquished TPM locality 0\n"); + + err = set_mtrrs_for_acmod (sinit_base); + if (err) + return grub_error (err, N_("secure launch failed to set MTRRs for ACM")); + + grub_dprintf ("slaunch", "MTRRs set for ACMOD\n"); + + err = grub_txt_prepare_cpu (); + if ( err ) + return err; + + grub_dprintf ("slaunch", "CPU prepared for secure launch\n"); + + if (!(grub_rdmsr (GRUB_MSR_X86_APICBASE) & GRUB_MSR_X86_APICBASE_BSP)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("secure launch must run on BSP")); + + return GRUB_ERR_NONE; +} + +void +grub_txt_add_slrt_policy_entries (void) +{ + struct grub_txt_os_mle_data *os_mle_data; + grub_uint8_t *txt_heap; + + txt_heap = grub_txt_get_heap (); + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_TXT_OS2MLE, + /*flags=*/0, + (grub_addr_t) os_mle_data, + sizeof(*os_mle_data), + "Measured TXT OS-MLE data"); +} diff --git a/grub-core/loader/i386/txt/verify.c b/grub-core/loader/i386/txt/verify.c new file mode 100644 index 000000000..26e21d7ec --- /dev/null +++ b/grub-core/loader/i386/txt/verify.c @@ -0,0 +1,277 @@ +/* + * verify.c: verify that platform and processor supports Intel(r) TXT + * + * Copyright (c) 2003-2010, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the Intel Corporation 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Current max that the secure launch can handle */ +#define TXT_MAX_CPUS 512 + +static grub_err_t +verify_bios_spec_ver_elt (struct grub_txt_heap_ext_data_element *elt) +{ + if (elt->size != GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_bios_spec_ver_element)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_BIOS_SPEC_VER element has wrong size (%d)"), + elt->size); + + /* Any values are allowed */ + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_acm_elt (struct grub_txt_heap_ext_data_element *elt) +{ + grub_uint64_t acm_addrs_size; + grub_uint32_t i; + + if (elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_acm_element) || + grub_mul (elt->acm.num_acms, sizeof (grub_uint64_t), &acm_addrs_size) || + elt->size - (GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + sizeof (elt->acm)) != + acm_addrs_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element has wrong size (%d)"), + elt->size); + + /* No addrs is not error, but print warning. */ + if (elt->acm.num_acms == 0) + grub_printf ("WARNING: HEAP_ACM element has no ACM addrs\n"); + + for (i = 0; i < elt->acm.num_acms; i++) + { + if (elt->acm.addr[i] == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element ACM addr (%d) is NULL"), i); + + if (elt->acm.addr[i] >= 0x100000000UL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element ACM addr (%d) is >4GB"), i); + + /* Not going to check if ACM addrs are valid ACMs */ + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_custom_elt (struct grub_txt_heap_ext_data_element *elt) +{ + if (elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_custom_element)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_CUSTOM element has wrong size (%d)"), + elt->size); + + /* Any values are allowed */ + + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_ext_data_elts(struct grub_txt_heap_ext_data_element *elts, + grub_uint64_t elts_size) +{ + struct grub_txt_heap_ext_data_element *elt = elts; + grub_err_t err; + + if (elts_size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("TXT heap ext data elements too small")); + + for ( ; ; ) + { + if (elts_size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE || + elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE || + elts_size < elt->size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("TXT heap invalid element size: type: %d, size: %d"), + elt->type, elt->size); + + switch (elt->type) + { + case GRUB_TXT_HEAP_EXTDATA_TYPE_END: + return GRUB_ERR_NONE; + case GRUB_TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER: + err = verify_bios_spec_ver_elt (elt); + if (err != GRUB_ERR_NONE) + return err; + break; + case GRUB_TXT_HEAP_EXTDATA_TYPE_ACM: + err = verify_acm_elt (elt); + if (err != GRUB_ERR_NONE) + return err; + break; + case GRUB_TXT_HEAP_EXTDATA_TYPE_STM: + /* Nothing to check, platform specific */ + break; + case GRUB_TXT_HEAP_EXTDATA_TYPE_CUSTOM: + err = verify_custom_elt (elt); + if (err != GRUB_ERR_NONE) + return err; + break; + /* These shouldn't be present in BIOS data, treat them as errors */ + case GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR: + case GRUB_TXT_HEAP_EXTDATA_TYPE_MADT: + case GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1: + case GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG: + default: + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("unknown element: type: %u, size: %u\n"), + elt->type, elt->size); + } + + elts_size -= elt->size; + elt = (struct grub_txt_heap_ext_data_element *)((grub_uint8_t *) elt + elt->size); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_txt_verify_platform (void) +{ + grub_uint8_t *txt_heap; + grub_uint32_t eax, ebx, ecx, edx, errorcode, heap_base, heap_size; + grub_uint64_t bios_size, msr; + grub_err_t err = GRUB_ERR_NONE; + struct grub_txt_bios_data *bios_data; + struct grub_txt_heap_ext_data_element *elts; + + grub_cpuid (GRUB_X86_CPUID_FEATURES, eax, ebx, ecx, edx); + + if (!(ecx & GRUB_X86_CPUID_FEATURES_ECX_SMX)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPU does not support SMX")); + + msr = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if ((msr & (GRUB_MSR_X86_SENTER_FUNCTIONS | GRUB_MSR_X86_SENTER_ENABLE)) != + (GRUB_MSR_X86_SENTER_FUNCTIONS | GRUB_MSR_X86_SENTER_ENABLE)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[SENTER] is not enabled")); + + /* + * TODO + * TXT Specification + * 4.5 SGX Requirement for TXT Platform + * Secure Launch currently does not support interop with SGX since it does + * not have TPM support to write the SE NVRAM index. + * Eventually need the verify_IA32_se_svn_status routine to be called here. + */ + + errorcode = grub_txt_reg_pub_read32 (GRUB_TXT_ERRORCODE); + /* 0 - no previous SENTER, 0xC0000001 - previous SENTER succeeded */ + if (errorcode != 0 && errorcode != 0xC0000001) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT_ERRORCODE reports failure: 0x%08" PRIxGRUB_UINT32_T), + errorcode); + + if (grub_txt_reg_pub_read8 (GRUB_TXT_ESTS) & GRUB_TXT_ESTS_TXT_RESET) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT_RESET.STS is set and GETSEC[SENTER] is disabled")); + + /* + * Verify that the BIOS information in the TXT heap that was setup by the + * BIOS ACM is reasonable. + */ + + txt_heap = grub_txt_get_heap (); + heap_base = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); + heap_size = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); + + if (txt_heap == NULL || heap_base == 0 || heap_size == 0) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT heap is not configured correctly")); + + bios_size = grub_txt_bios_data_size (txt_heap); + if (bios_size < sizeof (grub_uint64_t) + sizeof (*bios_data) || + bios_size > heap_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("invalid size of the TXT heap BIOS data table")); + + bios_data = grub_txt_bios_data_start (txt_heap); + + /* Check version */ + if (bios_data->version < 3) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("unsupported BIOS data version (%d)"), bios_data->version); + + if (bios_data->num_logical_procs > TXT_MAX_CPUS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("BIOS reports too many CPUs for secure launch (%d)"), + bios_data->num_logical_procs); + + /* + * grub_uint32_t mle_flags is supposed to be added in version 5, however, the + * only ACM in 630744_003 package that is version 4 (Sandy Bridge & Ivy Bridge + * SNB_IVB_SINIT_20190708_PW.bin) seems to also have this field, or at least + * a placeholder for it. + */ + if (bios_data->version >= 4 && bios_size > sizeof (*bios_data) + sizeof (bios_size)) + { + elts = (struct grub_txt_heap_ext_data_element *) ((grub_uint8_t *) bios_data + + sizeof (*bios_data)); + err = verify_ext_data_elts(elts, bios_size - sizeof (*bios_data)); + } + + return err; +} diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c index 3b856e842..dcdf005df 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -92,8 +92,7 @@ static struct xen_loader_state xen_state; static grub_dl_t my_mod; -#define PAGE_SIZE (1UL << PAGE_SHIFT) -#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) +#define MAX_MODULES (GRUB_PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) #define STACK_SIZE 1048576 #define ADDITIONAL_SIZE (1 << 19) #define ALIGN_SIZE (1 << 22) @@ -103,7 +102,7 @@ static grub_dl_t my_mod; static grub_uint64_t page2offset (grub_uint64_t page) { - return page << PAGE_SHIFT; + return page << GRUB_PAGE_SHIFT; } static grub_err_t @@ -142,7 +141,7 @@ get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn) continue; } - bits = PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; + bits = GRUB_PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; mask = (1ULL << bits) - 1; map->lvls[i].virt_start = map->area.virt_start & ~mask; map->lvls[i].virt_end = map->area.virt_end | mask; @@ -229,7 +228,7 @@ generate_page_table (grub_xen_mfn_t *mfn_list) for (m1 = 0; m1 < xen_state.n_mappings; m1++) grub_memset (xen_state.mappings[m1].where, 0, - xen_state.mappings[m1].area.n_pt_pages * PAGE_SIZE); + xen_state.mappings[m1].area.n_pt_pages * GRUB_PAGE_SIZE); for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--) { @@ -247,11 +246,11 @@ generate_page_table (grub_xen_mfn_t *mfn_list) if (lvl->virt_start >= end || lvl->virt_end <= start) continue; p_s = (grub_max (start, lvl->virt_start) - start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); p_e = (grub_min (end, lvl->virt_end) - start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; grub_dprintf ("xen", "write page table entries level %d pg %p " "mapping %d/%d index %lx-%lx pfn %llx\n", l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn); @@ -324,21 +323,21 @@ grub_xen_p2m_alloc (void) map = xen_state.mappings + xen_state.n_mappings; p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) * - grub_xen_start_page_addr->nr_pages, PAGE_SIZE); + grub_xen_start_page_addr->nr_pages, GRUB_PAGE_SIZE); if (xen_state.xen_inf.has_p2m_base) { err = get_pgtable_size (xen_state.xen_inf.p2m_base, xen_state.xen_inf.p2m_base + p2msize, - (xen_state.max_addr + p2msize) >> PAGE_SHIFT); + (xen_state.max_addr + p2msize) >> GRUB_PAGE_SHIFT); if (err) return err; - map->area.pfn_start = xen_state.max_addr >> PAGE_SHIFT; + map->area.pfn_start = xen_state.max_addr >> GRUB_PAGE_SHIFT; p2malloc = p2msize + page2offset (map->area.n_pt_pages); xen_state.n_mappings++; xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base; xen_state.next_start.first_p2m_pfn = map->area.pfn_start; - xen_state.next_start.nr_p2m_frames = p2malloc >> PAGE_SHIFT; + xen_state.next_start.nr_p2m_frames = p2malloc >> GRUB_PAGE_SHIFT; } else { @@ -380,9 +379,9 @@ grub_xen_special_alloc (void) xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base; xen_state.virt_start_info = get_virtual_current_address (ch); xen_state.max_addr = - ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE); - xen_state.console_pfn = xen_state.max_addr >> PAGE_SHIFT; - xen_state.max_addr += 2 * PAGE_SIZE; + ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), GRUB_PAGE_SIZE); + xen_state.console_pfn = xen_state.max_addr >> GRUB_PAGE_SHIFT; + xen_state.max_addr += 2 * GRUB_PAGE_SIZE; xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages; grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic, @@ -413,7 +412,7 @@ grub_xen_pt_alloc (void) xen_state.next_start.pt_base = xen_state.max_addr + xen_state.xen_inf.virt_base; - nr_info_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_info_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT; nr_need_pages = nr_info_pages; while (1) @@ -431,9 +430,9 @@ grub_xen_pt_alloc (void) /* Map the relocator page either at virtual 0 or after end of area. */ nr_need_pages = nr_info_pages + map->area.n_pt_pages; if (xen_state.xen_inf.virt_base) - err = get_pgtable_size (0, PAGE_SIZE, nr_need_pages); + err = get_pgtable_size (0, GRUB_PAGE_SIZE, nr_need_pages); else - err = get_pgtable_size (try_virt_end, try_virt_end + PAGE_SIZE, + err = get_pgtable_size (try_virt_end, try_virt_end + GRUB_PAGE_SIZE, nr_need_pages); if (err) return err; @@ -461,7 +460,7 @@ grub_xen_pt_alloc (void) xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base; xen_state.next_start.nr_pt_frames = nr_need_pages; xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base; - xen_state.pgtbl_end = xen_state.max_addr >> PAGE_SHIFT; + xen_state.pgtbl_end = xen_state.max_addr >> GRUB_PAGE_SHIFT; xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where + page2offset (map->area.n_pt_pages)); @@ -515,7 +514,7 @@ grub_xen_boot (void) if (err) return err; - nr_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT; grub_dprintf ("xen", "bootstrap domain %llx+%llx\n", (unsigned long long) xen_state.xen_inf.virt_base, @@ -538,7 +537,7 @@ grub_xen_boot (void) return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages, xen_state.xen_inf.virt_base < - PAGE_SIZE ? page2offset (nr_pages) : 0, + GRUB_PAGE_SIZE ? page2offset (nr_pages) : 0, xen_state.pgtbl_end - 1, page2offset (xen_state.pgtbl_end - 1) + xen_state.xen_inf.virt_base); @@ -677,7 +676,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), goto fail; } - if (xen_state.xen_inf.virt_base & (PAGE_SIZE - 1)) + if (xen_state.xen_inf.virt_base & (GRUB_PAGE_SIZE - 1)) { grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base"); goto fail; @@ -700,10 +699,10 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page - xen_state.xen_inf.virt_base); kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page - - xen_state.xen_inf.virt_base + PAGE_SIZE); + xen_state.xen_inf.virt_base + GRUB_PAGE_SIZE); } - xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (kern_end, GRUB_PAGE_SIZE); if (grub_sub (kern_end, kern_start, &sz)) @@ -730,7 +729,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), if (xen_state.xen_inf.has_hypercall_page) { unsigned i; - for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) + for (i = 0; i < GRUB_PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) set_hypercall_interface ((grub_uint8_t *) kern_chunk_src + i * HYPERCALL_INTERFACE_SIZE + xen_state.xen_inf.hypercall_page - @@ -818,7 +817,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), if (xen_state.xen_inf.unmapped_initrd) { xen_state.next_start.flags |= SIF_MOD_START_PFN; - xen_state.next_start.mod_start = xen_state.max_addr >> PAGE_SHIFT; + xen_state.next_start.mod_start = xen_state.max_addr >> GRUB_PAGE_SHIFT; } else xen_state.next_start.mod_start = @@ -828,7 +827,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base), (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: grub_initrd_close (&initrd_ctx); @@ -882,7 +881,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), { xen_state.xen_inf.unmapped_initrd = 0; xen_state.n_modules = 0; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); xen_state.modules_target_start = xen_state.max_addr; xen_state.next_start.mod_start = xen_state.max_addr + xen_state.xen_inf.virt_base; @@ -902,7 +901,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), MAX_MODULES * sizeof (xen_state.module_info_page[0]); } - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)); @@ -925,7 +924,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.module_info_page[xen_state.n_modules].cmdline = xen_state.max_addr - xen_state.modules_target_start; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, GRUB_PAGE_SIZE); if (size) { @@ -952,7 +951,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.n_modules++; grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", (unsigned) xen_state.max_addr, (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c index b91e2f840..ba73d909a 100644 --- a/grub-core/loader/i386/xnu.c +++ b/grub-core/loader/i386/xnu.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -802,6 +803,7 @@ grub_xnu_boot_resume (void) { struct grub_relocator32_state state = {0}; + state.edi = SLP_NONE; state.esp = grub_xnu_stack; state.ebp = grub_xnu_stack; state.eip = grub_xnu_entry_point; @@ -1129,6 +1131,7 @@ grub_xnu_boot (void) grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size, descriptor_version, memory_map); + state.edi = SLP_NONE; state.eip = grub_xnu_entry_point; state.eax = grub_xnu_arg1; state.esp = grub_xnu_stack; diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 94c112a29..58a8952be 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -51,6 +51,11 @@ #include #include #include +#if defined (__i386__) || defined (__x86_64__) +#include +#include +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -162,6 +167,22 @@ efi_boot (struct grub_relocator *rel __attribute__ ((unused)), static void normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state) { + struct grub_slaunch_params *slparams = grub_slaunch_params(); + state.edi = grub_slaunch_platform_type (); + + if (state.edi == SLP_INTEL_TXT) + { + /* Configure relocator GETSEC[SENTER] call. */ + state.eax = GRUB_SMX_LEAF_SENTER; + state.ebx = slparams->dce_base; + state.ecx = slparams->dce_size; + state.edx = 0; + } + else if (state.edi == SLP_AMD_SKINIT) + { + state.eax = slparams->dce_base; + } + grub_relocator32_boot (rel, state, 0); } #else @@ -176,6 +197,7 @@ static grub_err_t grub_multiboot_boot (void) { grub_err_t err; + grub_uint32_t mbi_size; #ifdef GRUB_USE_MULTIBOOT2 struct grub_relocator32_state state = MULTIBOOT2_INITIAL_STATE; @@ -184,11 +206,21 @@ grub_multiboot_boot (void) #endif state.MULTIBOOT_ENTRY_REGISTER = GRUB_MULTIBOOT (payload_eip); - err = GRUB_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER); + err = GRUB_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER, &mbi_size); if (err) return err; +#ifdef GRUB_USE_MULTIBOOT2 + if (grub_slaunch_platform_type () != SLP_NONE) + { + err = grub_multiboot2_prepare_slaunch (state.MULTIBOOT_MBI_REGISTER, + mbi_size); + if (err) + return err; + } +#endif + if (grub_efi_is_finished) normal_boot (GRUB_MULTIBOOT (relocator), state); else diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index 24480df50..e7d57a57b 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -44,6 +44,8 @@ #error "I'm confused" #endif +#include +#include #include #include @@ -71,6 +73,9 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) grub_off_t phlimit; unsigned int i; void *source = NULL; + struct grub_slaunch_params *slparams = grub_slaunch_params(); + grub_uint32_t mle_hdr_offset; + struct grub_txt_mle_header *mle_hdr; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 @@ -123,8 +128,31 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) if (mld->relocatable) { +#ifndef GRUB_USE_MULTIBOOT2 + if (grub_slaunch_platform_type () != SLP_NONE) + return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); +#endif + load_size = highest_load - mld->link_base_addr; + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + /* + * We allocate the binary together with the page tables to make one + * contiguous block for MLE. + */ + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (load_size); + slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); + + /* Do not go below GRUB_TXT_PMR_ALIGN. */ + if (mld->align < GRUB_TXT_PMR_ALIGN) + mld->align = GRUB_TXT_PMR_ALIGN; + } + else + { + slparams->mle_ptab_size = 0; + } + grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, " "load_size=0x%x, avoid_efi_boot_services=%d\n", (long) mld->align, mld->preference, load_size, @@ -146,9 +174,43 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) mld->load_base_addr = get_physical_target_address (ch); source = get_virtual_current_address (ch); + grub_memset (get_virtual_current_address (ch), 0, load_size); + grub_dprintf ("multiboot_loader", "load_base_addr=0x%lx, source=0x%lx\n", + (long) mld->load_base_addr, (long) source); + + if (grub_slaunch_platform_type () != SLP_NONE) + { + slparams->mle_start = mld->load_base_addr; + slparams->mle_mem = source; + } + + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch, + GRUB_MEMORY_MACHINE_UPPER_START, + mld->load_base_addr - slparams->mle_ptab_size, + slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + { + grub_dprintf ("multiboot_loader", "Cannot allocate memory for MLE page tables\n"); + return err; + } + + slparams->mle_ptab_mem = get_virtual_current_address (ch); + slparams->mle_ptab_target = (grub_uint64_t) get_physical_target_address (ch); + grub_dprintf ("multiboot_loader", "mle_ptab_mem = %p, mle_ptab_target = %lx, mle_ptab_size = %x\n", + slparams->mle_ptab_mem, (unsigned long) slparams->mle_ptab_target, + (unsigned) slparams->mle_ptab_size); + } } else - mld->load_base_addr = mld->link_base_addr; + { + mld->load_base_addr = mld->link_base_addr; + /* TODO: support non-relocatable */ + if (grub_slaunch_platform_type () != SLP_NONE) + return grub_error (GRUB_ERR_BAD_OS, "Non-relocatable ELF not supported with slaunch"); + } grub_dprintf ("multiboot_loader", "relocatable=%d, link_base_addr=0x%x, " "load_base_addr=0x%x\n", mld->relocatable, @@ -211,6 +273,30 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) } } + if (grub_slaunch_platform_type () != SLP_NONE) + { + slparams->mle_header_offset = 0xffffffff; + + /* TODO: decide on universal way of conveying location of MLE header */ + for (mle_hdr_offset = 0; mle_hdr_offset < 0x1000; mle_hdr_offset += 16) + { + mle_hdr = (struct grub_txt_mle_header *)((grub_addr_t)source + mle_hdr_offset); + if ( !grub_memcmp ((void *)mle_hdr->uuid, GRUB_TXT_MLE_UUID, 16) ) + { + slparams->mle_header_offset = mle_hdr_offset; + break; + } + } + + if (slparams->mle_header_offset == 0xffffffff) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "MLE header not found"); + + grub_dprintf ("slaunch", "slparams->mle_header_offset: 0x%08x\n", + slparams->mle_header_offset); + + slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; + } + for (i = 0; i < phnum; i++) if (phdr(i)->p_vaddr <= ehdr->e_entry && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry) diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 00a48413c..2ab48b8a6 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -36,6 +36,11 @@ #include #include #include +#include +#include +#include +#include +#include #if defined (GRUB_MACHINE_EFI) #include @@ -277,6 +282,9 @@ grub_multiboot2_load (grub_file_t file, const char *filename) if (addr_tag) { + if (grub_slaunch_platform_type () != SLP_NONE) + return grub_error (GRUB_ERR_BAD_OS, "Slaunch not supported with multiboot addr tag"); + grub_uint64_t load_addr = (addr_tag->load_addr + 1) ? addr_tag->load_addr : (addr_tag->header_addr - ((char *) header - (char *) mld.buffer)); @@ -390,6 +398,36 @@ grub_multiboot2_load (grub_file_t file, const char *filename) err = grub_multiboot2_set_console (GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT, accepted_consoles, 0, 0, 0, console_required); + + if (grub_slaunch_platform_type () != SLP_NONE) + { + grub_relocator_chunk_t ch; + struct grub_slaunch_params *slparams = grub_slaunch_params(); + + if (grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch, 0x1000000, + UP_TO_TOP32 (GRUB_SLAUNCH_TPM_EVT_LOG_SIZE), + GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_HIGH, 1)) + { + grub_free (mld.buffer); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not allocate TPM event log area"); + } + + slparams->tpm_evt_log_base = get_physical_target_address (ch); + slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; + + /* It's OK to call this for AMD SKINIT because SKL erases the log before use. */ + grub_txt_init_tpm_event_log(get_virtual_current_address (ch), + slparams->tpm_evt_log_size); + + grub_dprintf ("multiboot_loader", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", + (unsigned long) slparams->tpm_evt_log_base, + (unsigned) slparams->tpm_evt_log_size); + + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + grub_txt_setup_mle_ptab (slparams); + } + return err; } @@ -701,7 +739,7 @@ retrieve_video_parameters (grub_properly_aligned_t **ptrorig) } grub_err_t -grub_multiboot2_make_mbi (grub_uint32_t *target) +grub_multiboot2_make_mbi (grub_uint32_t *target, grub_uint32_t *size) { grub_properly_aligned_t *ptrorig; grub_properly_aligned_t *mbistart; @@ -1002,7 +1040,9 @@ grub_multiboot2_make_mbi (grub_uint32_t *target) / sizeof (grub_properly_aligned_t); } - ((grub_uint32_t *) mbistart)[0] = (char *) ptrorig - (char *) mbistart; + *size = (char *) ptrorig - (char *) mbistart; + + ((grub_uint32_t *) mbistart)[0] = *size; ((grub_uint32_t *) mbistart)[1] = 0; return GRUB_ERR_NONE; @@ -1126,3 +1166,77 @@ grub_multiboot2_set_bootdev (void) bootdev_set = 1; } + +static void +add_multiboot2_slrt_policy_entries (void) +{ + unsigned i; + struct module *cur; + + for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next) + { + grub_slaunch_add_slrt_policy_entry (17, + GRUB_SLR_ET_MULTIBOOT2_MODULE, + /*flags=*/0, + cur->start, + cur->size, + "Measured MB2 module"); + } +} + +grub_err_t +grub_multiboot2_prepare_slaunch (grub_uint32_t mbi_target, + grub_uint32_t mbi_size) +{ + grub_err_t err; + struct grub_slaunch_params *slparams = grub_slaunch_params (); + grub_uint32_t slp = grub_slaunch_platform_type (); + + slparams->boot_params_addr = mbi_target; + + if (slp == SLP_INTEL_TXT) + { + slparams->slr_table_base = GRUB_SLAUNCH_STORE_IN_OS2MLE; + slparams->slr_table_size = GRUB_PAGE_SIZE; + + slparams->slr_table_mem = grub_zalloc (slparams->slr_table_size); + if (slparams->slr_table_mem == NULL) + return GRUB_ERR_OUT_OF_MEMORY; + + err = grub_txt_boot_prepare (slparams); + if (err != GRUB_ERR_NONE) + return grub_error (err, "TXT boot preparation failed"); + } + else if (slp == SLP_AMD_SKINIT) + { + err = grub_skinit_boot_prepare (grub_multiboot2_relocator, slparams); + if (err != GRUB_ERR_NONE) + return grub_error (err, "SKINIT preparations have failed"); + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Unknown secure launcher platform type: %d\n"), slp); + + grub_slaunch_add_slrt_policy_entry (18, + GRUB_SLR_ET_MULTIBOOT2_INFO, + /*flags=*/0, + mbi_target, + mbi_size, + "Measured MB2 information"); + grub_slaunch_add_slrt_policy_entries (); + if (slp == SLP_INTEL_TXT) + grub_txt_add_slrt_policy_entries (); + add_multiboot2_slrt_policy_entries (); + grub_slaunch_finish_slr_table (); + + grub_dprintf ("multiboot_loader", "slr_table_base = %lx, slr_table_size = %x\n", + (unsigned long) slparams->slr_table_base, + (unsigned) slparams->slr_table_size); + + if (slp == SLP_INTEL_TXT) + grub_memcpy ((void *)(grub_addr_t) slparams->slr_table_base, + slparams->slr_table_mem, + slparams->slr_table_size); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c index c8c8312c5..80d6c60b8 100644 --- a/grub-core/mmap/mmap.c +++ b/grub-core/mmap/mmap.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -343,6 +344,88 @@ grub_mmap_unregister (int handle) #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */ +typedef struct +{ + grub_uint64_t addr; + grub_uint64_t limit; +} addr_limit_t; + +/* Helper for grub_mmap_get_lowest(). */ +static int +lowest_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + addr_limit_t *al = data; + grub_uint64_t end; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_add (addr, size, &end)) + return 0; + + if (addr >= al->limit) + al->addr = grub_min (al->addr, addr); + + if ((addr < al->limit) && (end > al->limit)) + al->addr = al->limit; + + return 0; +} + +/* + * This function calculates lowest available RAM address that is at or above + * the passed limit. If no RAM exists above the limit, ~0 is returned. + */ +grub_uint64_t +grub_mmap_get_lowest (grub_uint64_t limit) +{ + addr_limit_t al = {~0, limit}; + + grub_mmap_iterate (lowest_hook, &al); + + return al.addr; +} + +/* Helper for grub_mmap_get_highest(). */ +static int +highest_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + addr_limit_t *al = data; + grub_uint64_t end; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_add (addr, size, &end)) + return 0; + + if (end < al->limit) + al->addr = grub_max (al->addr, end); + + if ((addr < al->limit) && (end >= al->limit)) + al->addr = al->limit; + + return 0; +} + +/* + * This function calculates highest available RAM address that is below the + * passed limit. Returned address is either one byte after last byte of RAM or + * equal to limit, whichever is lower. If no RAM exists below limit, 0 is + * returned. + */ +grub_uint64_t +grub_mmap_get_highest (grub_uint64_t limit) +{ + addr_limit_t al = {0, limit}; + + grub_mmap_iterate (highest_hook, &al); + + return al.addr; +} + #define CHUNK_SIZE 0x400 struct badram_entry { diff --git a/include/grub/file.h b/include/grub/file.h index d678de063..2bd9e3364 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -134,6 +134,9 @@ enum grub_file_type GRUB_FILE_TYPE_VERIFY_SIGNATURE, + /* Secure Launch module. */ + GRUB_FILE_TYPE_SLAUNCH_MODULE, + GRUB_FILE_TYPE_MASK = 0xffff, /* --skip-sig is specified. */ diff --git a/include/grub/i386/cpuid.h b/include/grub/i386/cpuid.h index f7ae4b0a4..0ddd87b15 100644 --- a/include/grub/i386/cpuid.h +++ b/include/grub/i386/cpuid.h @@ -19,6 +19,18 @@ #ifndef GRUB_CPU_CPUID_HEADER #define GRUB_CPU_CPUID_HEADER 1 +/* General */ +#define GRUB_X86_CPUID_VENDOR 0x00000000 +#define GRUB_X86_CPUID_FEATURES 0x00000001 +/* Intel */ +#define GRUB_X86_CPUID_FEATURES_ECX_VMX (1<<5) +#define GRUB_X86_CPUID_FEATURES_ECX_SMX (1<<6) + +/* AMD */ +#define GRUB_AMD_CPUID_FEATURES 0x80000001 +#define GRUB_AMD_CPUID_FEATURES_ECX_SVM (1<<2) +#define GRUB_AMD_CPUID_FUNC 0x8000000a + extern unsigned char grub_cpuid_has_longmode; extern unsigned char grub_cpuid_has_pae; diff --git a/include/grub/i386/crfr.h b/include/grub/i386/crfr.h new file mode 100644 index 000000000..881a4dad4 --- /dev/null +++ b/include/grub/i386/crfr.h @@ -0,0 +1,127 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_CRFR_H +#define GRUB_CRFR_H 1 + +#include + +/* Routines for R/W of control and flags registers */ + +#define GRUB_CR0_X86_PE 0x00000001 /* Enable Protected Mode */ +#define GRUB_CR0_X86_MP 0x00000002 /* "Math" (FPU) Present */ +#define GRUB_CR0_X86_EM 0x00000004 /* EMulate FPU */ +#define GRUB_CR0_X86_TS 0x00000008 /* Task Switched */ +#define GRUB_CR0_X86_PG 0x80000000 /* Enable PaGing */ + +#define GRUB_CR0_X86_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */ +#define GRUB_CR0_X86_WP 0x00010000 /* Write Protect */ +#define GRUB_CR0_X86_AM 0x00040000 /* Alignment Mask */ +#define GRUB_CR0_X86_NW 0x20000000 /* Not Write-through */ +#define GRUB_CR0_X86_CD 0x40000000 /* Cache Disable */ + +#define GRUB_CR4_X86_VME 0x00000001 /* Virtual 8086 mode extensions */ +#define GRUB_CR4_X86_PVI 0x00000002 /* Protected-mode virtual interrupts */ +#define GRUB_CR4_X86_TSD 0x00000004 /* Time stamp disable */ +#define GRUB_CR4_X86_DE 0x00000008 /* Debugging extensions */ +#define GRUB_CR4_X86_PSE 0x00000010 /* Page size extensions */ +#define GRUB_CR4_X86_PAE 0x00000020 /* Physical address extension */ +#define GRUB_CR4_X86_MCE 0x00000040 /* Enable Machine check enable */ +#define GRUB_CR4_X86_PGE 0x00000080 /* Enable Page global */ +#define GRUB_CR4_X86_PCE 0x00000100 /* Enable Performance monitoring counter */ +#define GRUB_CR4_X86_FXSR 0x00000200 /* Fast FPU save/restore */ +#define GRUB_CR4_X86_XMM 0x00000400 /* Generate #XM instead of #UD for SIMD */ +#define GRUB_CR4_X86_VMXE 0x00002000 /* Enable VMX */ +#define GRUB_CR4_X86_SMXE 0x00004000 /* Enable SMX */ +#define GRUB_CR4_X86_PCIDE 0x00020000 /* Enable PCID */ + +static inline unsigned long +grub_read_cr0 (void) +{ + unsigned long val; + + asm volatile ("mov %%cr0, %0" : "=r" (val) : : "memory"); + + return val; +} + +static inline void +grub_write_cr0 (unsigned long val) +{ + asm volatile ("mov %0, %%cr0" : : "r" (val) : "memory"); +} + +static inline unsigned long +grub_read_cr4 (void) +{ + unsigned long val; + + asm volatile ("mov %%cr4, %0" : "=r" (val) : : "memory"); + + return val; +} + +static inline void +grub_write_cr4 (unsigned long val) +{ + asm volatile ("mov %0, %%cr4" : : "r" (val) : "memory"); +} + +#define GRUB_EFLAGS_X86_CF 0x00000001 /* Carry Flag */ +#define GRUB_EFLAGS_X86_PF 0x00000004 /* Parity Flag */ +#define GRUB_EFLAGS_X86_AF 0x00000010 /* Auxillary carry Flag */ +#define GRUB_EFLAGS_X86_ZF 0x00000040 /* Zero Flag */ +#define GRUB_EFLAGS_X86_SF 0x00000080 /* Sign Flag */ +#define GRUB_EFLAGS_X86_TF 0x00000100 /* Trap Flag */ +#define GRUB_EFLAGS_X86_IF 0x00000200 /* Interrupt Flag */ +#define GRUB_EFLAGS_X86_DF 0x00000400 /* Direction Flag */ +#define GRUB_EFLAGS_X86_OF 0x00000800 /* Overflow Flag */ +#define GRUB_EFLAGS_X86_IOPL 0x00003000 /* IOPL mask */ +#define GRUB_EFLAGS_X86_NT 0x00004000 /* Nested Task */ +#define GRUB_EFLAGS_X86_RF 0x00010000 /* Resume Flag */ +#define GRUB_EFLAGS_X86_VM 0x00020000 /* Virtual Mode */ +#define GRUB_EFLAGS_X86_AC 0x00040000 /* Alignment Check */ +#define GRUB_EFLAGS_X86_VIF 0x00080000 /* Virtual Interrupt Flag */ +#define GRUB_EFLAGS_X86_VIP 0x00100000 /* Virtual Interrupt Pending */ +#define GRUB_EFLAGS_X86_ID 0x00200000 /* CPUID detection flag */ + +static inline unsigned long +grub_read_flags_register (void) +{ + unsigned long flags; + +#ifdef __x86_64__ + asm volatile ("pushfq; popq %0" : "=r" (flags)); +#else + asm volatile ("pushfl; popl %0" : "=r" (flags)); +#endif + + return flags; +} + +static inline void +grub_write_flags_register (unsigned long flags) +{ +#ifdef __x86_64__ + asm volatile ("pushq %0; popfq" : : "r" (flags)); +#else + asm volatile ("pushl %0; popfl" : : "r" (flags)); +#endif +} + +#endif diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index 461097781..99e9d6cba 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -163,6 +163,17 @@ struct linux_i386_kernel_header grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; + grub_uint32_t kernel_info_offset; +} GRUB_PACKED; + +struct linux_kernel_info +{ + grub_uint32_t header; + grub_uint32_t size; /* In bytes, excluding var_len_data[] */ + grub_uint32_t size_total; /* In bytes, including var_len_data[] */ + grub_uint32_t setup_type_max; + grub_uint32_t mle_header_offset; + grub_uint8_t var_len_data[]; } GRUB_PACKED; /* Boot parameters for Linux based on 2.6.12. This is used by the setup @@ -345,9 +356,10 @@ struct linux_kernel_params grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; + grub_uint32_t kernel_info_offset; /* Linux setup header copy - END. */ - grub_uint8_t _pad7[40]; + grub_uint8_t _pad7[36]; grub_uint32_t edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 290 */ struct grub_e820_mmap e820_map[(0x400 - 0x2d0) / 20]; /* 2d0 */ } GRUB_PACKED; diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h index 5cb607fb4..56f64855b 100644 --- a/include/grub/i386/memory.h +++ b/include/grub/i386/memory.h @@ -20,7 +20,9 @@ #ifndef GRUB_MEMORY_CPU_HEADER #define GRUB_MEMORY_CPU_HEADER 1 -#define PAGE_SHIFT 12 +#define GRUB_PAGE_SHIFT 12 +#define GRUB_PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) +#define GRUB_PAGE_MASK (~(GRUB_PAGE_SIZE - 1)) /* The flag for protected mode. */ #define GRUB_MEMORY_CPU_CR0_PE_ON 0x1 @@ -42,8 +44,12 @@ #define GRUB_MMAP_MALLOC_LOW 1 +#include #include +#define GRUB_PAGE_UP(p) ALIGN_UP (p, GRUB_PAGE_SIZE) +#define GRUB_PAGE_DOWN(p) ALIGN_DOWN (p, GRUB_PAGE_SIZE) + struct grub_e820_mmap_entry { grub_uint64_t addr; diff --git a/include/grub/i386/mmio.h b/include/grub/i386/mmio.h new file mode 100644 index 000000000..97a30f7d8 --- /dev/null +++ b/include/grub/i386/mmio.h @@ -0,0 +1,72 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_I386_MMIO_H +#define GRUB_I386_MMIO_H 1 + +#include + +static inline grub_uint8_t +grub_read8 (const grub_addr_t addr) +{ + grub_uint8_t val; + + val = (*(volatile grub_uint8_t *) (addr)); + + return val; +} + +static inline grub_uint32_t +grub_read32 (const grub_addr_t addr) +{ + grub_uint32_t val; + + val = (*(volatile grub_uint32_t *) (addr)); + + return val; +} + +static inline grub_uint64_t +grub_read64 (const grub_addr_t addr) +{ + grub_uint64_t val; + + val = (*(volatile grub_uint64_t *) (addr)); + + return val; +} + +static inline void +grub_write8 (grub_uint8_t val, grub_addr_t addr) +{ + (*(volatile grub_uint8_t *) (addr)) = val; +} + +static inline void +grub_write32 (grub_uint32_t val, grub_addr_t addr) +{ + (*(volatile grub_uint32_t *) (addr)) = val; +} + +static inline void +grub_write64 (grub_uint64_t val, grub_addr_t addr) +{ + (*(volatile grub_uint64_t *) (addr)) = val; +} + +#endif /* GRUB_I386_MMIO_H */ diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h new file mode 100644 index 000000000..52c923ab9 --- /dev/null +++ b/include/grub/i386/msr.h @@ -0,0 +1,137 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * Some definitions in this header are extracted from the Trusted Computing + * Group's "TPM Main Specification", Parts 1-3. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_I386_MSR_H +#define GRUB_I386_MSR_H 1 + +/* General */ +#define GRUB_MSR_X86_PLATFORM_ID 0x00000017 + +#define GRUB_MSR_X86_APICBASE 0x0000001b +#define GRUB_MSR_X86_APICBASE_BSP (1<<8) +#define GRUB_MSR_X86_APICBASE_ENABLE (1<<11) +#define GRUB_MSR_X86_APICBASE_BASE (0xfffff<<12) /* Mask for APIC base address */ + +#define GRUB_MSR_X86_FEATURE_CONTROL 0x0000003a +#define GRUB_MSR_X86_FEATURE_CTRL_LOCK (1<<0) /* Lock writes to this register */ +#define GRUB_MSR_X86_ENABLE_VMX_IN_SMX (1<<1) /* Enable VMX inside SMX */ +#define GRUB_MSR_X86_ENABLE_VMX_OUT_SMX (1<<2) /* Enable VMX outside SMX */ +#define GRUB_MSR_X86_SENTER_FUNCTIONS (0x7f<<8) /* Bitmap of SENTER function enables */ +#define GRUB_MSR_X86_SENTER_ENABLE (1<<15) /* SENTER global enable */ + +#define GRUB_MSR_X86_MTRRCAP 0x000000fe +#define GRUB_MSR_X86_VCNT_MASK 0xff /* Number of variable MTRRs */ + +#define GRUB_MSR_X86_MCG_CAP 0x00000179 +#define GRUB_MSR_MCG_BANKCNT_MASK 0xff /* Number of banks */ +#define GRUB_MSR_X86_MCG_STATUS 0x0000017a +#define GRUB_MSR_MCG_STATUS_MCIP (1ULL<<2) /* MC in progress */ + +#define GRUB_MSR_X86_MISC_ENABLE 0x000001a0 +#define GRUB_MSR_X86_ENABLE_MONITOR_FSM (1<<18) + +#define GRUB_MSR_X86_MTRR_PHYSBASE0 0x00000200 +#define GRUB_MSR_X86_MTRR_PHYSMASK0 0x00000201 +#define GRUB_MSR_X86_MTRR_PHYSBASE(n) (GRUB_MSR_X86_MTRR_PHYSBASE0 + 2 * (n)) +#define GRUB_MSR_X86_MTRR_PHYSMASK(n) (GRUB_MSR_X86_MTRR_PHYSMASK0 + 2 * (n)) +#define GRUB_MSR_X86_BASE_DEF_TYPE_MASK 0xff +#define GRUB_MSR_X86_MASK_VALID (1<<11) + +#define GRUB_MSR_X86_MTRR_DEF_TYPE 0x000002ff +#define GRUB_MSR_X86_DEF_TYPE_MASK 0xff +#define GRUB_MSR_X86_MTRR_ENABLE_FIXED (1<<10) +#define GRUB_MSR_X86_MTRR_ENABLE (1<<11) + +#define GRUB_MSR_X86_MC0_STATUS 0x00000401 + +#define GRUB_MSR_X86_EFER 0xc0000080 /* Extended features */ +#define GRUB_MSR_EFER_LME (1<<8) /* Enable Long Mode/IA-32e */ +#define GRUB_MSR_EFER_LMA (1<<10) /* Long Mode/IA-32e Active */ +#define GRUB_MSR_EFER_SVME (1<<12) /* Enable SVM (AMD-V) */ + +/* AMD Specific */ +#define GRUB_MSR_AMD64_VM_CR 0xc0010114 /* SVM control register */ +#define GRUB_MSR_SVM_VM_CR_SVM_DISABLE (1<<4) /* Disable writes to EFER.SVME */ + +/* MTRR Specific */ +#define GRUB_MTRR_MEMORY_TYPE_UC 0 +#define GRUB_MTRR_MEMORY_TYPE_WC 1 +#define GRUB_MTRR_MEMORY_TYPE_WT 4 +#define GRUB_MTRR_MEMORY_TYPE_WP 5 +#define GRUB_MTRR_MEMORY_TYPE_WB 6 + +#ifndef ASM_FILE + +#include +#include +#include + +static inline grub_err_t +grub_cpu_is_msr_supported (void) +{ + grub_uint32_t eax, ebx, ecx, edx; + + /* + * The CPUID instruction should be used to determine whether MSRs + * are supported, CPUID.01H:EDX[5] = 1. + */ + if (!grub_cpu_is_cpuid_supported ()) + return GRUB_ERR_BAD_DEVICE; + + grub_cpuid (0, eax, ebx, ecx, edx); + + if (eax < 1) + return GRUB_ERR_BAD_DEVICE; + + grub_cpuid (1, eax, ebx, ecx, edx); + + if (!(edx & (1 << 5))) + return GRUB_ERR_BAD_DEVICE; + + return GRUB_ERR_NONE; +} + +/* + * TODO: Add a general protection exception handler. + * Accessing a reserved or unimplemented MSR address results in a GP#. + */ + +static inline grub_uint64_t +grub_rdmsr (grub_uint32_t msr_id) +{ + grub_uint32_t low, high; + + asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr_id)); + + return ((grub_uint64_t) high << 32) | low; +} + +static inline void +grub_wrmsr (grub_uint32_t msr_id, grub_uint64_t msr_value) +{ + grub_uint32_t low = msr_value, high = msr_value >> 32; + + asm volatile ("wrmsr" : : "c" (msr_id), "a" (low), "d" (high)); +} + +#endif /* ASM_FILE */ + +#endif /* GRUB_I386_MSR_H */ diff --git a/include/grub/i386/wrmsr.h b/include/grub/i386/skinit.h similarity index 54% rename from include/grub/i386/wrmsr.h rename to include/grub/i386/skinit.h index dea60aed1..2901dbbb9 100644 --- a/include/grub/i386/wrmsr.h +++ b/include/grub/i386/skinit.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2019 Free Software Foundation, Inc. + * Copyright (C) 2020 Oracle and/or its affiliates. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,22 +14,23 @@ * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . + * + * Main secure launch definitions header file. */ -#ifndef GRUB_WRMSR_H -#define GRUB_WRMSR_H 1 +#ifndef GRUB_I386_SKINIT_H +#define GRUB_I386_SKINIT_H 1 -/* - * TODO: Add a general protection exception handler. - * Accessing a reserved or unimplemented MSR address results in a GP#. - */ +/* SLB is 64k, 64k-aligned. */ +#define GRUB_SKINIT_SLB_SIZE 0x10000 +#define GRUB_SKINIT_SLB_ALIGN 0x10000 + +#include +#include -static inline void -grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value) -{ - grub_uint32_t low = msr_value, high = msr_value >> 32; +int grub_skinit_is_slb (const void *slb_base, grub_uint32_t slb_size); - asm volatile ("wrmsr" : : "c" (msr_id), "a" (low), "d" (high)); -} +grub_err_t grub_skinit_boot_prepare (struct grub_relocator *rel, + struct grub_slaunch_params *slparams); -#endif /* GRUB_WRMSR_H */ +#endif /* GRUB_I386_SKINIT_H */ diff --git a/include/grub/i386/slaunch.h b/include/grub/i386/slaunch.h new file mode 100644 index 000000000..9b4932756 --- /dev/null +++ b/include/grub/i386/slaunch.h @@ -0,0 +1,90 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Main secure launch definitions header file. + */ + +#ifndef GRUB_I386_SLAUNCH_H +#define GRUB_I386_SLAUNCH_H 1 + +/* Secure launch platform types. */ +#define SLP_NONE 0 +#define SLP_INTEL_TXT 1 +#define SLP_AMD_SKINIT 2 + +/* PCRs used by Secure launch. */ +#define GRUB_SLAUNCH_CODE_PCR 17 +#define GRUB_SLAUNCH_DATA_PCR 18 + +#define GRUB_SLAUNCH_TPM_EVT_LOG_SIZE (8 * GRUB_PAGE_SIZE) + +/* + * Special value for slr_table_base of struct grub_slaunch_params that indicates + * that the table should be stored near OS2MLE data (right after it). + * + * In this case: + * 1. Platform-specific code (e.g., TXT-code) is responsible for setting + * slr_table_base to its final value + * 2. SLRT should be copied from slr_table_mem to slr_table_base after invoking + * grub_slaunch_finish_slr_table () by the code which used this special + * value. + */ +#define GRUB_SLAUNCH_STORE_IN_OS2MLE ((grub_uint64_t) 0xFFFFFFFFFFFFFFFF) + +#ifndef ASM_FILE + +#include + +struct grub_slaunch_params +{ + grub_uint32_t boot_params_addr; + grub_uint64_t slr_table_base; + /* This is size of SLRT buffer, so maximum size of the table. */ + grub_uint32_t slr_table_size; + void *slr_table_mem; + grub_uint32_t mle_start; + grub_uint32_t mle_size; + void *mle_mem; + grub_uint64_t mle_ptab_target; + grub_uint32_t mle_ptab_size; + void *mle_ptab_mem; + grub_uint32_t mle_header_offset; + grub_uint32_t ap_wake_block; + grub_uint32_t ap_wake_block_size; + grub_uint32_t dce_base; + grub_uint32_t dce_size; + grub_uint64_t tpm_evt_log_base; + grub_uint32_t tpm_evt_log_size; +}; + +extern grub_uint32_t grub_slaunch_platform_type (void); +extern void *grub_slaunch_module (void); +extern struct grub_slaunch_params *grub_slaunch_params (void); + +extern void grub_slaunch_init_slrt_storage (int arch); +extern void grub_slaunch_add_slrt_policy_entries (void); +extern void grub_slaunch_add_slrt_policy_entry (grub_uint16_t pcr, + grub_uint16_t entity_type, + grub_uint16_t flags, + grub_uint64_t entity, + grub_uint64_t size, + const char *evt_info); +extern void grub_slaunch_finish_slr_table (void); + +#endif /* ASM_FILE */ + +#endif /* GRUB_I386_SLAUNCH_H */ diff --git a/include/grub/i386/rdmsr.h b/include/grub/i386/tpm.h similarity index 59% rename from include/grub/i386/rdmsr.h rename to include/grub/i386/tpm.h index c0a0c717a..ded6d9af3 100644 --- a/include/grub/i386/rdmsr.h +++ b/include/grub/i386/tpm.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2019 Free Software Foundation, Inc. + * Copyright (C) 2020 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,22 +16,20 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_RDMSR_H -#define GRUB_RDMSR_H 1 +#ifndef GRUB_I386_TPM_H +#define GRUB_I386_TPM_H 1 -/* - * TODO: Add a general protection exception handler. - * Accessing a reserved or unimplemented MSR address results in a GP#. - */ - -static inline grub_uint64_t -grub_msr_read (grub_uint32_t msr_id) -{ - grub_uint32_t low, high; +#include - asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr_id)); +typedef enum + { + GRUB_TPM_NONE = 0, + GRUB_TPM_12, + GRUB_TPM_20 + } +grub_tpm_ver_t; - return ((grub_uint64_t)high << 32) | low; -} +extern grub_tpm_ver_t grub_get_tpm_ver (void); +extern void grub_tpm_relinquish_locality (grub_uint8_t lcl); -#endif /* GRUB_RDMSR_H */ +#endif /* GRUB_I386_TPM_H */ diff --git a/include/grub/i386/txt.h b/include/grub/i386/txt.h new file mode 100644 index 000000000..ca4ce67ef --- /dev/null +++ b/include/grub/i386/txt.h @@ -0,0 +1,742 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Intel TXT definitions header file. + */ + +#ifndef GRUB_TXT_H +#define GRUB_TXT_H 1 + +#include +#include +#include +#include +#include +#include + +/* Intel TXT Software Developers Guide Revision 017.4 */ + +/* Chapter 2, Table 4. MLE/SINIT Capabilities Field Bit Definitions */ +#define GRUB_TXT_PLATFORM_TYPE_LEGACY 0 +#define GRUB_TXT_PLATFORM_TYPE_CLIENT 1 +#define GRUB_TXT_PLATFORM_TYPE_SERVER 2 +#define GRUB_TXT_PLATFORM_TYPE_RESERVED 3 + +#define GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT (1<<0) +#define GRUB_TXT_CAPS_MONITOR_SUPPORT (1<<1) +#define GRUB_TXT_CAPS_ECX_PT_SUPPORT (1<<2) +#define GRUB_TXT_CAPS_STM_SUPPORT (1<<3) +#define GRUB_TXT_CAPS_TPM_12_NO_LEGACY_PCR_USAGE (1<<4) +#define GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE (1<<5) /* Must be 1 for TPM 2.0 */ +#define GRUB_TXT_CAPS_PLATFORM_TYPE (3<<6) +#define GRUB_TXT_CAPS_MAXPHYSADDR_SUPPORT (1<<8) +#define GRUB_TXT_CAPS_TPM_20_EVTLOG_SUPPORT (1<<9) +#define GRUB_TXT_CAPS_CBNT_SUPPORT (1<<10) +#define GRUB_TXT_CAPS_STARTUP_ACM_SUPPORT (7<<11) /* Reserved for MLE, must be 0 */ +#define GRUB_TXT_CAPS_DMA_PROTECTION (1<<14) /* 0 = Legacy, 1 = TPR */ +/* Rest is reserved */ + +/* + * Appendix A TXT Execution Technology Authenticated Code Modules + * A.1 Authenticated Code Module Format + */ +#define GRUB_TXT_ACM_MODULE_TYPE 2 + +#define GRUB_TXT_ACM_MODULE_SUB_TYPE_TXT_ACM 0 +#define GRUB_TXT_ACM_MODULE_SUB_TYPE_S_ACM 1 + +#define GRUB_TXT_ACM_FLAG_PREPRODUCTION (1<<14) +#define GRUB_TXT_ACM_FLAG_DEBUG_SIGNED (1<<15) + +#define GRUB_TXT_ACM_MODULE_VENDOR_INTEL 0x00008086 + +#define GRUB_TXT_MLE_MAX_SIZE 0x40000000 + +#define GRUB_MLE_AP_WAKE_BLOCK_SIZE (20 * GRUB_PAGE_SIZE) + +struct grub_txt_acm_header +{ + grub_uint16_t module_type; + grub_uint16_t module_sub_type; + grub_uint32_t header_len; + grub_uint32_t header_version; + grub_uint16_t chipset_id; + grub_uint16_t flags; + grub_uint32_t module_vendor; + grub_uint32_t date; /* e.g 20131231H == December 31, 2013 */ + grub_uint32_t size; /* multiples of 4 bytes */ + grub_uint16_t txt_svn; + grub_uint16_t se_svn; + grub_uint32_t code_control; + grub_uint32_t error_entry_point; + grub_uint32_t gdt_limit; + grub_uint32_t gdt_base; + grub_uint32_t seg_sel; + grub_uint32_t entry_point; + grub_uint8_t reserved2[64]; + grub_uint32_t key_size; + grub_uint32_t scratch_size; + /* RSA Pub Key and Signature */ +} GRUB_PACKED; + +/* A.1.2 ACM Information Table */ +#define GRUB_TXT_ACM_UUID "\xaa\x3a\xc0\x7f\xa7\x46\xdb\x18\x2e\xac\x69\x8f\x8d\x41\x7f\x5a" + +#define GRUB_TXT_ACM_CHIPSET_TYPE_BIOS 0 +#define GRUB_TXT_ACM_CHIPSET_TYPE_SINIT 1 +/* Revocation ACMs */ +#define GRUB_TXT_ACM_CHIPSET_TYPE_BIOS_RACM 8 +#define GRUB_TXT_ACM_CHIPSET_TYPE_SINIT_RACM 9 + +struct grub_txt_acm_info_table +{ + grub_uint8_t uuid[16]; + grub_uint8_t chipset_acm_type; + grub_uint8_t version; + grub_uint16_t length; + grub_uint32_t chipset_id_list; + grub_uint32_t os_sinit_data_ver; + grub_uint32_t min_mle_header_ver; + grub_uint32_t capabilities; + grub_uint32_t acm_version_revision; + grub_uint32_t processor_id_list; + /* Version >= 5 */ + grub_uint32_t tpm_info_list; +} GRUB_PACKED; + +struct grub_txt_acm_chipset_id_list +{ + grub_uint32_t count; + /* Array of chipset ID structs */ +} GRUB_PACKED; + +#define GRUB_TXT_ACM_REVISION_ID_MASK (1<<0) + +struct grub_txt_acm_chipset_id +{ + grub_uint32_t flags; + grub_uint16_t vendor_id; + grub_uint16_t device_id; + grub_uint16_t revision_id; + grub_uint16_t reserved; + grub_uint32_t extended_id; +} GRUB_PACKED; + +struct grub_txt_acm_processor_id_list +{ + grub_uint32_t count; + /* Array of processor ID structs */ +} GRUB_PACKED; + +struct grub_txt_acm_processor_id +{ + grub_uint32_t fms; + grub_uint32_t fms_mask; + grub_uint64_t platform_id; + grub_uint64_t platform_mask; +} GRUB_PACKED; + +struct grub_txt_acm_tpm_info +{ + grub_uint32_t capabilities; + grub_uint16_t count; + /* List of supported hash algorithm per TPM2 spec */ +} GRUB_PACKED; + +/* + * Appendix B SMX Interaction with Platform + * B.1 Intel Trusted Execution Technology Configuration Registers + */ + +#ifdef __x86_64__ +#define GRUB_TXT_CFG_REGS_PUB 0xfed30000ULL +#else +#define GRUB_TXT_CFG_REGS_PUB 0xfed30000 +#endif + +#define GRUB_TXT_STS 0x0000 +#define GRUB_TXT_ESTS 0x0008 +#define GRUB_TXT_ERRORCODE 0x0030 +#define GRUB_TXT_CMD_RESET 0x0038 +#define GRUB_TXT_CMD_CLOSE_PRIVATE 0x0048 +/* VER_FSBIF is considered deprecated, but some CPUs still use it */ +#define GRUB_TXT_VER_FSBIF 0x0100 +#define GRUB_TXT_DIDVID 0x0110 +#define GRUB_TXT_VER_QPIIF 0x0200 +#define GRUB_TXT_CMD_UNLOCK_MEM_CONFIG 0x0218 +#define GRUB_TXT_SINIT_BASE 0x0270 +#define GRUB_TXT_SINIT_SIZE 0x0278 +#define GRUB_TXT_MLE_JOIN 0x0290 +#define GRUB_TXT_HEAP_BASE 0x0300 +#define GRUB_TXT_HEAP_SIZE 0x0308 +/* DPR is considered deprecated, but some CPUs still use it */ +#define GRUB_TXT_DPR 0x0330 +#define GRUB_TXT_CMD_OPEN_LOCALITY1 0x0380 +#define GRUB_TXT_CMD_CLOSE_LOCALITY1 0x0388 +#define GRUB_TXT_CMD_OPEN_LOCALITY2 0x0390 +#define GRUB_TXT_CMD_CLOSE_LOCALITY2 0x0398 +#define GRUB_TXT_PUBLIC_KEY 0x0400 +#define GRUB_TXT_CMD_SECRETS 0x08e0 +#define GRUB_TXT_CMD_NO_SECRETS 0x08e8 +#define GRUB_TXT_E2STS 0x08f0 + +#define GRUB_TXT_STS_SENTER_DONE (1 << 0) +#define GRUB_TXT_STS_SEXIT_DONE (1 << 1) +#define GRUB_TXT_STS_MEM_CONFIG_LOCK (1 << 6) +#define GRUB_TXT_STS_PRIVATE_OPEN (1 << 7) +#define GRUB_TXT_STS_LOCALITY1_OPEN (1 << 15) +#define GRUB_TXT_STS_LOCALITY2_OPEN (1 << 16) + +#define GRUB_TXT_ESTS_TXT_RESET (1 << 0) + +#define GRUB_TXT_VER_FSBIF_DEBUG_FUSE (1 << 31) + +#define GRUB_TXT_VER_QPIIF_DEBUG_FUSE (1 << 31) + +#define GRUB_TXT_E2STS_SECRETS (1 << 1) + +union grub_txt_didvid +{ + grub_uint64_t value; + struct + { + grub_uint16_t vid; + grub_uint16_t did; + grub_uint16_t rid; + grub_uint16_t id_ext; + }; +} GRUB_PACKED; + +#define GRUB_TXT_VERSION_PROD_FUSED (1<<31) + +/* Appendix C Intel TXT Heap Memory */ + +/* Ext Data Structs */ + +struct grub_txt_heap_uuid +{ + grub_uint32_t data1; + grub_uint16_t data2; + grub_uint16_t data3; + grub_uint16_t data4; + grub_uint8_t data5[6]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_END 0 + +struct grub_txt_heap_end_element +{ + /* Empty, just the common header with type and size */ +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER 1 + +struct grub_txt_heap_bios_spec_ver_element +{ + grub_uint16_t spec_ver_major; + grub_uint16_t spec_ver_minor; + grub_uint16_t spec_ver_revision; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_ACM 2 + +struct grub_txt_heap_acm_element +{ + grub_uint32_t num_acms; + /* Array of num_acms grub_uint64_t addresses */ + grub_uint64_t addr[]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_STM 3 + +struct grub_txt_heap_stm_element +{ + /* STM specific BIOS properties */ + /* + * GNU extension used to counteract "error: flexible array member in a struct + * with no named members". + */ + grub_uint8_t data[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_CUSTOM 4 + +struct grub_txt_heap_custom_element +{ + struct grub_txt_heap_uuid uuid; + /* Vendor Data */ + grub_uint8_t data[]; +} GRUB_PACKED; + +/* Deprecated, but still used for TPM 1.2 */ +#define GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR 5 + +struct grub_txt_heap_tpm_event_log_element +{ + grub_uint64_t event_log_phys_addr; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_MADT 6 + +struct grub_txt_heap_madt_element +{ + /* Copy of ACPI MADT table */ + /* + * GNU extension used to counteract "error: flexible array member in a struct + * with no named members". + */ + grub_uint8_t madt[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 8 + +struct grub_txt_heap_event_log_pointer2_1_element +{ + grub_uint64_t phys_addr; + grub_uint32_t allocated_event_container_size; + grub_uint32_t first_record_offset; + grub_uint32_t next_record_offset; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG 9 + +struct grub_txt_heap_mcfg_element +{ + /* Copy of ACPI MCFG table */ + /* + * GNU extension used to counteract "error: flexible array member in a struct + * with no named members". + */ + grub_uint8_t data[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE (2 * sizeof(grub_uint32_t)) + +struct grub_txt_heap_ext_data_element +{ + grub_uint32_t type; + grub_uint32_t size; /* Must be at least 8 bytes, includes size of this struct */ + union { + struct grub_txt_heap_end_element end; + struct grub_txt_heap_bios_spec_ver_element bios_spec_ver; + struct grub_txt_heap_acm_element acm; + struct grub_txt_heap_stm_element stm; + struct grub_txt_heap_custom_element custom; + struct grub_txt_heap_tpm_event_log_element tpm_event_log; + struct grub_txt_heap_madt_element madt; + struct grub_txt_heap_event_log_pointer2_1_element event_log_pointer2_1; + struct grub_txt_heap_mcfg_element mcfg; + }; +} GRUB_PACKED; + +/* TXT Heap Tables */ + +struct grub_txt_bios_data +{ + grub_uint32_t version; /* Currently 5 for TPM 1.2 and 6 for TPM 2.0 */ + grub_uint32_t bios_sinit_size; + grub_uint64_t reserved1; + grub_uint64_t reserved2; + grub_uint32_t num_logical_procs; + /* Versions >= 3 */ + grub_uint32_t sinit_flags; + /* Versions >= 5 with updates in version 6 */ + grub_uint32_t mle_flags; + /* Versions >= 4 */ + /* Ext Data Elements */ +} GRUB_PACKED; + +/* GRUB SLAUNCH specific definitions OS-MLE data */ +#define GRUB_SL_OS_MLE_STRUCT_VERSION 1 + +struct grub_txt_os_mle_data +{ + grub_uint32_t version; + grub_uint32_t boot_params_addr; + grub_uint32_t slrt; + grub_uint32_t txt_info; + grub_uint32_t ap_wake_block; + grub_uint32_t ap_wake_block_size; + grub_uint8_t mle_scratch[64]; +} GRUB_PACKED; + +/* Table 29. OS to SINIT Data Table */ +#define GRUB_TXT_PCR_EXT_MAX_AGILITY_POLICY 0 +#define GRUB_TXT_PCR_EXT_MAX_PERF_POLICY 1 + +struct grub_txt_os_sinit_data +{ + grub_uint32_t version; /* Currently 6 for TPM 1.2 and 7 for TPM 2.0 */ + grub_uint32_t flags; /* Version 7+ only, otherwise reserved */ + grub_uint64_t mle_ptab; + grub_uint64_t mle_size; + grub_uint64_t mle_hdr_base; + grub_uint64_t vtd_pmr_lo_base; + grub_uint64_t vtd_pmr_lo_size; + grub_uint64_t vtd_pmr_hi_base; + grub_uint64_t vtd_pmr_hi_size; + grub_uint64_t lcp_po_base; + grub_uint64_t lcp_po_size; + grub_uint32_t capabilities; + /* Versions >= 5 */ + /* Warning: version 5 has pointer to RSDT here, not RSDP */ + grub_uint64_t efi_rsdp_ptr; + /* Versions >= 6 */ + /* Ext Data Elements */ + grub_uint8_t ext_data_elts[]; +} GRUB_PACKED; + +struct grub_txt_sinit_mle_data +{ + grub_uint32_t version; /* Current values are 6 through 9 */ + /* Reserved for versions >= 9, must be 0 */ + grub_uint8_t bios_acm_id[20]; + grub_uint32_t edx_senter_flags; + grub_uint64_t mseg_valid; + grub_uint8_t sinit_hash[20]; + grub_uint8_t mle_hash[20]; + grub_uint8_t stm_hash[20]; + grub_uint8_t lcp_policy_hash[20]; + grub_uint32_t lcp_policy_control; + /* Versions >= 7 */ + grub_uint32_t rlp_wakeup_addr; + grub_uint32_t reserved; + grub_uint32_t num_of_sinit_mdrs; + grub_uint32_t sinit_mdrs_table_offset; + grub_uint32_t sinit_vtd_dmar_table_size; + grub_uint32_t sinit_vtd_dmar_table_offset; + /* Versions >= 8 */ + grub_uint32_t processor_scrtm_status; /* Reserved for version 9 */ + /* Versions >= 9 */ + /* Ext Data Elements */ +} GRUB_PACKED; + +struct grub_txt_sinit_memory_descriptor_records +{ + grub_uint64_t address; + grub_uint64_t length; + grub_uint8_t type; + grub_uint8_t reserved[7]; +} GRUB_PACKED; + +/* + * Section 2 Measured Launch Environment + * 2.1 MLE Architecture Overview + * Table 3. MLE Header structure + */ + +#define GRUB_TXT_MLE_UUID "\x5a\xac\x82\x90\x6f\x47\xa7\x74\x0f\x5c\x55\xa2\xcb\x51\xb6\x42" + +struct grub_txt_mle_header +{ + grub_uint8_t uuid[16]; + grub_uint32_t header_len; + grub_uint32_t version; + grub_uint32_t entry_point; + grub_uint32_t first_valid_page; + grub_uint32_t mle_start; + grub_uint32_t mle_end; + grub_uint32_t capabilities; + grub_uint32_t cmdline_start; + grub_uint32_t cmdline_end; +} GRUB_PACKED; + +struct grub_txt_heap_event_log_ptr_elt +{ + grub_uint64_t event_log_phys_addr; +} GRUB_PACKED; + +struct grub_txt_heap_event_log_ptr_elt2_1 +{ + grub_uint64_t phys_addr; + grub_uint32_t allcoated_event_container_size; + grub_uint32_t first_record_offset; + grub_uint32_t next_record_offset; +} GRUB_PACKED; + +/* TXT register and heap access */ + +static inline grub_uint8_t +grub_txt_reg_pub_read8 (grub_uint16_t reg) +{ + return grub_read8 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint32_t +grub_txt_reg_pub_read32 (grub_uint16_t reg) +{ + return grub_read32 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint64_t +grub_txt_reg_pub_read64 (grub_uint16_t reg) +{ + return grub_read64 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint8_t * +grub_txt_get_heap (void) +{ + return (grub_uint8_t *)(grub_addr_t) grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); +} + +static inline grub_uint32_t +grub_txt_get_heap_size (void) +{ + return grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); +} + +/* + * Each block of data on heap begins with 64-bit size field, followed by proper + * data. Specified size includes size field, so the minimal value of that field + * is 8. TXT SDG mentions that all sizes must be multiples of 8 bytes, but even + * BiosData produced by code signed by Intel doesn't follow that requirement. + * This means that we can't just cast pointer to arbitrary location on TXT heap + * with (grub_uint64_t *), because unaligned pointer is UB. + */ +static inline grub_uint64_t +grub_txt_bios_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)heap; +} + +static inline struct grub_txt_bios_data * +grub_txt_bios_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_bios_data*)(heap + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_os_mle_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + grub_txt_bios_data_size (heap)); +} + +static inline struct grub_txt_os_mle_data * +grub_txt_os_mle_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_os_mle_data*)(heap + + grub_txt_bios_data_size (heap) + + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_os_sinit_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap)); +} + +static inline struct grub_txt_os_sinit_data * +grub_txt_os_sinit_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_os_sinit_data *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_sinit_mle_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + grub_txt_os_sinit_data_size (heap)); +} + +static inline struct grub_txt_sinit_mle_data * +grub_txt_sinit_mle_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_sinit_mle_data *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + grub_txt_os_sinit_data_size (heap) + + sizeof (grub_uint64_t)); +} + +/* + * Intel 64 and IA-32 Architectures Software Developer’s Manual + * Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z + * Order Number: 325383-082US December 2023 + */ + +/* CHAPTER 7 SAFER MODE EXTENSIONS REFERENCE */ + +/* Table 7-2. GETSEC Leaf Functions */ +#define GRUB_SMX_LEAF_CAPABILITIES 0 +#define GRUB_SMX_LEAF_UNDEFINED 1 +#define GRUB_SMX_LEAF_ENTERACCS 2 +#define GRUB_SMX_LEAF_EXITAC 3 +#define GRUB_SMX_LEAF_SENTER 4 +#define GRUB_SMX_LEAF_SEXIT 5 +#define GRUB_SMX_LEAF_PARAMETERS 6 +#define GRUB_SMX_LEAF_SMCTRL 7 +#define GRUB_SMX_LEAF_WAKEUP 8 + +/* Table 7-3. GETSEC Capability Result Encoding */ +#define GRUB_SMX_CAPABILITY_CHIPSET_PRESENT (1<<0) +#define GRUB_SMX_CAPABILITY_UNDEFINED (1<<1) +#define GRUB_SMX_CAPABILITY_ENTERACCS (1<<2) +#define GRUB_SMX_CAPABILITY_EXITAC (1<<3) +#define GRUB_SMX_CAPABILITY_SENTER (1<<4) +#define GRUB_SMX_CAPABILITY_SEXIT (1<<5) +#define GRUB_SMX_CAPABILITY_PARAMETERS (1<<6) +#define GRUB_SMX_CAPABILITY_SMCTRL (1<<7) +#define GRUB_SMX_CAPABILITY_WAKEUP (1<<8) +#define GRUB_SMX_CAPABILITY_EXTENDED_LEAFS (1<<31) + +static inline grub_uint32_t +grub_txt_getsec_capabilities (grub_uint32_t index) +{ + grub_uint32_t caps; + + asm volatile ("getsec" + : "=a" (caps) + : "a" (GRUB_SMX_LEAF_CAPABILITIES), "b" (index)); + return caps; +} + +static inline void +grub_txt_getsec_parameters (grub_uint32_t index, grub_uint32_t *eax_out, + grub_uint32_t *ebx_out, grub_uint32_t *ecx_out) +{ + if (!eax_out || !ebx_out || !ecx_out) + return; + + asm volatile ("getsec" + : "=a" (*eax_out), "=b" (*ebx_out), "=c" (*ecx_out) + : "a" (GRUB_SMX_LEAF_PARAMETERS), "b" (index)); +} + +#define GRUB_SMX_PARAMETER_TYPE_MASK 0x1f +#define GRUB_SMX_PARAMETER_NULL 0 +#define GRUB_SMX_PARAMETER_ACM_VERSIONS 1 +#define GRUB_SMX_PARAMETER_MAX_ACM_SIZE 2 +#define GRUB_SMX_PARAMETER_ACM_MEMORY_TYPES 3 +#define GRUB_SMX_PARAMETER_SENTER_CONTROLS 4 +#define GRUB_SMX_PARAMETER_TXT_EXTENSIONS 5 + +#define GRUB_SMX_PARAMETER_MAX_VERSIONS 0x20 + +#define GRUB_SMX_GET_MAX_ACM_SIZE(v) ((v) & ~(__typeof__(v))GRUB_SMX_PARAMETER_TYPE_MASK) + +#define GRUB_SMX_ACM_MEMORY_TYPE_UC 0x00000100 +#define GRUB_SMX_ACM_MEMORY_TYPE_WC 0x00000200 +#define GRUB_SMX_ACM_MEMORY_TYPE_WT 0x00001000 +#define GRUB_SMX_ACM_MEMORY_TYPE_WP 0x00002000 +#define GRUB_SMX_ACM_MEMORY_TYPE_WB 0x00004000 + +#define GRUB_SMX_GET_ACM_MEMORY_TYPES(v) ((v) & ~(__typeof__(v))GRUB_SMX_PARAMETER_TYPE_MASK) + +#define GRUB_SMX_GET_SENTER_CONTROLS(v) ((v & 0x7f00) >> 8) + +#define GRUB_SMX_PROCESSOR_BASE_SCRTM 0x00000020 +#define GRUB_SMX_MACHINE_CHECK_HANDLING 0x00000040 +#define GRUB_SMX_GET_TXT_EXT_FEATURES(v) (v & (__typeof__(v))(GRUB_SMX_PROCESSOR_BASE_SCRTM|GRUB_SMX_MACHINE_CHECK_HANDLING)) + +#define GRUB_SMX_DEFAULT_VERSION 0x0 +#define GRUB_SMX_DEFAULT_VERSION_MASK 0xffffffff +#define GRUB_SMX_DEFAULT_MAX_ACM_SIZE 0x8000 /* 32K */ +#define GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE GRUB_SMX_ACM_MEMORY_TYPE_UC +#define GRUB_SMX_DEFAULT_SENTER_CONTROLS 0x0 + +/* + * Measured Launch Environment Developer’s Guide, + * Table 29. OS to SINIT Data Table + */ +#define GRUB_TXT_PMR_ALIGN_SHIFT 21 +#define GRUB_TXT_PMR_ALIGN (1 << GRUB_TXT_PMR_ALIGN_SHIFT) + +struct grub_smx_supported_versions +{ + grub_uint32_t mask; + grub_uint32_t version; +} GRUB_PACKED; + +struct grub_smx_parameters +{ + struct grub_smx_supported_versions versions[GRUB_SMX_PARAMETER_MAX_VERSIONS]; + grub_uint32_t version_count; + grub_uint32_t max_acm_size; + grub_uint32_t acm_memory_types; + grub_uint32_t senter_controls; + grub_uint32_t txt_feature_ext_flags; +} GRUB_PACKED; + +/* Structures and constants used for TPM 1.2 event log initialization */ +struct grub_tpm12_pcr_event +{ + grub_uint32_t pcr_index; + grub_uint32_t type; + grub_uint8_t digest[SHA1_DIGEST_SIZE]; + grub_uint32_t data_size; + grub_uint8_t data[]; +} GRUB_PACKED; + +#define EVTLOG_SIGNATURE "TXT Event Container" +#define EVTLOG_CNTNR_MAJOR_VER 1 +#define EVTLOG_CNTNR_MINOR_VER 0 +#define EVTLOG_EVT_MAJOR_VER 1 +#define EVTLOG_EVT_MINOR_VER 0 + +struct grub_txt_event_log_container +{ + grub_uint8_t signature[20]; + grub_uint8_t reserved[12]; + grub_uint8_t container_ver_major; + grub_uint8_t container_ver_minor; + grub_uint8_t pcr_event_ver_major; + grub_uint8_t pcr_event_ver_minor; + grub_uint32_t size; + grub_uint32_t pcr_events_offset; + grub_uint32_t next_event_offset; + struct grub_tpm12_pcr_event pcr_events[]; +} GRUB_PACKED; + +extern grub_uint32_t grub_txt_supported_os_sinit_data_ver (struct grub_txt_acm_header *hdr); + +extern grub_uint32_t grub_txt_get_sinit_capabilities (struct grub_txt_acm_header *hdr); + +extern int grub_txt_is_sinit_acmod (const void *acmod_base, grub_uint32_t acmod_size); + +extern int grub_txt_acmod_match_platform (struct grub_txt_acm_header *hdr); + +extern struct grub_txt_acm_header *grub_txt_sinit_select (struct grub_txt_acm_header *sinit); + +extern grub_err_t grub_txt_verify_platform (void); +extern grub_err_t grub_txt_prepare_cpu (void); + +extern void grub_txt_init_tpm_event_log (void *buf, grub_size_t size); + +extern grub_uint32_t grub_txt_get_mle_ptab_size (grub_uint32_t mle_size); +extern void grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams); + +extern grub_err_t grub_txt_init (void); +extern void grub_txt_shutdown (void); +extern void grub_txt_state_show (void); +/* + * This function doesn't finish building of SLRT. It's caller's responsibility + * to call grub_slaunch_finish_slr_table() after making any necessary + * grub_slr_add_entry() and grub_slaunch_add_slrt_policy_entry() calls including + * grub_txt_add_slrt_polic_entries() and grub_slaunch_add_slrt_policy_entries(). + */ +extern grub_err_t grub_txt_boot_prepare (struct grub_slaunch_params *slparams); +extern void grub_txt_add_slrt_policy_entries (void); + +#endif diff --git a/include/grub/memory.h b/include/grub/memory.h index 6da114a1b..8f22f7525 100644 --- a/include/grub/memory.h +++ b/include/grub/memory.h @@ -69,6 +69,9 @@ void *grub_mmap_malign_and_register (grub_uint64_t align, grub_uint64_t size, void grub_mmap_free_and_unregister (int handle); +extern grub_uint64_t grub_mmap_get_lowest (grub_uint64_t limit); +extern grub_uint64_t grub_mmap_get_highest (grub_uint64_t limit); + #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE struct grub_mmap_region diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h index d8847f753..020c54847 100644 --- a/include/grub/multiboot.h +++ b/include/grub/multiboot.h @@ -41,7 +41,7 @@ void grub_multiboot (int argc, char *argv[]); void grub_module (int argc, char *argv[]); void grub_multiboot_set_accepts_video (int val); -grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target); +grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target, grub_uint32_t *size); void grub_multiboot_free_mbi (void); grub_err_t grub_multiboot_init_mbi (int argc, char *argv[]); grub_err_t grub_multiboot_add_module (grub_addr_t start, grub_size_t size, diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h index b90aa6989..60d104fed 100644 --- a/include/grub/multiboot2.h +++ b/include/grub/multiboot2.h @@ -33,7 +33,8 @@ void grub_multiboot2 (int argc, char *argv[]); void grub_module2 (int argc, char *argv[]); void grub_multiboot2_set_accepts_video (int val); -grub_err_t grub_multiboot2_make_mbi (grub_uint32_t *target); +grub_err_t grub_multiboot2_make_mbi (grub_uint32_t *target, + grub_uint32_t *size); void grub_multiboot2_free_mbi (void); grub_err_t grub_multiboot2_init_mbi (int argc, char *argv[]); grub_err_t grub_multiboot2_add_module (grub_addr_t start, grub_size_t size, @@ -42,6 +43,8 @@ void grub_multiboot2_set_bootdev (void); void grub_multiboot2_add_elfsyms (grub_size_t num, grub_size_t entsize, unsigned shndx, void *data); +grub_err_t grub_multiboot2_prepare_slaunch (grub_uint32_t mbi_target, + grub_uint32_t mbi_size); grub_uint32_t grub_multiboot2_get_mmap_count (void); grub_err_t grub_multiboot2_set_video_mode (void); diff --git a/include/grub/slr_table.h b/include/grub/slr_table.h new file mode 100644 index 000000000..514ec5feb --- /dev/null +++ b/include/grub/slr_table.h @@ -0,0 +1,328 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Secure Launch Resource Table definitions + */ + +#ifndef GRUB_SLR_TABLE_H +#define GRUB_SLR_TABLE_H 1 + +#include + +#define GRUB_UEFI_SLR_TABLE_GUID \ + { 0x877a9b2a, 0x0385, 0x45d1, { 0xa0, 0x34, 0x9d, 0xac, 0x9c, 0x9e, 0x56, 0x5f }} + +/* SLR table header values */ +#define GRUB_SLR_TABLE_MAGIC 0x4452544d +#define GRUB_SLR_TABLE_REVISION 1 + +/* Current revisions for the policy and UEFI config */ +#define GRUB_SLR_POLICY_REVISION 1 +#define GRUB_SLR_UEFI_CONFIG_REVISION 1 + +/* SLR defined architectures */ +#define GRUB_SLR_INTEL_TXT 1 +#define GRUB_SLR_AMD_SKINIT 2 + +/* SLR defined bootloaders */ +#define GRUB_SLR_BOOTLOADER_INVALID 0 +#define GRUB_SLR_BOOTLOADER_GRUB 1 + +/* Log formats */ +#define GRUB_SLR_DRTM_TPM12_LOG 1 +#define GRUB_SLR_DRTM_TPM20_LOG 2 + +/* DRTM Policy Entry Flags */ +#define GRUB_SLR_POLICY_FLAG_MEASURED 0x1 +#define GRUB_SLR_POLICY_IMPLICIT_SIZE 0x2 + +/* Array Lengths */ +#define GRUB_TPM_EVENT_INFO_LENGTH 32 +#define GRUB_TXT_VARIABLE_MTRRS_LENGTH 32 + +/* Tags */ +#define GRUB_SLR_ENTRY_INVALID 0x0000 +#define GRUB_SLR_ENTRY_DL_INFO 0x0001 +#define GRUB_SLR_ENTRY_LOG_INFO 0x0002 +#define GRUB_SLR_ENTRY_DRTM_POLICY 0x0003 +#define GRUB_SLR_ENTRY_INTEL_INFO 0x0004 +#define GRUB_SLR_ENTRY_AMD_INFO 0x0005 +#define GRUB_SLR_ENTRY_ARM_INFO 0x0006 +#define GRUB_SLR_ENTRY_UEFI_INFO 0x0007 +#define GRUB_SLR_ENTRY_UEFI_CONFIG 0x0008 +#define GRUB_SLR_ENTRY_END 0xffff + +/* Entity Types */ +#define GRUB_SLR_ET_UNSPECIFIED 0x0000 +#define GRUB_SLR_ET_SLRT 0x0001 +#define GRUB_SLR_ET_BOOT_PARAMS 0x0002 +#define GRUB_SLR_ET_SETUP_DATA 0x0003 +#define GRUB_SLR_ET_CMDLINE 0x0004 +#define GRUB_SLR_ET_UEFI_MEMMAP 0x0005 +#define GRUB_SLR_ET_RAMDISK 0x0006 +#define GRUB_SLR_ET_MULTIBOOT2_INFO 0x0007 +#define GRUB_SLR_ET_MULTIBOOT2_MODULE 0x0008 +#define GRUB_SLR_ET_TXT_OS2MLE 0x0010 +#define GRUB_SLR_ET_UNUSED 0xffff + +/* + * Primary Secure Launch Resource Table Header + */ +struct grub_slr_table +{ + grub_uint32_t magic; + grub_uint16_t revision; + grub_uint16_t architecture; + grub_uint32_t size; + grub_uint32_t max_size; + /* table entries */ +} GRUB_PACKED; + +/* + * Common SLRT Table Header + */ +struct grub_slr_entry_hdr +{ + grub_uint32_t tag; + grub_uint32_t size; +} GRUB_PACKED; + +/* + * Boot loader context + */ +struct grub_slr_bl_context +{ + grub_uint16_t bootloader; + grub_uint16_t reserved[3]; + grub_uint64_t context; +} GRUB_PACKED; + +/* + * Dynamic Launch Callback Function type + */ +typedef void (*grub_dl_handler_func)(struct grub_slr_bl_context *bl_context); + +/* + * DRTM Dynamic Launch Configuration + */ +struct grub_slr_entry_dl_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint64_t dce_size; + grub_uint64_t dce_base; + grub_uint64_t dlme_size; + grub_uint64_t dlme_base; + grub_uint64_t dlme_entry; + struct grub_slr_bl_context bl_context; + grub_uint64_t dl_handler; +} GRUB_PACKED; + +/* + * TPM Log Information + */ +struct grub_slr_entry_log_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t format; + grub_uint16_t reserved; + grub_uint32_t size; + grub_uint64_t addr; +} GRUB_PACKED; + +/* + * DRTM Measurement Entry + */ +struct grub_slr_policy_entry +{ + grub_uint16_t pcr; + grub_uint16_t entity_type; + grub_uint16_t flags; + grub_uint16_t reserved; + grub_uint64_t size; + grub_uint64_t entity; + char evt_info[GRUB_TPM_EVENT_INFO_LENGTH]; +} GRUB_PACKED; + +/* + * DRTM Measurement Policy + */ +struct grub_slr_entry_policy +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t reserved[2]; + grub_uint16_t revision; + grub_uint16_t nr_entries; + struct grub_slr_policy_entry policy_entries[]; +} GRUB_PACKED; + +/* + * Secure Launch defined MTRR saving structures + */ +struct grub_slr_txt_mtrr_pair +{ + grub_uint64_t mtrr_physbase; + grub_uint64_t mtrr_physmask; +} GRUB_PACKED; + +struct grub_slr_txt_mtrr_state +{ + grub_uint64_t default_mem_type; + grub_uint64_t mtrr_vcnt; + struct grub_slr_txt_mtrr_pair mtrr_pair[GRUB_TXT_VARIABLE_MTRRS_LENGTH]; +} GRUB_PACKED; + +/* + * Intel TXT Info table + */ +struct grub_slr_entry_intel_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint64_t txt_heap; + grub_uint64_t saved_misc_enable_msr; + struct grub_slr_txt_mtrr_state saved_bsp_mtrrs; +} GRUB_PACKED; + +/* + * UEFI config measurement entry + */ +struct grub_slr_uefi_cfg_entry +{ + grub_uint16_t pcr; + grub_uint16_t reserved; + grub_uint32_t size; + grub_uint64_t cfg; /* address or value */ + char evt_info[GRUB_TPM_EVENT_INFO_LENGTH]; +} GRUB_PACKED; + +/* + * UEFI config measurements + */ +struct grub_slr_entry_uefi_config +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t reserved[2]; + grub_uint16_t revision; + grub_uint16_t nr_entries; + struct grub_slr_uefi_cfg_entry uefi_cfg_entries[]; +} GRUB_PACKED; + +static inline grub_addr_t +grub_slr_end_of_entries (struct grub_slr_table *table) +{ + return (grub_addr_t) table + table->size; +} + +static inline struct grub_slr_entry_hdr * +grub_slr_next_entry (struct grub_slr_table *table, + struct grub_slr_entry_hdr *curr) +{ + grub_addr_t addr; + struct grub_slr_entry_hdr *next; + + /* Can read the size field of current entry? */ + if (grub_add ((grub_addr_t) curr, sizeof(*curr), &addr)) + return NULL; + + /* Does current size overflow? */ + if (grub_add ((grub_addr_t) curr, curr->size, &addr)) + return NULL; + + /* Can read the size field of next entry? */ + if (grub_add (addr, sizeof(*next), &addr)) + return NULL; + + /* Does next element's header fit within the table? */ + if (addr > grub_slr_end_of_entries (table)) + return NULL; + + next = (struct grub_slr_entry_hdr *) (addr - sizeof(*next)); + + /* Does next element fit within the table? */ + if (grub_slr_end_of_entries (table) - (addr - sizeof(*next)) < next->size) + return NULL; + + /* Is this the last element? */ + if (next->tag == GRUB_SLR_ENTRY_END) + return NULL; + + return next; +} + +static inline struct grub_slr_entry_hdr * +grub_slr_next_entry_by_tag (struct grub_slr_table *table, + struct grub_slr_entry_hdr *entry, + grub_uint16_t tag) +{ + if (!entry) /* Start from the beginning */ + entry = (struct grub_slr_entry_hdr *)((grub_uint8_t *) table + sizeof(*table)); + + for ( ; ; ) + { + if (entry->tag == tag) + return entry; + + entry = grub_slr_next_entry (table, entry); + if (!entry) + return NULL; + } + + return NULL; +} + +static inline int +grub_slr_add_entry (struct grub_slr_table *table, + struct grub_slr_entry_hdr *entry) +{ + struct grub_slr_entry_hdr *end; + grub_uint32_t new_size; + + if (entry->size < sizeof(*end) || + grub_add (table->size, entry->size, &new_size) || + new_size > table->max_size) + return -1; + + grub_memcpy ((grub_uint8_t *) table + table->size - sizeof(*end), entry, entry->size); + table->size += entry->size; + + end = (struct grub_slr_entry_hdr *)((grub_uint8_t *) table + table->size - sizeof(*end)); + end->tag = GRUB_SLR_ENTRY_END; + end->size = sizeof(*end); + + return 0; +} + +static inline void +grub_slr_init_table(struct grub_slr_table *slrt, grub_uint16_t architecture, + grub_uint32_t max_size) +{ + struct grub_slr_entry_hdr *end; + + if (max_size < sizeof(*slrt) + sizeof(*end)) + grub_fatal ("Requested SLRT max size (%" PRIuGRUB_UINT32_T " B) is too small\n", + max_size); + + slrt->magic = GRUB_SLR_TABLE_MAGIC; + slrt->revision = GRUB_SLR_TABLE_REVISION; + slrt->architecture = architecture; + slrt->size = sizeof(*slrt) + sizeof(*end); + slrt->max_size = max_size; + end = (struct grub_slr_entry_hdr *)((grub_uint8_t *) slrt + sizeof(*slrt)); + end->tag = GRUB_SLR_ENTRY_END; + end->size = sizeof(*end); +} + +#endif /* GRUB_SLR_TABLE_H */