2020-05-04 23:25:44

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

Hi,

This is an RFC patchset for the GRUB introducing the Intel TXT secure launcher.
This is a part of larger work known as the TrenchBoot. Patchset can be split
into two distinct parts:
- 01-12: preparatory patches,
- 13-18: the Intel TXT secure launcher itself.

The initial implementation of the Intel TXT secure launcher works. However,
there are still some missing bits and pieces, e.g.:
- SINIT ACM auto loader,
- lack of RMRR support,
- lack of support for MLEs larger than 1 GiB,
- lack of TPM 1.2 support.
- various fixes and cleanups.

Commands introduced by this patchset: tpm_type, slaunch, slaunch_module (not
required on server platforms) and slaunch_state (useful for checking platform
configuration and state; based on tboot's txt-stat).

Daniel


2020-05-04 23:26:24

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 06/18] mmap: Add grub_mmap_get_lowest() and grub_mmap_get_highest()

The functions calculate lowest and highest available RAM
addresses respectively.

Both functions are needed to calculate PMR boundaries for
Intel TXT secure launcher introduced by subsequent patches.

Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/mmap/mmap.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
include/grub/memory.h | 3 +++
2 files changed, 67 insertions(+)

diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c
index b569cb23b..cf4b69f56 100644
--- a/grub-core/mmap/mmap.c
+++ b/grub-core/mmap/mmap.c
@@ -340,6 +340,70 @@ 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;
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+
+ if (addr >= al->limit)
+ al->addr = grub_min (al->addr, addr);
+
+ if ((addr < al->limit) && ((addr + size) > al->limit))
+ al->addr = al->limit;
+
+ return 0;
+}
+
+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;
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+
+ if ((addr + size) < al->limit)
+ al->addr = grub_max (al->addr, addr + size);
+
+ if ((addr < al->limit) && ((addr + size) >= al->limit))
+ al->addr = al->limit;
+
+ return 0;
+}
+
+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/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
--
2.11.0

2020-05-04 23:26:31

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 14/18] i386/txt: Add Intel TXT definitions header file

From: Ross Philipson <[email protected]>

Signed-off-by: Ross Philipson <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
include/grub/i386/txt.h | 690 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 690 insertions(+)
create mode 100644 include/grub/i386/txt.h

diff --git a/include/grub/i386/txt.h b/include/grub/i386/txt.h
new file mode 100644
index 000000000..8280e461e
--- /dev/null
+++ b/include/grub/i386/txt.h
@@ -0,0 +1,690 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Intel TXT definitions header file.
+ */
+
+#ifndef GRUB_TXT_H
+#define GRUB_TXT_H 1
+
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/i386/memory.h>
+#include <grub/i386/mmio.h>
+#include <grub/i386/slaunch.h>
+
+/* Intel TXT Software Developers Guide */
+
+/* Chapter 2, Table 2 MLE/SINIT Capabilities Field Bit Definitions */
+
+#define GRUB_TXT_PCR_EXT_MAX_AGILITY_POLICY 0
+#define GRUB_TXT_PCR_EXT_MAX_PERF_POLICY 1
+
+#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)
+#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)
+/* 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_HEADER_LEN_0_0 161
+#define GRUB_TXT_ACM_HEADER_LEN_3_0 224
+
+#define GRUB_TXT_ACM_HEADER_VERSION_0_0 0x0000
+#define GRUB_TXT_ACM_HEADER_VERSION_3_0 0x0300
+
+#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 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;
+
+#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
+#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;
+
+#define GRUB_TXT_TPM_CAPS_EXTPOL_NONE 0 /* TPM 1.2 */
+#define GRUB_TXT_TPM_CAPS_EXTPOL_MA 1
+#define GRUB_TXT_TPM_CAPS_EXTPOL_MP 2
+#define GRUB_TXT_TPM_CAPS_EXTPOL_BOTH 3
+
+#define GRUB_TXT_TPM_CAPS_FAMILY_DISCRETE_12 1
+#define GRUB_TXT_TPM_CAPS_FAMILY_DISCRETE_20 2
+#define GRUB_TXT_TPM_CAPS_FAMILY_FIRMWARE_20 8
+
+#define GRUB_TXT_TPM_CAPS_INITIAL_NV_INDICES 0
+#define GRUB_TXT_TPM_CAPS_TCP_NV_INDICES 1
+
+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
+#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
+#define GRUB_TXT_MSEG_BASE 0x0310
+#define GRUB_TXT_MSEG_SIZE 0x0318
+#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_DEBUG_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;
+
+struct grub_txt_heap_ext_data_element
+{
+ grub_uint32_t type;
+ grub_uint32_t size;
+ grub_uint8_t data[];
+} GRUB_PACKED;
+
+#define GRUB_TXT_HEAP_EXTDATA_TYPE_END 0
+
+struct grub_txt_heap_end_element
+{
+ grub_uint32_t type;
+ grub_uint32_t 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_PACKED;
+
+#define GRUB_TXT_HEAP_EXTDATA_TYPE_STM 3
+
+struct grub_txt_heap_stm_element
+{
+ /* STM specific BIOS properties */
+} 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_PACKED;
+
+#define GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR 5
+
+struct grub_txt_heap_tpm_event_log_element
+{
+ grub_uint32_t type;
+ grub_uint32_t size;
+ 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 */
+} GRUB_PACKED;
+
+#define GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 8
+
+struct grub_txt_heap_event_log_pointer2_1_element
+{
+ grub_uint32_t type;
+ grub_uint32_t size;
+ 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 */
+} 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 reserved22;
+ grub_uint32_t num_logical_procs;
+ /* Versions >= 5 with updates in version 6 */
+ grub_uint32_t sinit_flags;
+ grub_uint32_t mle_flags;
+ /* Versions >= 4 */
+ /* Ext Data Elements */
+} GRUB_PACKED;
+
+/* GRUB SLAUNCH specific definitions OS-MLE data */
+#define GRUB_SL_BOOTPARAMS_OFFSET 0x12c
+#define GRUB_SL_MAX_EVENT_LOG_SIZE (5*4*1024) /* 4k*5 */
+#define GRUB_SL_MAX_VARIABLE_MTRRS 32
+#define GRUB_SL_OS_MLE_STRUCT_VERSION 1
+
+struct grub_slaunch_mtrr_pair
+{
+ grub_uint64_t mtrr_physbase;
+ grub_uint64_t mtrr_physmask;
+} GRUB_PACKED;
+
+struct grub_slaunch_mtrr_state
+{
+ grub_uint64_t default_mem_type;
+ grub_uint64_t mtrr_vcnt;
+ struct grub_slaunch_mtrr_pair mtrr_pair[GRUB_SL_MAX_VARIABLE_MTRRS];
+} GRUB_PACKED;
+
+struct grub_txt_os_mle_data
+{
+ grub_uint32_t version;
+ grub_uint32_t zero_page_addr;
+ grub_uint8_t msb_key_hash[20];
+ grub_uint64_t saved_misc_enable_msr;
+ struct grub_slaunch_mtrr_state saved_bsp_mtrrs;
+ grub_uint64_t ap_wake_ebp;
+ grub_uint64_t ap_wake_block;
+ grub_uint8_t event_log_buffer[GRUB_SL_MAX_EVENT_LOG_SIZE];
+} GRUB_PACKED;
+
+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;
+ 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;
+ /* Version = 5 */
+ grub_uint64_t efi_rsdt_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 */
+ /* Versions <= 8 */
+ 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;
+ /* 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 1. MLE Header structure */
+
+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_uint64_t
+grub_txt_reg_pub_readq (grub_uint32_t reg)
+{
+ return grub_readq ((void *) (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_readq (GRUB_TXT_HEAP_BASE);
+}
+
+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 */
+
+/* CHAPTER 6 SAFER MODE EXTENSIONS REFERENCE */
+
+#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
+
+#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 (".byte 0x0f,0x37\n"
+ : "=a" (caps)
+ : "a" (GRUB_SMX_LEAF_CAPABILITIES), "b" (index));
+ return caps;
+}
+
+static inline void
+grub_txt_getsec_enteraccs (grub_uint32_t acm_phys_addr, grub_uint32_t acm_size)
+{
+ asm volatile (".byte 0x0f,0x37\n" :
+ : "a" (GRUB_SMX_LEAF_ENTERACCS),
+ "b" (acm_phys_addr), "c" (acm_size));
+}
+
+static inline void
+grub_txt_getsec_exitac (grub_uint32_t near_jump)
+{
+ asm volatile (".byte 0x0f,0x37\n" :
+ : "a" (GRUB_SMX_LEAF_EXITAC), "b" (near_jump));
+}
+
+static inline void
+grub_txt_getsec_senter (grub_uint32_t acm_phys_addr, grub_uint32_t acm_size)
+{
+ asm volatile (".byte 0x0f,0x37\n" :
+ : "a" (GRUB_SMX_LEAF_SENTER),
+ "b" (acm_phys_addr), "c" (acm_size));
+}
+
+static inline void
+grub_txt_getsec_sexit (void)
+{
+ asm volatile (".byte 0x0f,0x37\n" : : "a" (GRUB_SMX_LEAF_SEXIT));
+}
+
+#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 & ~GRUB_SMX_PARAMETER_TYPE_MASK)*0x20)
+
+#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 & ~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_HANLDING 0x00000040
+#define GRUB_SMX_GET_TXT_EXT_FEATURES(v) (v & (GRUB_SMX_PROCESSOR_BASE_SCRTM|GRUB_SMX_MACHINE_CHECK_HANLDING))
+
+#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
+
+#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;
+
+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 (".byte 0x0f,0x37\n"
+ : "=a" (*eax_out), "=b" (*ebx_out), "=c" (*ecx_out)
+ : "0" (GRUB_SMX_LEAF_PARAMETERS), "1" (index));
+}
+
+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 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);
+extern grub_err_t grub_txt_boot_prepare (struct grub_slaunch_params *slparams);
+
+#endif
--
2.11.0

2020-05-04 23:27:31

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 07/18] i386/tpm: Rename tpm module to tpm_verifier

..to avoid naming collision with TPM TIS and CRB driver introduced
by subsequent patch.

Signed-off-by: Daniel Kiper <[email protected]>
---
docs/grub.texi | 15 ++++++++-------
grub-core/Makefile.core.def | 4 ++--
grub-core/commands/{tpm.c => tpm_verifier.c} | 6 +++---
3 files changed, 13 insertions(+), 12 deletions(-)
rename grub-core/commands/{tpm.c => tpm_verifier.c} (96%)

diff --git a/docs/grub.texi b/docs/grub.texi
index d6408d242..395431bce 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -5940,10 +5940,10 @@ it cannot be unloaded if it was loaded into the memory.
@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
@@ -5968,9 +5968,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 platforms.

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 48b82e763..b74a34f0c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2497,8 +2497,8 @@ module = {
};

module = {
- name = tpm;
- common = commands/tpm.c;
+ name = tpm_verifier;
+ common = commands/tpm_verifier.c;
efi = commands/efi/tpm.c;
enable = x86_64_efi;
};
diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm_verifier.c
similarity index 96%
rename from grub-core/commands/tpm.c
rename to grub-core/commands/tpm_verifier.c
index 1441c494d..6fff17720 100644
--- a/grub-core/commands/tpm.c
+++ b/grub-core/commands/tpm_verifier.c
@@ -85,18 +85,18 @@ 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)
{
grub_verifier_register (&grub_tpm_verifier);
}

-GRUB_MOD_FINI (tpm)
+GRUB_MOD_FINI (tpm_verifier)
{
grub_verifier_unregister (&grub_tpm_verifier);
}
--
2.11.0

2020-05-04 23:28:06

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 17/18] i386/txt: Add Intel TXT verification routines

From: Ross Philipson <[email protected]>

Signed-off-by: Ross Philipson <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/loader/i386/txt/verify.c | 297 +++++++++++++++++++++++++++++++++++++
1 file changed, 297 insertions(+)
create mode 100644 grub-core/loader/i386/txt/verify.c

diff --git a/grub-core/loader/i386/txt/verify.c b/grub-core/loader/i386/txt/verify.c
new file mode 100644
index 000000000..97f3b325d
--- /dev/null
+++ b/grub-core/loader/i386/txt/verify.c
@@ -0,0 +1,297 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/cpu/relocator.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i386/msr.h>
+#include <grub/i386/txt.h>
+
+/* 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)
+{
+ grub_uint8_t *ptr = (grub_uint8_t *)elt;
+ struct grub_txt_heap_bios_spec_ver_element *bios_spec_ver_elt =
+ (struct grub_txt_heap_bios_spec_ver_element *)ptr;
+
+ if ( elt->size != sizeof(*elt) + sizeof(*bios_spec_ver_elt) )
+ 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_uint8_t *ptr = ((grub_uint8_t *)elt + sizeof(*elt));
+ struct grub_txt_heap_acm_element *acm_elt =
+ (struct grub_txt_heap_acm_element *)ptr;
+ grub_uint64_t *acm_addrs;
+ grub_uint32_t i;
+
+ if ( elt->size != sizeof(*elt) + sizeof(*acm_elt) +
+ acm_elt->num_acms*sizeof(grub_uint64_t) )
+ 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 ( acm_elt->num_acms == 0 )
+ grub_printf ("WARNING: HEAP_ACM element has no ACM addrs\n");
+
+ acm_addrs = (grub_uint64_t *)(ptr + sizeof(*acm_elt));
+ for ( i = 0; i < acm_elt->num_acms; i++ )
+ {
+ if ( acm_addrs[i] == 0 )
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ N_("HEAP_ACM element ACM addr (%d) is NULL"), i);
+
+ if ( acm_addrs[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)
+{
+ grub_uint8_t *ptr = (grub_uint8_t *)elt;
+ struct grub_txt_heap_custom_element *custom_elt =
+ (struct grub_txt_heap_custom_element *)ptr;
+
+ if ( elt->size < sizeof(*elt) + sizeof(*custom_elt) )
+ 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_evt_log_ptr_elt (struct grub_txt_heap_ext_data_element *elt)
+{
+ grub_uint8_t *ptr = (grub_uint8_t *)elt;
+ struct grub_txt_heap_tpm_event_log_element *elog_elt =
+ (struct grub_txt_heap_tpm_event_log_element *)ptr;
+
+ if ( elt->size != sizeof(*elt) + sizeof(*elog_elt) )
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ N_("HEAP_EVENT_LOG_POINTER element has wrong size (%d)"),
+ elt->size);
+
+ /* TODO: Sort out how to do this verifier once the event log handling is in place
+ *
+ * return verify_evt_log((event_log_container_t *)(unsigned long)
+ * elog_elt->event_log_phys_addr);
+ */
+
+ 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 < sizeof(*elt) )
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ N_("TXT heap ext data elements too small"));
+
+ for ( ; ; )
+ {
+ if ( elts_size < elt->size || elt->size == 0 )
+ 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 )
+ return err;
+ break;
+ case GRUB_TXT_HEAP_EXTDATA_TYPE_ACM:
+ err = verify_acm_elt (elt);
+ if ( err )
+ 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 )
+ return err;
+ break;
+ case GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR:
+ err = verify_evt_log_ptr_elt (elt);
+ if ( err )
+ return err;
+ break;
+ case GRUB_TXT_HEAP_EXTDATA_TYPE_MADT:
+ /* Copy of ACPI MADT, not validating */
+ break;
+ case GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1:
+ /* TODO TBOOT did not verify this, not sure why or how to do it */
+ break;
+ case GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG:
+ /* Copy of ACPI MCFG, not validating */
+ break;
+ default:
+ /* TODO: What kind of element??? Improve the message. */
+ grub_printf ("WARNING: unknown element: type: %u, size: %u\n", elt->type, elt->size);
+ break;
+ }
+
+ 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;
+ grub_uint64_t bios_size, heap_base, heap_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_SMX_CPUID_FEATURE))
+ 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.
+ */
+
+ if (grub_txt_reg_pub_readq (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 sane.
+ */
+
+ txt_heap = grub_txt_get_heap ();
+ heap_base = grub_txt_reg_pub_readq (GRUB_TXT_HEAP_BASE);
+ heap_size = grub_txt_reg_pub_readq (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 == 0 || 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 < 5 )
+ 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);
+
+ 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;
+}
--
2.11.0

2020-05-04 23:28:30

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 18/18] i386/slaunch: Add secure launch framework and commands

From: Ross Philipson <[email protected]>

Signed-off-by: Ross Philipson <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/Makefile.am | 3 +
grub-core/Makefile.core.def | 15 +++
grub-core/lib/i386/relocator32.S | 8 ++
grub-core/loader/i386/bsd.c | 7 ++
grub-core/loader/i386/linux.c | 206 ++++++++++++++++++++++++++++++++++++---
grub-core/loader/i386/slaunch.c | 194 ++++++++++++++++++++++++++++++++++++
grub-core/loader/i386/xnu.c | 3 +
grub-core/loader/multiboot.c | 5 +
include/grub/file.h | 3 +
include/grub/i386/linux.h | 14 ++-
include/grub/i386/slaunch.h | 56 +++++++++++
11 files changed, 500 insertions(+), 14 deletions(-)
create mode 100644 grub-core/loader/i386/slaunch.c
create mode 100644 include/grub/i386/slaunch.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index 3ea8e7ff4..ae6676e6f 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -99,6 +99,7 @@ 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
endif

if COND_i386_xen_pvh
@@ -118,6 +119,7 @@ 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/acpi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h
@@ -179,6 +181,7 @@ 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/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 b74a34f0c..a07811b51 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1824,6 +1824,15 @@ module = {
};

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;
+ enable = x86;
+};
+
+module = {
name = fdt;
efi = loader/efi/fdt.c;
common = lib/fdt.c;
@@ -2497,6 +2506,12 @@ module = {
};

module = {
+ name = tpm;
+ x86 = commands/i386/tpm.c;
+ enable = x86;
+};
+
+module = {
name = tpm_verifier;
common = commands/tpm_verifier.c;
efi = commands/efi/tpm.c;
diff --git a/grub-core/lib/i386/relocator32.S b/grub-core/lib/i386/relocator32.S
index 09ce56ad0..a2b377197 100644
--- a/grub-core/lib/i386/relocator32.S
+++ b/grub-core/lib/i386/relocator32.S
@@ -24,6 +24,8 @@

#include "relocator_common.S"

+#include <grub/i386/slaunch.h>
+
.p2align 4 /* force 16-byte alignment */

VARIABLE(grub_relocator32_start)
@@ -110,11 +112,17 @@ VARIABLE(grub_relocator32_edx)
payload and makes this implementation easier. */
cld

+ cmpl $SLP_INTEL_TXT, %edi
+ je LOCAL(intel_txt)
+
.byte 0xea
VARIABLE(grub_relocator32_eip)
.long 0
.word CODE_SEGMENT

+LOCAL(intel_txt):
+ getsec
+
/* GDT. Copied from loader/i386/linux.c. */
.p2align 4
LOCAL(gdt):
diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c
index eb82391db..53bb2af93 100644
--- a/grub-core/loader/i386/bsd.c
+++ b/grub-core/loader/i386/bsd.c
@@ -21,6 +21,10 @@
#include <grub/i386/cpuid.h>
#include <grub/memory.h>
#include <grub/i386/memory.h>
+#if 0
+#include <grub/i386/slaunch.h>
+#endif
+#define SLP_NONE 0
#include <grub/file.h>
#include <grub/err.h>
#include <grub/dl.h>
@@ -791,6 +795,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;
@@ -906,6 +911,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;
@@ -1223,6 +1229,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/linux.c b/grub-core/loader/i386/linux.c
index 952eb1191..da8be621e 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -34,6 +34,8 @@
#include <grub/i386/relocator.h>
#include <grub/i18n.h>
#include <grub/lib/cmdline.h>
+#include <grub/i386/slaunch.h>
+#include <grub/i386/txt.h>
#include <grub/linux.h>
#include <grub/machine/kernel.h>

@@ -61,18 +63,23 @@ GRUB_MOD_LICENSE ("GPLv3+");
#define ACCEPTS_PURE_TEXT 1
#endif

+#define KERNEL_INFO_HEADER "LToP"
+#define KERNEL_INFO_MIN_SIZE_TOTAL 12
+
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
@@ -80,6 +87,7 @@ static grub_efi_uintn_t efi_mmap_size;
#else
static const grub_size_t efi_mmap_size = 0;
#endif
+static struct grub_slaunch_params slparams;

/* FIXME */
#if 0
@@ -96,6 +104,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)
{
@@ -148,11 +158,35 @@ 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;

if (prot_size == 0)
prot_size = 1;

- prot_size = page_align (prot_size);
+ if (grub_slaunch_platform_type () == SLP_INTEL_TXT)
+ {
+ prot_size = ALIGN_UP (prot_size, GRUB_TXT_PMR_ALIGN);
+
+ if (prot_size > GRUB_TXT_MLE_MAX_SIZE)
+ {
+ err = GRUB_ERR_OUT_OF_RANGE;
+ goto fail;
+ }
+
+ 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;
+ }
+
+ total_size = prot_size + slparams.mle_ptab_size;

/* Initialize the memory pointers with NULL for convenience. */
free_pages ();
@@ -174,7 +208,7 @@ 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)--)
@@ -182,8 +216,8 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
grub_errno = GRUB_ERR_NONE;
err = grub_relocator_alloc_chunk_align (relocator, &ch,
0x1000000,
- 0xffffffff & ~prot_size,
- prot_size, 1 << *align,
+ 0xffffffff & ~total_size,
+ total_size, 1 << *align,
GRUB_RELOCATOR_PREFERENCE_LOW,
1);
}
@@ -193,11 +227,42 @@ 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_INTEL_TXT)
+ {
+ 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;
+
+ 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);
+
+ if (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))
+ goto fail;
+
+ slparams.tpm_evt_log_base = get_physical_target_address (ch);
+ slparams.tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE;
+
+ grub_memset (get_virtual_current_address (ch), 0, 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);
+ }
}

grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n",
@@ -491,6 +556,7 @@ grub_linux_boot (void)
};
grub_size_t mmap_size;
grub_size_t cl_offset;
+ grub_size_t ap_wake_block_size = 0;

#ifdef GRUB_MACHINE_IEEE1275
{
@@ -623,6 +689,9 @@ grub_linux_boot (void)
(unsigned) ctx.real_size,
(unsigned) efi_mmap_size);

+ if (grub_slaunch_platform_type () == SLP_INTEL_TXT)
+ ap_wake_block_size = GRUB_MLE_AP_WAKE_BLOCK_SIZE;
+
if (! ctx.real_mode_target)
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");

@@ -630,13 +699,24 @@ grub_linux_boot (void)
grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_addr (relocator, &ch,
ctx.real_mode_target,
- (ctx.real_size + efi_mmap_size));
+ (ctx.real_size + efi_mmap_size +
+ ap_wake_block_size));
if (err)
return err;
real_mode_mem = get_virtual_current_address (ch);
}
efi_mmap_buf = (grub_uint8_t *) real_mode_mem + ctx.real_size;

+ if (grub_slaunch_platform_type () == SLP_INTEL_TXT)
+ {
+ slparams.ap_wake_block = ctx.real_mode_target + ctx.real_size + efi_mmap_size;
+ grub_memset ((void *) ((grub_addr_t) real_mode_mem + ctx.real_size +
+ efi_mmap_size), 0, 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) ap_wake_block_size);
+ }
+
grub_dprintf ("linux", "real_mode_mem = %p\n",
real_mode_mem);

@@ -700,12 +780,33 @@ 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.params = ctx.params;
+
+ err = grub_txt_boot_prepare (&slparams);
+
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /* Configure relocator GETSEC[SENTER] call. */
+ state.eax = GRUB_SMX_LEAF_SENTER;
+ state.ebx = slparams.sinit_acm_base;
+ state.ecx = slparams.sinit_acm_size;
+ state.edx = 0;
+ }
+ 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);
}

@@ -726,7 +827,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
grub_file_t file = 0;
struct linux_i386_kernel_header lh;
grub_uint8_t setup_sects;
- grub_size_t real_size, prot_size, prot_file_size;
+ grub_size_t real_size, prot_size;
grub_ssize_t len;
int i;
grub_size_t align, min_align;
@@ -839,6 +940,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))
@@ -846,6 +954,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.
@@ -872,6 +983,75 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
goto fail;
}

+ /* Read the kernel_info struct. */
+ if (grub_le_to_cpu16 (lh.version) >= 0x020f)
+ {
+ if (grub_file_seek (file, 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);
+
+ 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;
diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c
new file mode 100644
index 000000000..72d09236b
--- /dev/null
+++ b/grub-core/loader/i386/slaunch.c
@@ -0,0 +1,194 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/cpu/relocator.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i386/msr.h>
+#include <grub/i386/mmio.h>
+#include <grub/i386/slaunch.h>
+#include <grub/i386/txt.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_uint32_t slp = SLP_NONE;
+
+static void *slaunch_module = NULL;
+
+grub_uint32_t
+grub_slaunch_platform_type (void)
+{
+ return slp;
+}
+
+void *
+grub_slaunch_module (void)
+{
+ return slaunch_module;
+}
+
+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;
+ 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
+ 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;
+
+ if (!argc)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (slp == SLP_NONE)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("secure launch not enabled"));
+
+ 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;
+ }
+
+ slaunch_module = grub_malloc (size);
+
+ if (slaunch_module == NULL)
+ goto fail;
+
+ if (grub_file_read (file, slaunch_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 (slaunch_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 (slaunch_module))
+ {
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("SINIT ACM does not match platform"));
+ goto fail;
+ }
+ }
+
+ grub_file_close (file);
+
+ return GRUB_ERR_NONE;
+
+ fail:
+ grub_error_push ();
+
+ grub_free (slaunch_module);
+ grub_file_close (file);
+
+ slaunch_module = NULL;
+
+ 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 ();
+ }
+
+ 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_("Secure launcher module command"));
+ cmd_slaunch_state = grub_register_command ("slaunch_state", grub_cmd_slaunch_state,
+ NULL, N_("Display secure launcher state"));
+}
+
+GRUB_MOD_FINI (slaunch)
+{
+ grub_unregister_command (cmd_slaunch_state);
+ grub_unregister_command (cmd_slaunch_module);
+ grub_unregister_command (cmd_slaunch);
+
+ if (slp == SLP_INTEL_TXT)
+ grub_txt_shutdown ();
+}
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
index e64ed08f5..e586967b0 100644
--- a/grub-core/loader/i386/xnu.c
+++ b/grub-core/loader/i386/xnu.c
@@ -28,6 +28,7 @@
#include <grub/i386/cpuid.h>
#include <grub/efi/api.h>
#include <grub/i386/pit.h>
+#include <grub/i386/slaunch.h>
#include <grub/misc.h>
#include <grub/charset.h>
#include <grub/term.h>
@@ -807,6 +808,7 @@ grub_xnu_boot_resume (void)
{
struct grub_relocator32_state state;

+ state.edi = SLP_NONE;
state.esp = grub_xnu_stack;
state.ebp = grub_xnu_stack;
state.eip = grub_xnu_entry_point;
@@ -1134,6 +1136,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 4a98d7082..6efb59e8f 100644
--- a/grub-core/loader/multiboot.c
+++ b/grub-core/loader/multiboot.c
@@ -50,6 +50,9 @@
#include <grub/video.h>
#include <grub/memory.h>
#include <grub/i18n.h>
+#if defined (__i386__) || defined (__x86_64__)
+#include <grub/i386/slaunch.h>
+#endif

GRUB_MOD_LICENSE ("GPLv3+");

@@ -161,6 +164,8 @@ efi_boot (struct grub_relocator *rel __attribute__ ((unused)),
static void
normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
{
+ state.edi = SLP_NONE;
+
grub_relocator32_boot (rel, state, 0);
}
#else
diff --git a/include/grub/file.h b/include/grub/file.h
index 31567483c..f08fcda74 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -128,6 +128,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/linux.h b/include/grub/i386/linux.h
index 6aea73ddb..fade29fab 100644
--- a/include/grub/i386/linux.h
+++ b/include/grub/i386/linux.h
@@ -152,6 +152,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;
+ grub_uint32_t size_total;
+ grub_uint32_t setup_type_max;
+ grub_uint32_t mle_header_offset;
+ grub_uint8_t var_len_data[0];
} GRUB_PACKED;

/* Boot parameters for Linux based on 2.6.12. This is used by the setup
@@ -329,9 +340,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/slaunch.h b/include/grub/i386/slaunch.h
new file mode 100644
index 000000000..e5eebd58c
--- /dev/null
+++ b/include/grub/i386/slaunch.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 GRUB_SLAUNCH_TPM_EVT_LOG_SIZE (8 * GRUB_PAGE_SIZE)
+
+#ifndef ASM_FILE
+
+#include <grub/i386/linux.h>
+#include <grub/types.h>
+
+struct grub_slaunch_params
+{
+ struct linux_kernel_params *params;
+ grub_uint32_t mle_start;
+ grub_uint32_t mle_size;
+ void *mle_ptab_mem;
+ grub_uint64_t mle_ptab_target;
+ grub_uint32_t mle_ptab_size;
+ grub_uint32_t mle_header_offset;
+ grub_uint64_t ap_wake_block;
+ grub_uint32_t sinit_acm_base;
+ grub_uint32_t sinit_acm_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);
+
+#endif /* ASM_FILE */
+
+#endif /* GRUB_I386_SLAUNCH_H */
--
2.11.0

2020-05-04 23:28:40

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 16/18] i386/txt: Add Intel TXT ACM module support

From: Ross Philipson <[email protected]>

Signed-off-by: Ross Philipson <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/loader/i386/txt/acmod.c | 575 ++++++++++++++++++++++++++++++++++++++
1 file changed, 575 insertions(+)
create mode 100644 grub-core/loader/i386/txt/acmod.c

diff --git a/grub-core/loader/i386/txt/acmod.c b/grub-core/loader/i386/txt/acmod.c
new file mode 100644
index 000000000..ed2fbab7b
--- /dev/null
+++ b/grub-core/loader/i386/txt/acmod.c
@@ -0,0 +1,575 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/cpu/relocator.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i386/msr.h>
+#include <grub/i386/txt.h>
+
+/*
+ * This checks to see if two numbers multiplied together are larger
+ * than the type that they are. Returns TRUE if OVERFLOWING.
+ * If the first parameter "x" is greater than zero and
+ * if that is true, that the largest possible value 0xFFFFFFFF / "x"
+ * is less than the second parameter "y". If "y" is zero then
+ * it will also fail because no unsigned number is less than zero.
+ */
+static inline int
+multiply_overflow_u32 (grub_uint32_t x, grub_uint32_t y)
+{
+ /* Use x instead of (x > 0)? */
+ return (x > 0) ? ((((grub_uint32_t)(~0))/x) < y) : 0;
+}
+
+/*
+ * These three "plus overflow" functions take a "x" value
+ * and add the "y" value to it and if the two values are
+ * greater than the size of the variable type, they will
+ * overflow the type and end up with a smaller value and
+ * return TRUE - that they did overflow. i.e.
+ */
+static inline int plus_overflow_u32 (grub_uint32_t x, grub_uint32_t y)
+{
+ return ((((grub_uint32_t)(~0)) - x) < y);
+}
+
+static struct grub_txt_acm_info_table*
+get_acmod_info_table (struct grub_txt_acm_header* hdr)
+{
+ grub_uint32_t user_area_off;
+
+ /* Overflow? */
+ if ( plus_overflow_u32 (hdr->header_len, hdr->scratch_size) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length plus scratch size overflows"));
+ return NULL;
+ }
+
+ if ( multiply_overflow_u32 ((hdr->header_len + hdr->scratch_size), 4) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length and scratch size in bytes overflows"));
+ return NULL;
+ }
+
+ /*
+ * This fn assumes that the ACM has already passed at least the initial
+ * is_acmod() checks.
+ */
+
+ user_area_off = (hdr->header_len + hdr->scratch_size) * 4;
+
+ /* Overflow? */
+ if ( plus_overflow_u32 (user_area_off, sizeof(struct grub_txt_acm_info_table)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("user_area_off plus acm_info_table_t size overflows"));
+ return NULL;
+ }
+
+ /* Check that table is within module. */
+ if ( user_area_off + sizeof(struct grub_txt_acm_info_table) > hdr->size * 4 )
+ {
+ /* TODO: Is (grub_uint32_t) correct??? */
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table size too large: %x > %x"),
+ user_area_off + (grub_uint32_t)sizeof(struct grub_txt_acm_info_table), hdr->size * 4);
+ return NULL;
+ }
+
+ /* Overflow? */
+ if ( plus_overflow_u32 ((grub_uint32_t)(unsigned long)hdr, user_area_off) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("hdr plus user_area_off overflows"));
+ return NULL;
+ }
+
+ return (struct grub_txt_acm_info_table *)((unsigned long)hdr + user_area_off);
+}
+
+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 size, id_list_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 )
+ return NULL;
+ id_list_off = info_table->chipset_id_list;
+
+ size = hdr->size * 4;
+
+ /* Overflow? */
+ if ( plus_overflow_u32 (id_list_off, sizeof(struct grub_txt_acm_chipset_id)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("id_list_off plus acm_chipset_id_t size overflows"));
+ return NULL;
+ }
+
+ /* Check that chipset id table is w/in ACM */
+ if ( id_list_off + sizeof(struct grub_txt_acm_chipset_id) > size )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("chipset id list is too big: %x"), id_list_off);
+ return NULL;
+ }
+
+ /* Overflow? */
+ if ( plus_overflow_u32 ((grub_uint32_t)(unsigned long)hdr, id_list_off) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("hdr plus id_list_off overflows"));
+ return NULL;
+ }
+
+ chipset_id_list = (struct grub_txt_acm_chipset_id_list*)
+ ((unsigned long)hdr + id_list_off);
+
+ /* Overflows? */
+ if ( multiply_overflow_u32 (chipset_id_list->count,
+ sizeof(struct grub_txt_acm_chipset_id)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("size of acm_chipset_id_list overflows"));
+ return NULL;
+ }
+
+ if ( plus_overflow_u32 (id_list_off + sizeof(struct grub_txt_acm_chipset_id),
+ chipset_id_list->count * sizeof(struct grub_txt_acm_chipset_id)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("size of all entries overflows"));
+ return NULL;
+ }
+
+ /* Check that all entries are w/in ACM */
+ if ( id_list_off + sizeof(struct grub_txt_acm_chipset_id) +
+ chipset_id_list->count * sizeof(struct grub_txt_acm_chipset_id) > size )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset id entries are too big: %x"),
+ chipset_id_list->count);
+ return NULL;
+ }
+
+ return chipset_id_list;
+}
+
+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 size, id_list_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 )
+ return NULL;
+ id_list_off = info_table->processor_id_list;
+
+ size = hdr->size * 4;
+
+ /* Overflow? */
+ if ( plus_overflow_u32 (id_list_off, sizeof(struct grub_txt_acm_processor_id)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("id_list_off plus acm_processor_id_t size overflows"));
+ return NULL;
+ }
+
+ /* Check that processor id table is w/in ACM */
+ if ( id_list_off + sizeof(struct grub_txt_acm_processor_id) > size )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor id list is too big: %x"), id_list_off);
+ return NULL;
+ }
+
+ /* Overflow? */
+ if ( plus_overflow_u32 ((unsigned long)hdr, id_list_off) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("hdr plus id_list_off overflows"));
+ return NULL;
+ }
+
+ proc_id_list = (struct grub_txt_acm_processor_id_list *)
+ ((unsigned long)hdr + id_list_off);
+
+ /* Overflows? */
+ if ( multiply_overflow_u32 (proc_id_list->count,
+ sizeof(struct grub_txt_acm_processor_id)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("size of acm_processor_id_list overflows"));
+ return NULL;
+ }
+
+ if ( plus_overflow_u32 (id_list_off + sizeof(struct grub_txt_acm_processor_id),
+ proc_id_list->count * sizeof(struct grub_txt_acm_processor_id)) )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("size of all entries overflows"));
+ return NULL;
+ }
+
+ /* Check that all entries are w/in ACM */
+ if ( id_list_off + sizeof(struct grub_txt_acm_processor_id) +
+ proc_id_list->count * sizeof(struct grub_txt_acm_processor_id) > size )
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor id entries are too big: %x"),
+ proc_id_list->count);
+ 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;
+
+ /* First check size */
+ if ( acmod_size < sizeof (struct grub_txt_acm_header) )
+ return 0;
+
+ /* Then check overflow */
+ if ( multiply_overflow_u32 (acm_hdr->size, 4) )
+ return 0;
+
+ /* Then check size equivalency */
+ if ( acmod_size != acm_hdr->size * 4 )
+ return 0;
+
+ /* Then check type and vendor */
+ if ( (acm_hdr->module_type != GRUB_TXT_ACM_MODULE_TYPE) ||
+ (acm_hdr->module_vendor != GRUB_TXT_ACM_MODULE_VENDOR_INTEL) )
+ return 0;
+
+ info_table = get_acmod_info_table (acm_hdr);
+ if ( !info_table )
+ return 0;
+
+ /* Check if ACM UUID is present */
+ if ( grub_memcmp (&(info_table->uuid), GRUB_TXT_ACM_UUID, 16) )
+ return 0;
+
+ if ( type_out )
+ *type_out = 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;
+
+ if ( !sinit_region_base )
+ return NULL;
+
+ if ( bios_data->bios_sinit_size == 0 )
+ 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) )
+ 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 is_sinit_acmod() */
+ info_table = get_acmod_info_table (hdr);
+
+ if ( info_table == NULL )
+ return 0;
+
+ return 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 is_sinit_acmod() */
+ info_table = get_acmod_info_table (hdr);
+
+ if ( info_table == NULL || info_table->version < 3 )
+ return 0;
+
+ return 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;
+}
+
+/* Format of VER.FSBIF and VER.QPIIF registers. */
+typedef union {
+ grub_uint64_t _raw;
+ struct {
+ grub_uint64_t reserved : 31;
+ grub_uint64_t prod_fused : 1;
+ };
+} grub_txt_ver_fsbif_qpiif_t;
+
+int
+grub_txt_acmod_match_platform (struct grub_txt_acm_header *hdr)
+{
+ union grub_txt_didvid didvid;
+ grub_uint32_t fms, ign, i;
+ grub_uint64_t platform_id;
+ grub_txt_ver_fsbif_qpiif_t ver;
+ 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 */
+
+ /* Get chipset fusing, device, and vendor id info */
+ didvid.value = grub_txt_reg_pub_readq (GRUB_TXT_DIDVID);
+
+ ver._raw = grub_txt_reg_pub_readq (GRUB_TXT_VER_FSBIF);
+ if ( (ver._raw & 0xffffffff) == 0xffffffff ||
+ (ver._raw & 0xffffffff) == 0x00 ) /* Need to use VER.QPIIF */
+ ver._raw = grub_txt_reg_pub_readq (GRUB_TXT_VER_QPIIF);
+
+ grub_dprintf ("slaunch", "chipset production fused: %x, "
+ "chipset vendor: 0x%x, device: 0x%x, revision: 0x%x\n",
+ ver.prod_fused, 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._raw & GRUB_TXT_VERSION_DEBUG_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 )
+ 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.vid == chipset_id->vendor_id ) &&
+ (didvid.did == chipset_id->device_id ) &&
+ ( ( ( (chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) == 0) &&
+ (didvid.rid == chipset_id->revision_id) ) ||
+ ( ( (chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) == 1) &&
+ ( (didvid.rid & chipset_id->revision_id) != 0 ) ) ) )
+ break;
+ }
+
+ if ( i >= chipset_id_list->count )
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, N_("chipset id mismatch"));
+ return 0;
+ }
+
+ /* Check if processor family/model/stepping and platform IDs match */
+ info_table = get_acmod_info_table (hdr);
+ if ( !info_table )
+ return 0;
+
+ /*
+ * Logic inverted from oringal to avoid the if block. Not sure what drives
+ * the logic of not checking processor infrmation for version 4 or less.
+ */
+ if ( info_table->version < 4 )
+ return 1;
+
+ proc_id_list = get_acmod_processor_list(hdr);
+ if ( !proc_id_list )
+ return 1;
+
+ 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_("chipset id mismatch"));
+ return 0;
+ }
+
+ return 1;
+}
+
+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_region_size;
+
+ sinit_region_base = (void *)(grub_addr_t) grub_txt_reg_pub_readq (GRUB_TXT_SINIT_BASE);
+ sinit_region_size = (grub_uint32_t) grub_txt_reg_pub_readq (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 (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 (multiply_overflow_u32 (sinit->size, 4))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM size in bytes overflows"));
+ return NULL;
+ }
+
+ if ((sinit->size * 4) > 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 * 4);
+ return NULL;
+ }
+
+ grub_memcpy (sinit_region_base, sinit, sinit->size * 4);
+
+ return sinit_region_base;
+}
--
2.11.0

2020-05-04 23:28:42

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 13/18] i386/slaunch: Add basic platform support for secure launch

From: Ross Philipson <[email protected]>

Signed-off-by: Ross Philipson <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
include/grub/i386/cpuid.h | 13 ++++
include/grub/i386/crfr.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++
include/grub/i386/mmio.h | 90 ++++++++++++++++++++++
include/grub/i386/msr.h | 61 +++++++++++++++
4 files changed, 350 insertions(+)
create mode 100644 include/grub/i386/crfr.h
create mode 100644 include/grub/i386/mmio.h

diff --git a/include/grub/i386/cpuid.h b/include/grub/i386/cpuid.h
index f7ae4b0a4..8176e5d11 100644
--- a/include/grub/i386/cpuid.h
+++ b/include/grub/i386/cpuid.h
@@ -19,6 +19,19 @@
#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_VMX_CPUID_FEATURE (1<<5)
+#define GRUB_SMX_CPUID_FEATURE (1<<6)
+
+/* AMD */
+#define GRUB_AMD_CPUID_FEATURES 0x80000001
+#define GRUB_SVM_CPUID_FEATURE (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..284d6967b
--- /dev/null
+++ b/include/grub/i386/crfr.h
@@ -0,0 +1,186 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_CRFR_H
+#define GRUB_CRFR_H 1
+
+#include <grub/types.h>
+
+/* 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 /* Enable SIMD/MMX2 to use except 16 */
+#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_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_CR0 0
+#define GRUB_CR1 1
+#define GRUB_CR2 2
+#define GRUB_CR3 3
+#define GRUB_CR4 4
+
+#ifdef __x86_64__
+#define read_cr(r, d) asm volatile ("movq %%cr" r ", %0" : "=r" (d))
+#else
+#define read_cr(r, d) asm volatile ("movl %%cr" r ", %0" : "=r" (d))
+#endif
+
+static inline unsigned long
+grub_read_control_register(grub_uint8_t reg)
+{
+ unsigned long data;
+
+ switch (reg)
+ {
+ case GRUB_CR0:
+ read_cr("0", data);
+ break;
+ case GRUB_CR1:
+ read_cr("1", data);
+ break;
+ case GRUB_CR2:
+ read_cr("2", data);
+ break;
+ case GRUB_CR3:
+ read_cr("3", data);
+ break;
+ case GRUB_CR4:
+ read_cr("4", data);
+ break;
+ default:
+ /* TODO: Loudly complain if this is called. Even some kind of BUG() */
+ data = ~0UL;
+ break;
+ }
+
+ return data;
+}
+
+#ifdef __x86_64__
+#define write_cr(r, d) asm volatile ("movq %0, %%cr" r : : "r" (d))
+#else
+#define write_cr(r, d) asm volatile ("movl %0, %%cr" r : : "r" (d))
+#endif
+
+static inline void
+grub_write_control_register(grub_uint8_t reg, unsigned long data)
+{
+ switch (reg)
+ {
+ case GRUB_CR0:
+ write_cr("0", data);
+ break;
+ case GRUB_CR1:
+ write_cr("1", data);
+ break;
+ case GRUB_CR2:
+ write_cr("2", data);
+ break;
+ case GRUB_CR3:
+ write_cr("3", data);
+ break;
+ case GRUB_CR4:
+ write_cr("4", data);
+ break;
+ default:
+ /* TODO: Loudly complain if this is called. Even some kind of BUG() */
+ ;
+ }
+}
+
+#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/mmio.h b/include/grub/i386/mmio.h
new file mode 100644
index 000000000..6f5bf18ce
--- /dev/null
+++ b/include/grub/i386/mmio.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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_I386_MMIO_H
+#define GRUB_I386_MMIO_H 1
+
+#include <grub/types.h>
+
+/* TODO: Are these barirers really needed??? */
+#define grub_mb() asm volatile ("mfence" : : : "memory")
+#define grub_rmb() asm volatile ("lfence" : : : "memory")
+#define grub_wmb() asm volatile ("sfence" : : : "memory")
+#define grub_barrier() asm volatile ("" : : : "memory")
+
+static inline grub_uint8_t
+grub_readb (const volatile void *addr)
+{
+ grub_uint8_t val;
+
+ grub_barrier ();
+ val = (*(volatile grub_uint8_t *) (addr));
+ grub_rmb ();
+
+ return val;
+}
+
+static inline grub_uint32_t
+grub_readl (const volatile void *addr)
+{
+ grub_uint32_t val;
+
+ grub_barrier ();
+ val = (*(volatile grub_uint32_t *) (addr));
+ grub_rmb ();
+
+ return val;
+}
+
+static inline grub_uint64_t
+grub_readq (const volatile void *addr)
+{
+ grub_uint64_t val;
+
+ grub_barrier ();
+ val = (*(volatile grub_uint64_t *) (addr));
+ grub_rmb ();
+
+ return val;
+}
+
+static inline void
+grub_writeb (grub_uint8_t val, volatile void *addr)
+{
+ grub_wmb ();
+ (*(volatile grub_uint8_t *) (addr)) = val;
+ grub_barrier ();
+}
+
+static inline void
+grub_writel (grub_uint32_t val, volatile void *addr)
+{
+ grub_wmb ();
+ (*(volatile grub_uint32_t *) (addr)) = val;
+ grub_barrier ();
+}
+
+static inline void
+grub_writeq (grub_uint64_t val, volatile void *addr)
+{
+ grub_wmb ();
+ (*(volatile grub_uint64_t *) (addr)) = val;
+ grub_barrier ();
+}
+
+#endif /* GRUB_I386_MMIO_H */
diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h
index 1e838c022..f2552ecbc 100644
--- a/include/grub/i386/msr.h
+++ b/include/grub/i386/msr.h
@@ -2,6 +2,9 @@
* 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
@@ -19,6 +22,62 @@
#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)
+
+#define GRUB_MSR_X86_FEATURE_CONTROL 0x0000003a
+#define GRUB_MSR_X86_ENABLE_VMX_IN_SMX (1<<1)
+#define GRUB_MSR_X86_SENTER_FUNCTIONS (0x7f<<8)
+#define GRUB_MSR_X86_SENTER_ENABLE (1<<15)
+
+#define GRUB_MSR_X86_MTRRCAP 0x000000fe
+#define GRUB_MSR_X86_VCNT_MASK 0xff
+
+#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_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 Actuve */
+#define GRUB_MSR_EFER_SVME (1<<12) /* Enable virtualization */
+
+/* AMD Specific */
+#define GRUB_MSR_AMD64_PATCH_LEVEL 0x0000008b
+#define GRUB_MSR_AMD64_PATCH_CLEAR 0xc0010021 /* AMD-specific microcode patch clear */
+#define GRUB_MSR_AMD64_VM_CR 0xc0010114
+#define GRUB_MSR_SVM_VM_CR_SVM_DISABLE 4
+
+/* 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 <grub/err.h>
#include <grub/i386/cpuid.h>
#include <grub/types.h>
@@ -71,4 +130,6 @@ grub_wrmsr (grub_uint32_t msr_id, grub_uint64_t msr_value)
asm volatile ("wrmsr" : : "c" (msr_id), "a" (low), "d" (high));
}

+#endif /* ASM_FILE */
+
#endif /* GRUB_I386_MSR_H */
--
2.11.0

2020-05-04 23:29:16

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 02/18] i386/msr: Rename grub_msr_read() and grub_msr_write()

.. to grub_rdmsr() and grub_wrmsr() respectively. New names are more
obvious than older ones.

Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/commands/i386/rdmsr.c | 2 +-
grub-core/commands/i386/wrmsr.c | 2 +-
include/grub/i386/msr.h | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c
index fa4622f9e..89ece7657 100644
--- a/grub-core/commands/i386/rdmsr.c
+++ b/grub-core/commands/i386/rdmsr.c
@@ -76,7 +76,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/wrmsr.c b/grub-core/commands/i386/wrmsr.c
index 9b7abba7c..e3e9f2ee3 100644
--- a/grub-core/commands/i386/wrmsr.c
+++ b/grub-core/commands/i386/wrmsr.c
@@ -76,7 +76,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/include/grub/i386/msr.h b/include/grub/i386/msr.h
index 7b52b5d61..4fba1b8e0 100644
--- a/include/grub/i386/msr.h
+++ b/include/grub/i386/msr.h
@@ -25,7 +25,7 @@
*/

static inline grub_uint64_t
-grub_msr_read (grub_uint32_t msr_id)
+grub_rdmsr (grub_uint32_t msr_id)
{
grub_uint32_t low, high;

@@ -35,7 +35,7 @@ grub_msr_read (grub_uint32_t msr_id)
}

static inline void
-grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value)
+grub_wrmsr (grub_uint32_t msr_id, grub_uint64_t msr_value)
{
grub_uint32_t low = msr_value, high = msr_value >> 32;

--
2.11.0

2020-05-04 23:29:31

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 08/18] i386/tpm: Add TPM TIS and CRB driver

It will be used by Intel TXT secure launcher introduced
by subsequent patches.

Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/commands/i386/tpm.c | 182 ++++++++++++++++++++++++++++++++++++++++++
include/grub/i386/tpm.h | 36 +++++++++
2 files changed, 218 insertions(+)
create mode 100644 grub-core/commands/i386/tpm.c
create mode 100644 include/grub/i386/tpm.h

diff --git a/grub-core/commands/i386/tpm.c b/grub-core/commands/i386/tpm.c
new file mode 100644
index 000000000..ff29c2e85
--- /dev/null
+++ b/grub-core/commands/i386/tpm.c
@@ -0,0 +1,182 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * TPM TIS and CRB driver.
+ *
+ * Note: It is suggested to not use this driver together with UEFI TPM driver.
+ */
+
+#include <grub/command.h>
+#include <grub/dl.h>
+#include <grub/err.h>
+#include <grub/i386/memory.h>
+#include <grub/i386/mmio.h>
+#include <grub/i386/tpm.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define TPM_MMIO_BASE 0xfed40000
+
+/* TIS registers. */
+#define TPM_ACCESS 0x0000
+#define TPM_INTF_CAPABILITY 0x0014
+#define TPM_INTERFACE_ID 0x0030
+
+/* CRB registers. */
+#define TPM_LOC_CTRL 0x0008
+
+#define TPM_12_TIS_INTF_12 0x0
+#define TPM_12_TIS_INTF_13 0x2
+#define TPM_20_TIS_INTF_13 0x3
+
+#define TPM_CRB_INTF_ACTIVE 0x1
+
+#define TIS_RELINQUISH_LCL 0x20
+#define CRB_RELINQUISH_LCL 0x0002
+
+/* TODO: Do we need GRUB_PACKED for unions below??? */
+
+union tpm_interface_id
+{
+ grub_uint32_t raw;
+ struct
+ {
+ grub_uint32_t interface_type:4;
+ grub_uint32_t interface_version:4;
+ grub_uint32_t cap_locality:1;
+ grub_uint32_t reserved_0:4;
+ grub_uint32_t cap_tis:1;
+ grub_uint32_t cap_crb:1;
+ grub_uint32_t cap_ifres:2;
+ grub_uint32_t interface_selector:2;
+ grub_uint32_t intf_sel_lock:1;
+ grub_uint32_t reserved_1:4;
+ grub_uint32_t reserved_2:8;
+ };
+} GRUB_PACKED;
+typedef union tpm_interface_id tpm_interface_id_t;
+
+union tpm_intf_capability
+{
+ grub_uint32_t raw;
+ struct
+ {
+ grub_uint32_t data_avail_int_support:1;
+ grub_uint32_t sts_valid_int_support:1;
+ grub_uint32_t locality_change_int_support:1;
+ grub_uint32_t interrupt_level_high:1;
+ grub_uint32_t interrupt_level_low:1;
+ grub_uint32_t interrupt_edge_rising:1;
+ grub_uint32_t interrupt_edge_falling:1;
+ grub_uint32_t command_ready_int_support:1;
+ grub_uint32_t burst_count_static:1;
+ grub_uint32_t data_transfer_size_support:2;
+ grub_uint32_t reserved_0:17;
+ grub_uint32_t interface_version:3;
+ grub_uint32_t reserved_1:1;
+ };
+} GRUB_PACKED;
+typedef union tpm_intf_capability tpm_intf_capability_t;
+
+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_lcl (grub_uint8_t lcl)
+{
+ grub_addr_t addr = TPM_MMIO_BASE + lcl * GRUB_PAGE_SIZE;
+
+ if (tpm_intf == TPM_INTF_TIS)
+ grub_writeb (TIS_RELINQUISH_LCL, (void *) (addr + TPM_ACCESS));
+ else if (tpm_intf == TPM_INTF_CRB)
+ grub_writel (CRB_RELINQUISH_LCL, (void *) (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 VER: %s\nTPM INTF: %s\n", tpm_ver_s, tpm_intf_s);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd_tpm_type;
+
+GRUB_MOD_INIT (tpm)
+{
+ tpm_interface_id_t intf_id;
+ tpm_intf_capability_t intf_cap;
+
+ cmd_tpm_type = grub_register_command ("tpm_type", grub_cmd_tpm_type,
+ NULL, N_("Show TPM version and interface type."));
+
+ intf_cap.raw = grub_readl ((void *)(grub_addr_t) (TPM_MMIO_BASE + TPM_INTF_CAPABILITY));
+
+ if (intf_cap.interface_version == TPM_12_TIS_INTF_12 ||
+ intf_cap.interface_version == TPM_12_TIS_INTF_13)
+ {
+ tpm_ver = GRUB_TPM_12;
+ tpm_intf = TPM_INTF_TIS;
+ return;
+ }
+
+ if (intf_cap.interface_version != TPM_20_TIS_INTF_13)
+ return;
+
+ tpm_ver = GRUB_TPM_20;
+
+ intf_id.raw = grub_readl ((void *)(grub_addr_t) (TPM_MMIO_BASE + TPM_INTERFACE_ID));
+
+ tpm_intf = (intf_id.interface_type == TPM_CRB_INTF_ACTIVE) ? TPM_INTF_CRB : TPM_INTF_TIS;
+}
+
+GRUB_MOD_FINI (tpm)
+{
+ grub_unregister_command (cmd_tpm_type);
+}
diff --git a/include/grub/i386/tpm.h b/include/grub/i386/tpm.h
new file mode 100644
index 000000000..ae8d4a27d
--- /dev/null
+++ b/include/grub/i386/tpm.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GRUB_I386_TPM_H
+#define GRUB_I386_TPM_H 1
+
+#include <grub/types.h>
+
+typedef enum
+ {
+ GRUB_TPM_NONE = 0,
+ GRUB_TPM_12,
+ GRUB_TPM_20
+ }
+grub_tpm_ver_t;
+
+extern grub_tpm_ver_t grub_get_tpm_ver (void);
+extern void grub_tpm_relinquish_lcl (grub_uint8_t lcl);
+
+#endif /* GRUB_I386_TPM_H */
--
2.11.0

2020-05-04 23:29:41

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 12/18] i386/efi: Report UEFI Secure Boot status to the Linux kernel

Otherwise the kernel does not know its state and cannot enable various
security features depending on UEFI Secure Boot.

Signed-off-by: Ignat Korchagin <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/loader/i386/linux.c | 86 ++++++++++++++++++++++++++++++++++++++++++-
include/grub/i386/linux.h | 14 ++++++-
2 files changed, 97 insertions(+), 3 deletions(-)

diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
index ac1fae72e..952eb1191 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -397,6 +397,87 @@ grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size,
return 0;
}

+#ifdef GRUB_MACHINE_EFI
+/*
+ * Determine whether we're in secure boot mode.
+ *
+ * Please keep the logic in sync with the Linux kernel,
+ * drivers/firmware/efi/libstub/secureboot.c:efi_get_secureboot().
+ */
+static grub_uint8_t
+grub_efi_get_secureboot (void)
+{
+ grub_efi_guid_t efi_variable_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID;
+ grub_efi_guid_t efi_shim_lock_guid = GRUB_EFI_SHIM_LOCK_GUID;
+ grub_efi_status_t status;
+ grub_efi_uint32_t attr = 0;
+ grub_size_t size = 0;
+ grub_uint8_t *secboot = NULL;
+ grub_uint8_t *setupmode = NULL;
+ grub_uint8_t *moksbstate = NULL;
+ grub_uint8_t secureboot = GRUB_LINUX_EFI_SECUREBOOT_MODE_UNKNOWN;
+ const char *secureboot_str = "UNKNOWN";
+
+ status = grub_efi_get_variable ("SecureBoot", &efi_variable_guid,
+ &size, (void **) &secboot);
+
+ if (status == GRUB_EFI_NOT_FOUND)
+ {
+ secureboot = GRUB_LINUX_EFI_SECUREBOOT_MODE_DISABLED;
+ goto out;
+ }
+
+ if (status != GRUB_EFI_SUCCESS)
+ goto out;
+
+ status = grub_efi_get_variable ("SetupMode", &efi_variable_guid,
+ &size, (void **) &setupmode);
+
+ if (status != GRUB_EFI_SUCCESS)
+ goto out;
+
+ if ((*secboot == 0) || (*setupmode == 1))
+ {
+ secureboot = GRUB_LINUX_EFI_SECUREBOOT_MODE_DISABLED;
+ goto out;
+ }
+
+ /*
+ * See if a user has put the shim into insecure mode. If so, and if the
+ * variable doesn't have the runtime attribute set, we might as well
+ * honor that.
+ */
+ status = grub_efi_get_variable_with_attributes ("MokSBState", &efi_shim_lock_guid,
+ &size, (void **) &moksbstate, &attr);
+
+ /* If it fails, we don't care why. Default to secure. */
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ secureboot = GRUB_LINUX_EFI_SECUREBOOT_MODE_ENABLED;
+ goto out;
+ }
+
+ if (!(attr & GRUB_EFI_VARIABLE_RUNTIME_ACCESS) && *moksbstate == 1)
+ secureboot = GRUB_LINUX_EFI_SECUREBOOT_MODE_DISABLED;
+
+ secureboot = GRUB_LINUX_EFI_SECUREBOOT_MODE_ENABLED;
+
+ out:
+ grub_free (moksbstate);
+ grub_free (setupmode);
+ grub_free (secboot);
+
+ if (secureboot == GRUB_LINUX_EFI_SECUREBOOT_MODE_DISABLED)
+ secureboot_str = "Disabled";
+ else if (secureboot == GRUB_LINUX_EFI_SECUREBOOT_MODE_ENABLED)
+ secureboot_str = "Enabled";
+
+ grub_dprintf ("linux", "UEFI Secure Boot state: %s\n", secureboot_str);
+
+ return secureboot;
+}
+#endif
+
static grub_err_t
grub_linux_boot (void)
{
@@ -579,6 +660,9 @@ grub_linux_boot (void)
grub_efi_uintn_t efi_desc_size;
grub_size_t efi_mmap_target;
grub_efi_uint32_t efi_desc_version;
+
+ ctx.params->secure_boot = grub_efi_get_secureboot ();
+
err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
&efi_desc_size, &efi_desc_version);
if (err)
@@ -790,7 +874,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),

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.padding10 = 0;
+ linux_params.ps_mouse = linux_params.padding11 = 0;
linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;

/* These two are used (instead of cmd_line_ptr) by older versions of Linux,
diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h
index ce30e7fb0..6aea73ddb 100644
--- a/include/grub/i386/linux.h
+++ b/include/grub/i386/linux.h
@@ -49,6 +49,12 @@
/* Maximum number of MBR signatures to store. */
#define EDD_MBR_SIG_MAX 16

+/* Possible values for Linux secure_boot kernel parameter. */
+#define GRUB_LINUX_EFI_SECUREBOOT_MODE_UNSET 0
+#define GRUB_LINUX_EFI_SECUREBOOT_MODE_UNKNOWN 1
+#define GRUB_LINUX_EFI_SECUREBOOT_MODE_DISABLED 2
+#define GRUB_LINUX_EFI_SECUREBOOT_MODE_ENABLED 3
+
#ifdef __x86_64__

#define GRUB_LINUX_EFI_SIGNATURE \
@@ -275,7 +281,11 @@ struct linux_kernel_params

grub_uint8_t mmap_size; /* 1e8 */

- grub_uint8_t padding9[0x1f1 - 0x1e9];
+ grub_uint8_t padding9[0x1ec - 0x1e9];
+
+ grub_uint8_t secure_boot; /* 1ec */
+
+ grub_uint8_t padding10[0x1f1 - 0x1ed];

/* Linux setup header copy - BEGIN. */
grub_uint8_t setup_sects; /* The size of the setup in sectors */
@@ -286,7 +296,7 @@ struct linux_kernel_params
grub_uint16_t vid_mode; /* Video mode control */
grub_uint16_t root_dev; /* Default root device number */

- grub_uint8_t padding10; /* 1fe */
+ grub_uint8_t padding11; /* 1fe */
grub_uint8_t ps_mouse; /* 1ff */

grub_uint16_t jump; /* Jump instruction */
--
2.11.0

2020-05-04 23:30:15

by Daniel Kiper

[permalink] [raw]
Subject: [GRUB PATCH RFC 15/18] i386/txt: Add Intel TXT core implementation

From: Ross Philipson <[email protected]>

Signed-off-by: Ross Philipson <[email protected]>
Signed-off-by: Daniel Kiper <[email protected]>
---
grub-core/loader/i386/txt/txt.c | 887 ++++++++++++++++++++++++++++++++++++++++
include/grub/i386/memory.h | 5 +
2 files changed, 892 insertions(+)
create mode 100644 grub-core/loader/i386/txt/txt.c

diff --git a/grub-core/loader/i386/txt/txt.c b/grub-core/loader/i386/txt/txt.c
new file mode 100644
index 000000000..3e25890c3
--- /dev/null
+++ b/grub-core/loader/i386/txt/txt.c
@@ -0,0 +1,887 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/acpi.h>
+#include <grub/cpu/relocator.h>
+#include <grub/i386/cpuid.h>
+#include <grub/i386/msr.h>
+#include <grub/i386/crfr.h>
+#include <grub/i386/txt.h>
+#include <grub/i386/linux.h>
+#include <grub/i386/memory.h>
+#include <grub/i386/slaunch.h>
+#include <grub/i386/tpm.h>
+
+#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 grub_err_t
+enable_smx_mode (void)
+{
+ grub_uint32_t caps;
+
+ /* 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 void
+grub_txt_smx_parameters (struct grub_smx_parameters *params)
+{
+ grub_uint32_t index = 0, eax, ebx, ecx, param_type;
+
+ grub_memset (params, 0, sizeof(struct grub_smx_supported_versions));
+
+ 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)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Too many ACM versions"));
+ break;
+ }
+ 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:
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SMX parameter"));
+ param_type = GRUB_SMX_PARAMETER_NULL;
+ }
+
+ ++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++;
+ }
+}
+
+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;
+
+ cr0 = grub_read_control_register (GRUB_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_control_register (GRUB_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"));
+
+ grub_txt_smx_parameters (&params);
+
+ 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_HANLDING)
+ 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] = %Lx ERROR"), i, mcg_stat);
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+save_mtrrs (struct grub_txt_os_mle_data *os_mle_data)
+{
+ grub_uint64_t i;
+
+ os_mle_data->saved_bsp_mtrrs.default_mem_type =
+ grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE);
+
+ os_mle_data->saved_bsp_mtrrs.mtrr_vcnt =
+ grub_rdmsr (GRUB_MSR_X86_MTRRCAP) & GRUB_MSR_X86_VCNT_MASK;
+
+ if (os_mle_data->saved_bsp_mtrrs.mtrr_vcnt > GRUB_SL_MAX_VARIABLE_MTRRS)
+ {
+ /* 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",
+ os_mle_data->saved_bsp_mtrrs.mtrr_vcnt,
+ GRUB_SL_MAX_VARIABLE_MTRRS);
+ os_mle_data->saved_bsp_mtrrs.mtrr_vcnt = GRUB_SL_MAX_VARIABLE_MTRRS;
+ }
+
+ for (i = 0; i < os_mle_data->saved_bsp_mtrrs.mtrr_vcnt; ++i)
+ {
+ os_mle_data->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physmask =
+ grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + i * 2);
+ os_mle_data->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physbase =
+ grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + i * 2);
+ }
+}
+
+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 */
+
+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_PHYSMASK0 + ndx*2);
+ mtrr_physmask.v = 0;
+ grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2, 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 if that MTRR's Size */
+ 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_PHYSBASE0 + ndx*2);
+ mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) &
+ SINIT_MTRR_MASK;
+ mtrr_physbase.type = mem_type;
+ grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2, mtrr_physbase.raw);
+
+ mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2);
+ mtrr_physmask.mask = ~(mtrr_s - 1) & SINIT_MTRR_MASK;
+ mtrr_physmask.v = 1;
+ grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2, 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"));
+ }
+
+ ndx = 0;
+
+ while ( num_pages > 0 )
+ {
+ /* Set the base of the current MTRR */
+ mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2);
+ mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) &
+ SINIT_MTRR_MASK;
+ mtrr_physbase.type = mem_type;
+ grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2, 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_PHYSMASK0 + ndx*2);
+ mtrr_physmask.mask = ~(pages_in_range - 1) & SINIT_MTRR_MASK;
+ mtrr_physmask.v = 1;
+ grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2, 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_control_register (GRUB_CR0);
+ grub_write_control_register (GRUB_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_control_register (GRUB_CR4);
+ grub_write_control_register (GRUB_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);
+ if ( err )
+ return err;
+
+ /* Undo some of earlier changes and enable our new settings */
+
+ /* Flush caches */
+ asm volatile ("wbinvd");
+
+ /* Enable MTRRs */
+ set_all_mtrrs (1);
+
+ /* Restore CR0 (cacheing) */
+ grub_write_control_register (GRUB_CR0, cr0);
+
+ /* Restore CR4 (global pages) */
+ grub_write_control_register (GRUB_CR4, cr4);
+
+ /* Restore flags */
+ grub_write_flags_register (eflags);
+
+ return GRUB_ERR_NONE;
+}
+
+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;
+ struct grub_txt_os_mle_data *os_mle_data;
+ struct grub_txt_os_sinit_data *os_sinit_data;
+ struct grub_txt_heap_end_element *heap_end_element;
+ struct grub_txt_heap_event_log_pointer2_1_element *heap_event_log_pointer2_1_element;
+#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 ();
+
+ /* OS/loader to MLE data. */
+
+ os_mle_data = grub_txt_os_mle_data_start (txt_heap);
+ size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t));
+ *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t);
+
+ grub_memset (os_mle_data, 0, sizeof (*os_mle_data));
+
+ os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION;
+ os_mle_data->zero_page_addr = (grub_uint32_t)(grub_addr_t) slparams->params;
+ os_mle_data->saved_misc_enable_msr = grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE);
+
+ os_mle_data->ap_wake_block = slparams->ap_wake_block;
+
+ save_mtrrs (os_mle_data);
+
+ /* OS/loader to SINIT data. */
+
+ 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);
+ 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) +
+ sizeof (struct grub_txt_heap_end_element);
+
+ if (grub_get_tpm_ver () == GRUB_TPM_12)
+ *size += sizeof (struct grub_txt_heap_tpm_event_log_element);
+ else if (grub_get_tpm_ver () == GRUB_TPM_20)
+ *size += sizeof (struct grub_txt_heap_event_log_pointer2_1_element);
+ else
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM version"));
+
+ grub_memset (os_sinit_data, 0, *size);
+
+#ifdef GRUB_MACHINE_EFI
+ rsdp = grub_acpi_get_rsdpv2 ();
+
+ if (rsdp == NULL)
+ return grub_printf ("WARNING: ACPI RSDP 2.0 missing\n");
+
+ os_sinit_data->efi_rsdt_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);
+
+ /* CBnT bits 5:4 must be 11b, since D/A mapping is the only one supported. */
+ os_sinit_data->capabilities = GRUB_TXT_CAPS_TPM_12_NO_LEGACY_PCR_USAGE |
+ GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE;
+
+ /* Choose monitor RLP wakeup mechanism first. */
+ 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)
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("TPM 1.2 is not supported"));
+ else
+ {
+ 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;
+
+ heap_event_log_pointer2_1_element =
+ (struct grub_txt_heap_event_log_pointer2_1_element *) os_sinit_data->ext_data_elts;
+ heap_event_log_pointer2_1_element->type = GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1;
+ heap_event_log_pointer2_1_element->size = sizeof (*heap_event_log_pointer2_1_element);
+ heap_event_log_pointer2_1_element->phys_addr = slparams->tpm_evt_log_base;
+ heap_event_log_pointer2_1_element->allocated_event_container_size = slparams->tpm_evt_log_size;
+
+ heap_end_element = (struct grub_txt_heap_end_element *)
+ ((grub_addr_t) heap_event_log_pointer2_1_element + heap_event_log_pointer2_1_element->size);
+ heap_end_element->type = GRUB_TXT_HEAP_EXTDATA_TYPE_END;
+ heap_end_element->size = sizeof (*heap_end_element);
+ }
+
+ /*
+ * TODO: TXT spec: Note: BiosDataSize + OsMleDataSize + OsSinitDataSize + SinitMleDataSize
+ * must be less than or equal to TXT.HEAP.SIZE, TXT spec, p. 102.
+ */
+
+ 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? Yes, because MLE image may not
+ * start and end at PTE (page) and PDE (2 MiB) boundary...
+ */
+ return ((((mle_size / GRUB_PAGE_SIZE) + 2) / 512) + 1 +
+ (((mle_size / (512 * GRUB_PAGE_SIZE)) + 2) / 512) + 1 + 1) * 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 requirments here.
+ *
+ * TODO: This function is not able to cover MLEs larger than 1 GiB. Fix it!!!
+ * After fixing inrease 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
+ {
+ *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);
+ }
+ } while (mle_off < 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)
+{
+ grub_uint64_t data;
+ grub_uint8_t *data8 = (grub_uint8_t *) &data;
+ int i;
+
+ data = grub_txt_reg_pub_readq (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, !!(data & GRUB_TXT_STS_SENTER_DONE),
+ !!(data & GRUB_TXT_STS_SEXIT_DONE),
+ !!(data & GRUB_TXT_STS_MEM_CONFIG_LOCK),
+ !!(data & GRUB_TXT_STS_PRIVATE_OPEN),
+ !!(data & GRUB_TXT_STS_LOCALITY1_OPEN),
+ !!(data & GRUB_TXT_STS_LOCALITY2_OPEN));
+
+ /* Only least significant byte has a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_ESTS) & 0x00000000000000ff;
+ grub_printf (" TXT.ESTS: 0x%02" PRIxGRUB_UINT64_T "\n"
+ " TXT_RESET.STS: %d\n", data,
+ !!(data & GRUB_TXT_ESTS_TXT_RESET));
+
+ data = grub_txt_reg_pub_readq (GRUB_TXT_E2STS);
+ grub_printf (" TXT.E2STS: 0x%016" PRIxGRUB_UINT64_T "\n"
+ " SECRETS.STS: %d\n", data,
+ !!(data & GRUB_TXT_E2STS_SECRETS));
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_ERRORCODE) & 0x00000000ffffffff;
+ grub_printf (" TXT.ERRORCODE: 0x%08" PRIxGRUB_UINT64_T "\n", data);
+
+ data = grub_txt_reg_pub_readq (GRUB_TXT_DIDVID);
+ grub_printf (" TXT.DIDVID: 0x%016" PRIxGRUB_UINT64_T "\n"
+ " VID: 0x%04" PRIxGRUB_UINT64_T "\n"
+ " DID: 0x%04" PRIxGRUB_UINT64_T "\n"
+ " RID: 0x%04" PRIxGRUB_UINT64_T "\n"
+ " ID-EXT: 0x%04" PRIxGRUB_UINT64_T "\n",
+ data, data & 0x000000000000ffff,
+ (data & 0x00000000ffff0000) >> 16,
+ (data & 0x0000ffff00000000) >> 32, data >> 48);
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_VER_FSBIF) & 0x00000000ffffffff;
+ grub_printf (" TXT.VER.FSBIF: 0x%08" PRIxGRUB_UINT64_T "\n", data);
+
+ if ((data != 0x00000000) && (data != 0xffffffff))
+ grub_printf (" DEBUG.FUSE: %d\n", !!(data & GRUB_TXT_VER_FSBIF_DEBUG_FUSE));
+ else
+ {
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_VER_QPIIF) & 0x00000000ffffffff;
+ grub_printf (" TXT.VER.QPIIF: 0x%08" PRIxGRUB_UINT64_T "\n"
+ " DEBUG.FUSE: %d\n", data,
+ !!(data & GRUB_TXT_VER_QPIIF_DEBUG_FUSE));
+ }
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_SINIT_BASE) & 0x00000000ffffffff;
+ grub_printf (" TXT.SINIT.BASE: 0x%08" PRIxGRUB_UINT64_T "\n", data);
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_SINIT_SIZE) & 0x00000000ffffffff;
+ grub_printf (" TXT.SINIT.SIZE: %" PRIuGRUB_UINT64_T
+ " B (0x%" PRIxGRUB_UINT64_T ")\n", data, data);
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_HEAP_BASE) & 0x00000000ffffffff;
+ grub_printf (" TXT.HEAP.BASE: 0x%08" PRIxGRUB_UINT64_T "\n", data);
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_HEAP_SIZE) & 0x00000000ffffffff;
+ grub_printf (" TXT.HEAP.SIZE: %" PRIuGRUB_UINT64_T
+ " B (0x%" PRIxGRUB_UINT64_T ")\n", data, data);
+
+ /* Only least significant 4 bytes have a meaning. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_DPR) & 0x00000000ffffffff;
+ grub_printf (" TXT.DPR: 0x%08" PRIxGRUB_UINT64_T "\n"
+ " LOCK: %d\n"
+ " TOP: 0x%08" PRIxGRUB_UINT64_T "\n"
+ " SIZE: %" PRIuGRUB_UINT64_T " MiB\n",
+ data, !!(data & (1 << 0)), (data & 0xfff00000),
+ (data & 0x00000ff0) >> 4);
+
+ grub_printf (" TXT.PUBLIC.KEY:\n");
+
+ for (i = 0; i < 4; ++i)
+ {
+ /* TODO: Check relevant MSRs on SGX platforms. */
+ data = grub_txt_reg_pub_readq (GRUB_TXT_PUBLIC_KEY + i * sizeof (grub_uint64_t));
+ grub_printf (" %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x%s", data8[0], data8[1],
+ data8[2], data8[3], data8[4], data8[5], data8[6], data8[7],
+ (i < 3) ? ":\n" : "\n");
+ }
+}
+
+grub_err_t
+grub_txt_boot_prepare (struct grub_slaunch_params *slparams)
+{
+ grub_err_t err;
+ struct grub_txt_mle_header *mle_header;
+ struct grub_txt_acm_header *sinit_base;
+
+ sinit_base = grub_txt_sinit_select (grub_slaunch_module ());
+
+ if (sinit_base == NULL)
+ return grub_errno;
+
+ err = init_txt_heap (slparams, sinit_base);
+
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ /* Update the MLE header. */
+ mle_header = (struct grub_txt_mle_header *)(grub_addr_t) (slparams->mle_start + slparams->mle_header_offset);
+ mle_header->first_valid_page = 0;
+ mle_header->mle_end = slparams->mle_size;
+
+ slparams->sinit_acm_base = (grub_uint32_t)(grub_addr_t) sinit_base;
+ slparams->sinit_acm_size = sinit_base->size * 4;
+
+ grub_tpm_relinquish_lcl (0);
+
+ err = set_mtrrs_for_acmod (sinit_base);
+ if (err)
+ return grub_error (err, N_("secure launch failed to set MTRRs for ACM"));
+
+ err = grub_txt_prepare_cpu ();
+ if ( err )
+ return err;
+
+ 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;
+}
diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h
index c64529630..56f64855b 100644
--- a/include/grub/i386/memory.h
+++ b/include/grub/i386/memory.h
@@ -22,6 +22,7 @@

#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
@@ -43,8 +44,12 @@

#define GRUB_MMAP_MALLOC_LOW 1

+#include <grub/misc.h>
#include <grub/types.h>

+#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;
--
2.11.0

2020-05-05 14:42:36

by Lukasz Hawrylko

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
> Hi,
>
> This is an RFC patchset for the GRUB introducing the Intel TXT secure launcher.
> This is a part of larger work known as the TrenchBoot. Patchset can be split
> into two distinct parts:
> - 01-12: preparatory patches,
> - 13-18: the Intel TXT secure launcher itself.
>
> The initial implementation of the Intel TXT secure launcher works. However,
> there are still some missing bits and pieces, e.g.:
> - SINIT ACM auto loader,
> - lack of RMRR support,
> - lack of support for MLEs larger than 1 GiB,
> - lack of TPM 1.2 support.
> - various fixes and cleanups.
>
> Commands introduced by this patchset: tpm_type, slaunch, slaunch_module (not
> required on server platforms) and slaunch_state (useful for checking platform
> configuration and state; based on tboot's txt-stat).
>
> Daniel
>

Hi Daniel

Your patch looks promising, however I have few concerns.

In OS-MLE table there is a buffer for TPM event log, however I see that
you are not using it, but instead allocate space somewhere in the
memory. I am just wondering if, from security perspective, it will be
better to use memory from TXT heap for event log, like we do in TBOOT.

There is a function that verifies if platform is TXT capable
-grub_txt_verify_platform(), it only checks SMX and GETSEC features.
Although BIOS should enforce both VMX and VT-d enabled when enabling
TXT, I think that adding these check here as redundancy may be a good
idea. The same situation is with TPM presence.

I suggest to add possibility to skip TXT launch when last boot ended
with TXT error. This option can avoid boot loops when something goes
wrong.

How will you read LCP from storage? I see that there is slaunch_module
command that currently you are using only for loading SINIT. In the
future it can be expanded to support LCP file too, what do you think?

Do not forget to apply changes required by latest Intel's platforms, you
should check following commits in TBOOT's repository: 2f03b57ffdba,
fe2dddd742dc.

Lukasz

2020-05-05 17:33:54

by Matthew Garrett

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 12/18] i386/efi: Report UEFI Secure Boot status to the Linux kernel

On Mon, May 4, 2020 at 4:25 PM Daniel Kiper <[email protected]> wrote:
>
> Otherwise the kernel does not know its state and cannot enable various
> security features depending on UEFI Secure Boot.

I think this needs more context. If the kernel is loaded via the EFI
boot stub, the kernel is aware of the UEFI secure boot state. Why
duplicate this functionality in order to avoid the EFI stub?

2020-05-06 13:38:45

by Daniel Kiper

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 12/18] i386/efi: Report UEFI Secure Boot status to the Linux kernel

On Tue, May 05, 2020 at 10:29:05AM -0700, Matthew Garrett wrote:
> On Mon, May 4, 2020 at 4:25 PM Daniel Kiper <[email protected]> wrote:
> >
> > Otherwise the kernel does not know its state and cannot enable various
> > security features depending on UEFI Secure Boot.
>
> I think this needs more context. If the kernel is loaded via the EFI
> boot stub, the kernel is aware of the UEFI secure boot state. Why
> duplicate this functionality in order to avoid the EFI stub?

It seems to me that this issue was discussed here [1] and here [2].
So, if you want me to improve the commit message I am OK with that.

Additionally, FYI I am not happy with that patch too. So, if somebody
has better idea how to do that then I am happy to discuss it here.

Daniel

[1] https://lkml.org/lkml/2020/3/25/982
[2] https://lkml.org/lkml/2020/3/26/985

2020-05-06 18:42:25

by Matthew Garrett

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 12/18] i386/efi: Report UEFI Secure Boot status to the Linux kernel

On Wed, May 6, 2020 at 6:33 AM Daniel Kiper <[email protected]> wrote:
>
> On Tue, May 05, 2020 at 10:29:05AM -0700, Matthew Garrett wrote:
> > On Mon, May 4, 2020 at 4:25 PM Daniel Kiper <[email protected]> wrote:
> > >
> > > Otherwise the kernel does not know its state and cannot enable various
> > > security features depending on UEFI Secure Boot.
> >
> > I think this needs more context. If the kernel is loaded via the EFI
> > boot stub, the kernel is aware of the UEFI secure boot state. Why
> > duplicate this functionality in order to avoid the EFI stub?
>
> It seems to me that this issue was discussed here [1] and here [2].
> So, if you want me to improve the commit message I am OK with that.

Yes, I think just providing an explanation for why it's currently
necessary for you to duplicate this is reasonable.

2020-05-07 10:51:55

by Daniel Kiper

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 12/18] i386/efi: Report UEFI Secure Boot status to the Linux kernel

On Wed, May 06, 2020 at 11:36:49AM -0700, Matthew Garrett wrote:
> On Wed, May 6, 2020 at 6:33 AM Daniel Kiper <[email protected]> wrote:
> >
> > On Tue, May 05, 2020 at 10:29:05AM -0700, Matthew Garrett wrote:
> > > On Mon, May 4, 2020 at 4:25 PM Daniel Kiper <[email protected]> wrote:
> > > >
> > > > Otherwise the kernel does not know its state and cannot enable various
> > > > security features depending on UEFI Secure Boot.
> > >
> > > I think this needs more context. If the kernel is loaded via the EFI
> > > boot stub, the kernel is aware of the UEFI secure boot state. Why
> > > duplicate this functionality in order to avoid the EFI stub?
> >
> > It seems to me that this issue was discussed here [1] and here [2].
> > So, if you want me to improve the commit message I am OK with that.
>
> Yes, I think just providing an explanation for why it's currently
> necessary for you to duplicate this is reasonable.

Sure, will do!

Daniel

2020-05-07 11:13:47

by Daniel Kiper

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

Hi Łukasz,

On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
> > Hi,
> >
> > This is an RFC patchset for the GRUB introducing the Intel TXT secure launcher.
> > This is a part of larger work known as the TrenchBoot. Patchset can be split
> > into two distinct parts:
> > - 01-12: preparatory patches,
> > - 13-18: the Intel TXT secure launcher itself.
> >
> > The initial implementation of the Intel TXT secure launcher works. However,
> > there are still some missing bits and pieces, e.g.:
> > - SINIT ACM auto loader,
> > - lack of RMRR support,
> > - lack of support for MLEs larger than 1 GiB,
> > - lack of TPM 1.2 support.
> > - various fixes and cleanups.
> >
> > Commands introduced by this patchset: tpm_type, slaunch, slaunch_module (not
> > required on server platforms) and slaunch_state (useful for checking platform
> > configuration and state; based on tboot's txt-stat).
> >
> > Daniel
> >
>
> Hi Daniel
>
> Your patch looks promising, however I have few concerns.

Below I will be referring to the Intel Trusted Execution Technology
(Intel TXT), Software Development Guide, December 2019, Revision 016.
So, the latest and greatest...

> In OS-MLE table there is a buffer for TPM event log, however I see that
> you are not using it, but instead allocate space somewhere in the

I think that this part requires more discussion. In my opinion we should
have this region dynamically allocated because it gives us more flexibility.
Of course there is a question about the size of this buffer too. I am
not sure about that because I have not checked yet how many log entries
are created by the SINIT ACM. Though probably it should not be large...

> memory. I am just wondering if, from security perspective, it will be
> better to use memory from TXT heap for event log, like we do in TBOOT.

Appendix F, TPM Event Log, has following sentence: There are no
requirements for event log to be in DMA protected memory – SINIT will
not enforce it.

I was thinking about it and it seems to me that the TPM event log does
not require any special protections. Any changes in it can be quickly
detected by comparing hashes with the TPM PCRs. Does not it?

> There is a function that verifies if platform is TXT capable
> -grub_txt_verify_platform(), it only checks SMX and GETSEC features.
> Although BIOS should enforce both VMX and VT-d enabled when enabling
> TXT, I think that adding these check here as redundancy may be a good

The TXT spec has the following pseudocode:

//
// Intel TXT detection
// Execute on all logical processors for compatibility with
// multiple processor systems
//
1. CPUID(EAX=1);
2. IF (SMX not supported) OR (VMX not supported) {
3. Fail measured environment startup;
4. }

However, a few lines above you can find this:

Lines 1 - 4: Before attempting to launch the measured environment, the
system software should check that all logical processors support VMX and
SMX (the check for VMX support is not necessary if the environment to be
launched will not use VMX).

Hence, AIUI, I am allowed to check SMX only. And I do not think that the
bootloader should enforce VMX. If the kernel wants VMX then it should
check the platform config. The booloader should just look for features
which are really required to properly execute GETSEC[SENTER].

> idea. The same situation is with TPM presence.

The TPM presence is checked in init_txt_heap(). However, we can do it earlier.

> I suggest to add possibility to skip TXT launch when last boot ended
> with TXT error. This option can avoid boot loops when something goes
> wrong.

grub_txt_verify_platform() checks TXT_RESET.STS bit and fails if it is
set. This produces an error during boot. Well, but it does not prevent
it... :-( Probably I have to fix it...

> How will you read LCP from storage? I see that there is slaunch_module
> command that currently you are using only for loading SINIT. In the
> future it can be expanded to support LCP file too, what do you think?

I think that we should add e.g. slaunch_lcp command and do not overload
slaunch_module command. However, I am not planning support for it in the
near feature. I mean I will not be working on it...

> Do not forget to apply changes required by latest Intel's platforms, you
> should check following commits in TBOOT's repository: 2f03b57ffdba,
> fe2dddd742dc.

Sure, will take that into account.

Thank you for your comments,

Daniel

PS By the way, I found an issue in TXT spec. TXT.VER.FSBIF refers to
TXT.VER.EMIF which does not exist in spec. I suppose that it is
remnant from previous TXT spec versions. It seems to me that it
should be changed to TXT.VER.QPIIF. TXT.VER.QPIIF descriptions
properly, IMO, refers back to TXT.VER.FSBIF.

2020-05-13 13:50:42

by Lukasz Hawrylko

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

Hi Daniel

On Thu, 2020-05-07 at 13:06 +0200, Daniel Kiper wrote:
> > There is a function that verifies if platform is TXT capable
> > -grub_txt_verify_platform(), it only checks SMX and GETSEC features.
> > Although BIOS should enforce both VMX and VT-d enabled when enabling
> > TXT, I think that adding these check here as redundancy may be a good
>
> The TXT spec has the following pseudocode:
>
> //
> // Intel TXT detection
> // Execute on all logical processors for compatibility with
> // multiple processor systems
> //
> 1. CPUID(EAX=1);
> 2. IF (SMX not supported) OR (VMX not supported) {
> 3. Fail measured environment startup;
> 4. }
>
> However, a few lines above you can find this:
>
> Lines 1 - 4: Before attempting to launch the measured environment, the
> system software should check that all logical processors support VMX and
> SMX (the check for VMX support is not necessary if the environment to be
> launched will not use VMX).
>
> Hence, AIUI, I am allowed to check SMX only. And I do not think that the
> bootloader should enforce VMX. If the kernel wants VMX then it should
> check the platform config. The booloader should just look for features
> which are really required to properly execute GETSEC[SENTER].

I agree with you that spec does not clearly define if VMX is required or
not. In theory you can use TXT without VMX, however this is highly
impractical because in that configuration it is impossible to use STM to
protect MLE from SMI handler as STM feature requires VMX.

All real-life MLE implementations should use STM to be compliant with
TXT idea that MLE does not have a security dependency on the pre-
existing software environment. You can find more information about STM
here:

https://software.intel.com/content/dam/develop/external/us/en/documents/stm-user-guide-001-819978.pdf

Anyway, I have asked TXT spec owner to clarify VMX situation in next
document revision.

>
> PS By the way, I found an issue in TXT spec. TXT.VER.FSBIF refers to
> TXT.VER.EMIF which does not exist in spec. I suppose that it is
> remnant from previous TXT spec versions. It seems to me that it
> should be changed to TXT.VER.QPIIF. TXT.VER.QPIIF descriptions
> properly, IMO, refers back to TXT.VER.FSBIF.

Yes, this is remnant from old document versions, thank you for finding
that issue. Here are changes that will go to next revision:

* register 0x100 (TXT.VER.FSBIF) does not exist for many years and will
be removed from TXT spec
* register 0x200 is called TXT.VER.EMIF and indicates if chipset is
debug of production fused

Thanks,
Lukasz

2020-05-22 13:33:43

by Krystian Hebel

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 15/18] i386/txt: Add Intel TXT core implementation


On 05.05.2020 01:21, Daniel Kiper wrote:
> +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;
> + struct grub_txt_os_mle_data *os_mle_data;
> + struct grub_txt_os_sinit_data *os_sinit_data;
> + struct grub_txt_heap_end_element *heap_end_element;
> + struct grub_txt_heap_event_log_pointer2_1_element *heap_event_log_pointer2_1_element;
> +#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 ();
> +
> + /* OS/loader to MLE data. */
> +
> + os_mle_data = grub_txt_os_mle_data_start (txt_heap);
> + size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t));
There is 'grub_txt_os_mle_data_size()' in previous patch, it would look
better.
> + *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t);
> +
> + grub_memset (os_mle_data, 0, sizeof (*os_mle_data));
> +
> + os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION;
> + os_mle_data->zero_page_addr = (grub_uint32_t)(grub_addr_t) slparams->params;
> + os_mle_data->saved_misc_enable_msr = grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE);
> +
> + os_mle_data->ap_wake_block = slparams->ap_wake_block;
> +
> + save_mtrrs (os_mle_data);
> +
> + /* OS/loader to SINIT data. */
> +
> + 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);
> + size = (grub_uint64_t *) ((grub_addr_t) os_sinit_data - sizeof (grub_uint64_t));
Ditto
> +
> + *size = sizeof(grub_uint64_t) + sizeof (struct grub_txt_os_sinit_data) +
> + sizeof (struct grub_txt_heap_end_element);
> +
> + if (grub_get_tpm_ver () == GRUB_TPM_12)
> + *size += sizeof (struct grub_txt_heap_tpm_event_log_element);
> + else if (grub_get_tpm_ver () == GRUB_TPM_20)
> + *size += sizeof (struct grub_txt_heap_event_log_pointer2_1_element);
> + else
> + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM version"));
> +
> + grub_memset (os_sinit_data, 0, *size);
> +
> +#ifdef GRUB_MACHINE_EFI
> + rsdp = grub_acpi_get_rsdpv2 ();
> +
> + if (rsdp == NULL)
> + return grub_printf ("WARNING: ACPI RSDP 2.0 missing\n");
> +
> + os_sinit_data->efi_rsdt_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;
Could it be done with just one PMR, from 0 to top of memory, or would
TXT complain?
> +
> + 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);
> +
>
> <snip>
>
> +/*
> + * 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 requirments here.
> + *
> + * TODO: This function is not able to cover MLEs larger than 1 GiB. Fix it!!!
> + * After fixing inrease GRUB_TXT_MLE_MAX_SIZE too.
> + */

What do you mean by "special regions"? In TXT spec it is written that
there may be no holes
in the virtual address space:

"There may not be any invalid (not-present) page table entries after the
first valid
entry (i.e. there may not be any gaps in the MLE’s linear address space)."

Also that spec:
"A breadth-first search of page tables must produce increasing physical
addresses."

Maybe I misunderstood, but does it mean that the paging structures
themselves cannot
be mapped? Or does it apply to the page tables only, not to the
addresses of pages in PTEs?

> +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;
IMHO using 'grub_uint64_t' would result in less type casting and cleaner
code below.
> + 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
> + {
> + *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);
> + }
> + } while (mle_off < slparams->mle_size);
> +}
>
> <snip>
>
> +grub_err_t
> +grub_txt_boot_prepare (struct grub_slaunch_params *slparams)
> +{
> + grub_err_t err;
> + struct grub_txt_mle_header *mle_header;
> + struct grub_txt_acm_header *sinit_base;
> +
> + sinit_base = grub_txt_sinit_select (grub_slaunch_module ());
> +
> + if (sinit_base == NULL)
> + return grub_errno;
> +
> + err = init_txt_heap (slparams, sinit_base);
> +
> + if (err != GRUB_ERR_NONE)
> + return err;
> +
> + /* Update the MLE header. */
> + mle_header = (struct grub_txt_mle_header *)(grub_addr_t) (slparams->mle_start + slparams->mle_header_offset);
> + mle_header->first_valid_page = 0;
> + mle_header->mle_end = slparams->mle_size;
> +
> + slparams->sinit_acm_base = (grub_uint32_t)(grub_addr_t) sinit_base;
> + slparams->sinit_acm_size = sinit_base->size * 4;
> +
> + grub_tpm_relinquish_lcl (0);
> +
> + err = set_mtrrs_for_acmod (sinit_base);
> + if (err)
> + return grub_error (err, N_("secure launch failed to set MTRRs for ACM"));
> +
> + err = grub_txt_prepare_cpu ();
> + if ( err )
> + return err;
> +
> + 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"));
This test should be the first one, before messing with TPM and MTTRs.
> +
> + return GRUB_ERR_NONE;
> +}
Best regards,

--
Krystian Hebel
Firmware Engineer
https://3mdeb.com | @3mdeb_com

2020-06-01 14:22:20

by Ross Philipson

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 15/18] i386/txt: Add Intel TXT core implementation

On 5/22/20 9:24 AM, Krystian Hebel wrote:
>
> On 05.05.2020 01:21, Daniel Kiper wrote:
>> +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;
>> +  struct grub_txt_os_mle_data *os_mle_data;
>> +  struct grub_txt_os_sinit_data *os_sinit_data;
>> +  struct grub_txt_heap_end_element *heap_end_element;
>> +  struct grub_txt_heap_event_log_pointer2_1_element
>> *heap_event_log_pointer2_1_element;
>> +#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 ();
>> +
>> +  /* OS/loader to MLE data. */
>> +
>> +  os_mle_data = grub_txt_os_mle_data_start (txt_heap);
>> +  size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof
>> (grub_uint64_t));
> There is 'grub_txt_os_mle_data_size()' in previous patch, it would look
> better.
>> +  *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t);
>> +
>> +  grub_memset (os_mle_data, 0, sizeof (*os_mle_data));
>> +
>> +  os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION;
>> +  os_mle_data->zero_page_addr = (grub_uint32_t)(grub_addr_t)
>> slparams->params;
>> +  os_mle_data->saved_misc_enable_msr = grub_rdmsr
>> (GRUB_MSR_X86_MISC_ENABLE);
>> +
>> +  os_mle_data->ap_wake_block = slparams->ap_wake_block;
>> +
>> +  save_mtrrs (os_mle_data);
>> +
>> +  /* OS/loader to SINIT data. */
>> +
>> +  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);
>> +  size = (grub_uint64_t *) ((grub_addr_t) os_sinit_data - sizeof
>> (grub_uint64_t));
> Ditto
>> +
>> +  *size = sizeof(grub_uint64_t) + sizeof (struct
>> grub_txt_os_sinit_data) +
>> +      sizeof (struct grub_txt_heap_end_element);
>> +
>> +  if (grub_get_tpm_ver () == GRUB_TPM_12)
>> +    *size += sizeof (struct grub_txt_heap_tpm_event_log_element);
>> +  else if (grub_get_tpm_ver () == GRUB_TPM_20)
>> +    *size += sizeof (struct grub_txt_heap_event_log_pointer2_1_element);
>> +  else
>> +    return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM
>> version"));
>> +
>> +  grub_memset (os_sinit_data, 0, *size);
>> +
>> +#ifdef GRUB_MACHINE_EFI
>> +  rsdp = grub_acpi_get_rsdpv2 ();
>> +
>> +  if (rsdp == NULL)
>> +    return grub_printf ("WARNING: ACPI RSDP 2.0 missing\n");
>> +
>> +  os_sinit_data->efi_rsdt_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;
> Could it be done with just one PMR, from 0 to top of memory, or would

No because there could be these legacy reserved regions in somewhere
below 4G that might cause hangs if you block DMA to them. So the idea is
the high PMR covers everything > 4G and the low PMR needs special logic
to not block these reserved regions. We are working on better logic for
this now.

> TXT complain?
>> +
>> +  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);
>> +
>>
>> <snip>
>>
>> +/*
>> + * 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
>> requirments here.
>> + *
>> + * TODO: This function is not able to cover MLEs larger than 1 GiB.
>> Fix it!!!
>> + * After fixing inrease GRUB_TXT_MLE_MAX_SIZE too.
>> + */
>
> What do you mean by "special regions"? In TXT spec it is written that
> there may be no holes
> in the virtual address space:
>
> "There may not be any invalid (not-present) page table entries after the
> first valid
> entry (i.e. there may not be any gaps in the MLE’s linear address space)."
>
> Also that spec:
> "A breadth-first search of page tables must produce increasing physical
> addresses."
>
> Maybe I misunderstood, but does it mean that the paging structures
> themselves cannot
> be mapped? Or does it apply to the page tables only, not to the
> addresses of pages in PTEs?

One of the rules for building the page table for the ACM is the following:

Neither the MLE nor the page tables may overlap certain regions of memory:
- device memory (PCI, PCIe*, etc.)
- addresses between [640k, 1M] or above Top of Memory (TOM)
- ISA hole (if enabled)
- the Intel ® TXT heap or SINIT memory regions
- Intel ® VT-d DMAR tables

Perhaps the comment could be better...

>
>> +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;
> IMHO using 'grub_uint64_t' would result in less type casting and cleaner
> code below.
>> +  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
>> +    {
>> +      *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);
>> +        }
>> +    } while (mle_off < slparams->mle_size);
>> +}
>>
>> <snip>
>>
>> +grub_err_t
>> +grub_txt_boot_prepare (struct grub_slaunch_params *slparams)
>> +{
>> +  grub_err_t err;
>> +  struct grub_txt_mle_header *mle_header;
>> +  struct grub_txt_acm_header *sinit_base;
>> +
>> +  sinit_base = grub_txt_sinit_select (grub_slaunch_module ());
>> +
>> +  if (sinit_base == NULL)
>> +    return grub_errno;
>> +
>> +  err = init_txt_heap (slparams, sinit_base);
>> +
>> +  if (err != GRUB_ERR_NONE)
>> +    return err;
>> +
>> +  /* Update the MLE header. */
>> +  mle_header = (struct grub_txt_mle_header *)(grub_addr_t)
>> (slparams->mle_start + slparams->mle_header_offset);
>> +  mle_header->first_valid_page = 0;
>> +  mle_header->mle_end = slparams->mle_size;
>> +
>> +  slparams->sinit_acm_base = (grub_uint32_t)(grub_addr_t) sinit_base;
>> +  slparams->sinit_acm_size = sinit_base->size * 4;
>> +
>> +  grub_tpm_relinquish_lcl (0);
>> +
>> +  err = set_mtrrs_for_acmod (sinit_base);
>> +  if (err)
>> +    return grub_error (err, N_("secure launch failed to set MTRRs for
>> ACM"));
>> +
>> +  err = grub_txt_prepare_cpu ();
>> +  if ( err )
>> +    return err;
>> +
>> +  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"));
> This test should be the first one, before messing with TPM and MTTRs.
>> +
>> +  return GRUB_ERR_NONE;
>> +}
> Best regards,
>

2020-06-01 15:36:07

by Daniel P. Smith

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On 5/7/20 7:06 AM, Daniel Kiper wrote:
> Hi Łukasz,
>
> On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
>> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:

...

>> In OS-MLE table there is a buffer for TPM event log, however I see that
>> you are not using it, but instead allocate space somewhere in the
>
> I think that this part requires more discussion. In my opinion we should
> have this region dynamically allocated because it gives us more flexibility.
> Of course there is a question about the size of this buffer too. I am
> not sure about that because I have not checked yet how many log entries
> are created by the SINIT ACM. Though probably it should not be large...
>
>> memory. I am just wondering if, from security perspective, it will be
>> better to use memory from TXT heap for event log, like we do in TBOOT.
>
> Appendix F, TPM Event Log, has following sentence: There are no
> requirements for event log to be in DMA protected memory – SINIT will
> not enforce it.
>
> I was thinking about it and it seems to me that the TPM event log does
> not require any special protections. Any changes in it can be quickly
> detected by comparing hashes with the TPM PCRs. Does not it?
>

I think it would be beneficial to consider the following in deciding
where the log is placed. There are two areas of attack/manipulation that
need to be considered. The first area is the log contents itself, which
as Daniel has pointed out, the log contents do not really need to be
protected from tampering as that would/should be detected during
verification by the attestor. The second area that we need to consider
is the log descriptors themselves. If these are in an area that can be
manipulated, it is an opportunity for an attacker to attempt to
influence the ACM's execution. For a little perspective, the ACM
executes from CRAM to take the most possible precaution to ensure that
it cannot be tampered with during execution. This is very important
since it runs a CPU mode (ACM Mode) that I would consider to be higher
(or lower depending on how you view it) than SMM. As a result, the txt
heap is also included in what is mapped into CRAM. If the event log is
place in the heap, this ensures that the ACM is not using memory outside
of CRAM during execution. Now as Daniel has pointed out, the down side
to this is that it greatly restricts the log size and can only be
managed by a combination of limiting the number of events and
restricting what content is carried in the event data field.

With everything that has been said, I am wondering if it would it be
possible to make it configurable. Thoughts?

V/r,
DPS

2020-06-01 16:56:27

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On Mon, Jun 1, 2020 at 8:33 AM Daniel P. Smith
<[email protected]> wrote:
>
> On 5/7/20 7:06 AM, Daniel Kiper wrote:
> > Hi Łukasz,
> >
> > On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
> >> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
>
> ...
>
> >> In OS-MLE table there is a buffer for TPM event log, however I see that
> >> you are not using it, but instead allocate space somewhere in the
> >
> > I think that this part requires more discussion. In my opinion we should
> > have this region dynamically allocated because it gives us more flexibility.
> > Of course there is a question about the size of this buffer too. I am
> > not sure about that because I have not checked yet how many log entries
> > are created by the SINIT ACM. Though probably it should not be large...
> >
> >> memory. I am just wondering if, from security perspective, it will be
> >> better to use memory from TXT heap for event log, like we do in TBOOT.
> >
> > Appendix F, TPM Event Log, has following sentence: There are no
> > requirements for event log to be in DMA protected memory – SINIT will
> > not enforce it.
> >
> > I was thinking about it and it seems to me that the TPM event log does
> > not require any special protections. Any changes in it can be quickly
> > detected by comparing hashes with the TPM PCRs. Does not it?
> >
>
> I think it would be beneficial to consider the following in deciding
> where the log is placed. There are two areas of attack/manipulation that
> need to be considered. The first area is the log contents itself, which
> as Daniel has pointed out, the log contents do not really need to be
> protected from tampering as that would/should be detected during
> verification by the attestor. The second area that we need to consider
> is the log descriptors themselves. If these are in an area that can be
> manipulated, it is an opportunity for an attacker to attempt to
> influence the ACM's execution. For a little perspective, the ACM
> executes from CRAM to take the most possible precaution to ensure that
> it cannot be tampered with during execution. This is very important
> since it runs a CPU mode (ACM Mode) that I would consider to be higher
> (or lower depending on how you view it) than SMM. As a result, the txt
> heap is also included in what is mapped into CRAM. If the event log is
> place in the heap, this ensures that the ACM is not using memory outside
> of CRAM during execution. Now as Daniel has pointed out, the down side
> to this is that it greatly restricts the log size and can only be
> managed by a combination of limiting the number of events and
> restricting what content is carried in the event data field.

Can you clarify what the actual flow of control is? If I had to guess, it's:

GRUB (or other bootloader) writes log.

GRUB transfers control to the ACM. At this point, GRUB is done
running and GRUB code will not run again.

ACM validates system configuration and updates TPM state using magic
privileged TPM access.

ACM transfers control to the shiny new Linux secure launch entry point

Maybe this is right, and maybe this is wrong. But I have some
questions about this whole setup. Is the ACM code going to inspect
this log at all? If so, why? Who supplies the ACM code? If the ACM
can be attacked by putting its inputs (e.g. this log) in the wrong
place in memory, why should this be considered anything other than a
bug in the ACM?

If GRUB is indeed done by the time anyone consumes the log, why does
GRUB care where the log ends up?

And finally, the log presumably has nonzero size, and it would be nice
not to pin some physical memory forever for the log. Could the kernel
copy it into tmpfs during boot so it's at least swappable and then
allow userspace to delete it when userspace is done with it?

2020-06-01 18:54:42

by Ross Philipson

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On 6/1/20 1:56 PM, Daniel P. Smith wrote:
> On 6/1/20 12:51 PM, Andy Lutomirski wrote:
>> On Mon, Jun 1, 2020 at 8:33 AM Daniel P. Smith
>> <[email protected]> wrote:
>>>
>>> On 5/7/20 7:06 AM, Daniel Kiper wrote:
>>>> Hi Łukasz,
>>>>
>>>> On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
>>>>> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
>>>
>>> ...
>>>
>>>>> In OS-MLE table there is a buffer for TPM event log, however I see that
>>>>> you are not using it, but instead allocate space somewhere in the
>>>>
>>>> I think that this part requires more discussion. In my opinion we should
>>>> have this region dynamically allocated because it gives us more flexibility.
>>>> Of course there is a question about the size of this buffer too. I am
>>>> not sure about that because I have not checked yet how many log entries
>>>> are created by the SINIT ACM. Though probably it should not be large...
>>>>
>>>>> memory. I am just wondering if, from security perspective, it will be
>>>>> better to use memory from TXT heap for event log, like we do in TBOOT.
>>>>
>>>> Appendix F, TPM Event Log, has following sentence: There are no
>>>> requirements for event log to be in DMA protected memory – SINIT will
>>>> not enforce it.
>>>>
>>>> I was thinking about it and it seems to me that the TPM event log does
>>>> not require any special protections. Any changes in it can be quickly
>>>> detected by comparing hashes with the TPM PCRs. Does not it?
>>>>
>>>
>>> I think it would be beneficial to consider the following in deciding
>>> where the log is placed. There are two areas of attack/manipulation that
>>> need to be considered. The first area is the log contents itself, which
>>> as Daniel has pointed out, the log contents do not really need to be
>>> protected from tampering as that would/should be detected during
>>> verification by the attestor. The second area that we need to consider
>>> is the log descriptors themselves. If these are in an area that can be
>>> manipulated, it is an opportunity for an attacker to attempt to
>>> influence the ACM's execution. For a little perspective, the ACM
>>> executes from CRAM to take the most possible precaution to ensure that
>>> it cannot be tampered with during execution. This is very important
>>> since it runs a CPU mode (ACM Mode) that I would consider to be higher
>>> (or lower depending on how you view it) than SMM. As a result, the txt
>>> heap is also included in what is mapped into CRAM. If the event log is
>>> place in the heap, this ensures that the ACM is not using memory outside
>>> of CRAM during execution. Now as Daniel has pointed out, the down side
>>> to this is that it greatly restricts the log size and can only be
>>> managed by a combination of limiting the number of events and
>>> restricting what content is carried in the event data field.
>>
>> Can you clarify what the actual flow of control is? If I had to guess, it's:
>>
>> GRUB (or other bootloader) writes log.
>>
>> GRUB transfers control to the ACM. At this point, GRUB is done
>> running and GRUB code will not run again.
>>
>> ACM validates system configuration and updates TPM state using magic
>> privileged TPM access.
>>
>> ACM transfers control to the shiny new Linux secure launch entry point
>>
>> Maybe this is right, and maybe this is wrong. But I have some
>> questions about this whole setup. Is the ACM code going to inspect
>> this log at all? If so, why? Who supplies the ACM code? If the ACM
>> can be attacked by putting its inputs (e.g. this log) in the wrong
>> place in memory, why should this be considered anything other than a
>> bug in the ACM?
>
> There is a lot behind that, so to get a complete detail of the event
> sequence I would recommend looking at Section Vol. 2D 6.2.3 (pg Vol. 2D
> 6-5/ pdf pg 2531), 6.3 GETSEC[ENTERACCS](pg 6-10 Vol. 2D/pdf pg 2546),
> and 6.3 GETSEC[SENTER](pg Vol. 2D 6-21/pdf pg 2557) in the Intel SDM[1].
> Section 6.2.3 gives a slightly detailed overview. Section
> GETSEC[ENTERACCS] details the requirements/procedures for entering AC
> execution mode and ACRAM (Authenticated CRAM) and section GETSEC[SENTER]
> will detail requirements/procedures for SENTER.
>
> To answer you additional questions I would say if you look at all the
> work that goes into protecting the ACM execution, why would you want to
> then turn around and have it use memory outside of the protected region.
> On the other hand, you are right, if the Developer's Guide says it
> doesn't need to be protected and someone somehow finds a way to cause a
> failure in the ACM through the use of a log outside of CRAM, then
> rightfully that is a bug in the ACM. This is why I asked about making it
> configurable, paranoid people could set the configuration to use the
> heap and all others could just use an external location.

After thinking about it, it should be easy to make it configurable since
as stated it is up the the pre-launch code to decide where the buffer
is. To do this, the static buffer will be removed from the OS-MLE
structure. In its place there will be two fields:

u64 evt_log_base;
u64 evt_log_size;

If the entity setting up the buffer wants to place the log in the TXT
heap, it simply allocates memory after the OS-MLE fixed structure and
points evt_log_base there. If a buffer outside of the heap is wanted,
just point evt_log_base there. If the MLE wants to determine if the log
is in the TXT heap, it just has to compare evt_log_base to the TXT heap
extents.

Thanks
Ross

>
>> If GRUB is indeed done by the time anyone consumes the log, why does
>> GRUB care where the log ends up?
>
> This is because the log buffer allocation was made the responsibility of
> the pre-launch environment, in this case GRUB, and is communicated to
> the ACM via the os_to_mle structure.
>
>> And finally, the log presumably has nonzero size, and it would be nice
>> not to pin some physical memory forever for the log. Could the kernel
>> copy it into tmpfs during boot so it's at least swappable and then
>> allow userspace to delete it when userspace is done with it?
>>
>
> Actually yes we will want to do that because when we move to enabling
> relaunching, an implementation may want to preserve the log from the
> last launch before triggering the new launch which will result in a
> reset of the DRTM PCRs and an overwriting the log.
>
> [1]
> https://urldefense.com/v3/__https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf__;!!GqivPVa7Brio!MqL6_Gyu6fCO2MyxrvWmvfkCBRXYv1rKJnUrqvQoMNSwQTA9Db9H9BqDI96S13A4HUA$
>

2020-06-01 19:02:01

by Daniel P. Smith

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On 6/1/20 12:51 PM, Andy Lutomirski wrote:
> On Mon, Jun 1, 2020 at 8:33 AM Daniel P. Smith
> <[email protected]> wrote:
>>
>> On 5/7/20 7:06 AM, Daniel Kiper wrote:
>>> Hi Łukasz,
>>>
>>> On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
>>>> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
>>
>> ...
>>
>>>> In OS-MLE table there is a buffer for TPM event log, however I see that
>>>> you are not using it, but instead allocate space somewhere in the
>>>
>>> I think that this part requires more discussion. In my opinion we should
>>> have this region dynamically allocated because it gives us more flexibility.
>>> Of course there is a question about the size of this buffer too. I am
>>> not sure about that because I have not checked yet how many log entries
>>> are created by the SINIT ACM. Though probably it should not be large...
>>>
>>>> memory. I am just wondering if, from security perspective, it will be
>>>> better to use memory from TXT heap for event log, like we do in TBOOT.
>>>
>>> Appendix F, TPM Event Log, has following sentence: There are no
>>> requirements for event log to be in DMA protected memory – SINIT will
>>> not enforce it.
>>>
>>> I was thinking about it and it seems to me that the TPM event log does
>>> not require any special protections. Any changes in it can be quickly
>>> detected by comparing hashes with the TPM PCRs. Does not it?
>>>
>>
>> I think it would be beneficial to consider the following in deciding
>> where the log is placed. There are two areas of attack/manipulation that
>> need to be considered. The first area is the log contents itself, which
>> as Daniel has pointed out, the log contents do not really need to be
>> protected from tampering as that would/should be detected during
>> verification by the attestor. The second area that we need to consider
>> is the log descriptors themselves. If these are in an area that can be
>> manipulated, it is an opportunity for an attacker to attempt to
>> influence the ACM's execution. For a little perspective, the ACM
>> executes from CRAM to take the most possible precaution to ensure that
>> it cannot be tampered with during execution. This is very important
>> since it runs a CPU mode (ACM Mode) that I would consider to be higher
>> (or lower depending on how you view it) than SMM. As a result, the txt
>> heap is also included in what is mapped into CRAM. If the event log is
>> place in the heap, this ensures that the ACM is not using memory outside
>> of CRAM during execution. Now as Daniel has pointed out, the down side
>> to this is that it greatly restricts the log size and can only be
>> managed by a combination of limiting the number of events and
>> restricting what content is carried in the event data field.
>
> Can you clarify what the actual flow of control is? If I had to guess, it's:
>
> GRUB (or other bootloader) writes log.
>
> GRUB transfers control to the ACM. At this point, GRUB is done
> running and GRUB code will not run again.
>
> ACM validates system configuration and updates TPM state using magic
> privileged TPM access.
>
> ACM transfers control to the shiny new Linux secure launch entry point
>
> Maybe this is right, and maybe this is wrong. But I have some
> questions about this whole setup. Is the ACM code going to inspect
> this log at all? If so, why? Who supplies the ACM code? If the ACM
> can be attacked by putting its inputs (e.g. this log) in the wrong
> place in memory, why should this be considered anything other than a
> bug in the ACM?

There is a lot behind that, so to get a complete detail of the event
sequence I would recommend looking at Section Vol. 2D 6.2.3 (pg Vol. 2D
6-5/ pdf pg 2531), 6.3 GETSEC[ENTERACCS](pg 6-10 Vol. 2D/pdf pg 2546),
and 6.3 GETSEC[SENTER](pg Vol. 2D 6-21/pdf pg 2557) in the Intel SDM[1].
Section 6.2.3 gives a slightly detailed overview. Section
GETSEC[ENTERACCS] details the requirements/procedures for entering AC
execution mode and ACRAM (Authenticated CRAM) and section GETSEC[SENTER]
will detail requirements/procedures for SENTER.

To answer you additional questions I would say if you look at all the
work that goes into protecting the ACM execution, why would you want to
then turn around and have it use memory outside of the protected region.
On the other hand, you are right, if the Developer's Guide says it
doesn't need to be protected and someone somehow finds a way to cause a
failure in the ACM through the use of a log outside of CRAM, then
rightfully that is a bug in the ACM. This is why I asked about making it
configurable, paranoid people could set the configuration to use the
heap and all others could just use an external location.

> If GRUB is indeed done by the time anyone consumes the log, why does
> GRUB care where the log ends up?

This is because the log buffer allocation was made the responsibility of
the pre-launch environment, in this case GRUB, and is communicated to
the ACM via the os_to_mle structure.

> And finally, the log presumably has nonzero size, and it would be nice
> not to pin some physical memory forever for the log. Could the kernel
> copy it into tmpfs during boot so it's at least swappable and then
> allow userspace to delete it when userspace is done with it?
>

Actually yes we will want to do that because when we move to enabling
relaunching, an implementation may want to preserve the log from the
last launch before triggering the new launch which will result in a
reset of the DRTM PCRs and an overwriting the log.

[1]
https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf

2020-06-01 19:42:28

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher


> On Jun 1, 2020, at 10:56 AM, Daniel P. Smith <[email protected]> wrote:
>
> On 6/1/20 12:51 PM, Andy Lutomirski wrote:
>>> On Mon, Jun 1, 2020 at 8:33 AM Daniel P. Smith
>>> <[email protected]> wrote:
>>>
>>> On 5/7/20 7:06 AM, Daniel Kiper wrote:
>>>> Hi Łukasz,
>>>>
>>>> On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
>>>>>> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
>>>
>>> ...
>>>
>>>>> In OS-MLE table there is a buffer for TPM event log, however I see that
>>>>> you are not using it, but instead allocate space somewhere in the
>>>>
>>>> I think that this part requires more discussion. In my opinion we should
>>>> have this region dynamically allocated because it gives us more flexibility.
>>>> Of course there is a question about the size of this buffer too. I am
>>>> not sure about that because I have not checked yet how many log entries
>>>> are created by the SINIT ACM. Though probably it should not be large...
>>>>
>>>>> memory. I am just wondering if, from security perspective, it will be
>>>>> better to use memory from TXT heap for event log, like we do in TBOOT.
>>>>
>>>> Appendix F, TPM Event Log, has following sentence: There are no
>>>> requirements for event log to be in DMA protected memory – SINIT will
>>>> not enforce it.
>>>>
>>>> I was thinking about it and it seems to me that the TPM event log does
>>>> not require any special protections. Any changes in it can be quickly
>>>> detected by comparing hashes with the TPM PCRs. Does not it?
>>>>
>>>
>>> I think it would be beneficial to consider the following in deciding
>>> where the log is placed. There are two areas of attack/manipulation that
>>> need to be considered. The first area is the log contents itself, which
>>> as Daniel has pointed out, the log contents do not really need to be
>>> protected from tampering as that would/should be detected during
>>> verification by the attestor. The second area that we need to consider
>>> is the log descriptors themselves. If these are in an area that can be
>>> manipulated, it is an opportunity for an attacker to attempt to
>>> influence the ACM's execution. For a little perspective, the ACM
>>> executes from CRAM to take the most possible precaution to ensure that
>>> it cannot be tampered with during execution. This is very important
>>> since it runs a CPU mode (ACM Mode) that I would consider to be higher
>>> (or lower depending on how you view it) than SMM. As a result, the txt
>>> heap is also included in what is mapped into CRAM. If the event log is
>>> place in the heap, this ensures that the ACM is not using memory outside
>>> of CRAM during execution. Now as Daniel has pointed out, the down side
>>> to this is that it greatly restricts the log size and can only be
>>> managed by a combination of limiting the number of events and
>>> restricting what content is carried in the event data field.
>>
>> Can you clarify what the actual flow of control is? If I had to guess, it's:
>>
>> GRUB (or other bootloader) writes log.
>>
>> GRUB transfers control to the ACM. At this point, GRUB is done
>> running and GRUB code will not run again.
>>
>> ACM validates system configuration and updates TPM state using magic
>> privileged TPM access.
>>
>> ACM transfers control to the shiny new Linux secure launch entry point
>>
>> Maybe this is right, and maybe this is wrong. But I have some
>> questions about this whole setup. Is the ACM code going to inspect
>> this log at all? If so, why? Who supplies the ACM code? If the ACM
>> can be attacked by putting its inputs (e.g. this log) in the wrong
>> place in memory, why should this be considered anything other than a
>> bug in the ACM?
>
> There is a lot behind that, so to get a complete detail of the event
> sequence I would recommend looking at Section Vol. 2D 6.2.3 (pg Vol. 2D
> 6-5/ pdf pg 2531), 6.3 GETSEC[ENTERACCS](pg 6-10 Vol. 2D/pdf pg 2546),
> and 6.3 GETSEC[SENTER](pg Vol. 2D 6-21/pdf pg 2557) in the Intel SDM[1].
> Section 6.2.3 gives a slightly detailed overview. Section
> GETSEC[ENTERACCS] details the requirements/procedures for entering AC
> execution mode and ACRAM (Authenticated CRAM) and section GETSEC[SENTER]
> will detail requirements/procedures for SENTER.
>
> To answer you additional questions I would say if you look at all the
> work that goes into protecting the ACM execution, why would you want to
> then turn around and have it use memory outside of the protected region.
> On the other hand, you are right, if the Developer's Guide says it
> doesn't need to be protected and someone somehow finds a way to cause a
> failure in the ACM through the use of a log outside of CRAM, then
> rightfully that is a bug in the ACM. This is why I asked about making it
> configurable, paranoid people could set the configuration to use the
> heap and all others could just use an external location.

And this provides no protection whatsoever to paranoid people AFAICS, unless the location gets hashed before any processing occurs.

But you haven’t answered the most important question: what is the ACM doing with this log? I feel like a lot of details are being covered but the big picture is missing.

>
>> If GRUB is indeed done by the time anyone consumes the log, why does
>> GRUB care where the log ends up?
>
> This is because the log buffer allocation was made the responsibility of
> the pre-launch environment, in this case GRUB, and is communicated to
> the ACM via the os_to_mle structure.
>
>> And finally, the log presumably has nonzero size, and it would be nice
>> not to pin some physical memory forever for the log. Could the kernel
>> copy it into tmpfs during boot so it's at least swappable and then
>> allow userspace to delete it when userspace is done with it?
>>
>
> Actually yes we will want to do that because when we move to enabling
> relaunching, an implementation may want to preserve the log from the
> last launch before triggering the new launch which will result in a
> reset of the DRTM PCRs and an overwriting the log.

I’m having a bit of trouble understanding how this log is useful for a relaunch. At the point that you’re relaunching, the log is even less trustworthy than on first launch. At least on first launch, if you dial up your secure and verified boot settings tight enough, you can have some degree of confidence in the boot environment. But on a relaunch, I don’t see how the log is useful for anything more than any other piece of kernel memory.

What am I missing?

2020-06-02 00:19:34

by Daniel P. Smith

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On 6/1/20 3:39 PM, Andy Lutomirski wrote:
>
>> On Jun 1, 2020, at 10:56 AM, Daniel P. Smith <[email protected]> wrote:
>>
>> On 6/1/20 12:51 PM, Andy Lutomirski wrote:
>>>> On Mon, Jun 1, 2020 at 8:33 AM Daniel P. Smith
>>>> <[email protected]> wrote:
>>>>
>>>> On 5/7/20 7:06 AM, Daniel Kiper wrote:
>>>>> Hi Łukasz,
>>>>>
>>>>> On Tue, May 05, 2020 at 04:38:02PM +0200, Lukasz Hawrylko wrote:
>>>>>>> On Tue, 2020-05-05 at 01:21 +0200, Daniel Kiper wrote:
>>>>
>>>> ...
>>>>
>>>>>> In OS-MLE table there is a buffer for TPM event log, however I see that
>>>>>> you are not using it, but instead allocate space somewhere in the
>>>>>
>>>>> I think that this part requires more discussion. In my opinion we should
>>>>> have this region dynamically allocated because it gives us more flexibility.
>>>>> Of course there is a question about the size of this buffer too. I am
>>>>> not sure about that because I have not checked yet how many log entries
>>>>> are created by the SINIT ACM. Though probably it should not be large...
>>>>>
>>>>>> memory. I am just wondering if, from security perspective, it will be
>>>>>> better to use memory from TXT heap for event log, like we do in TBOOT.
>>>>>
>>>>> Appendix F, TPM Event Log, has following sentence: There are no
>>>>> requirements for event log to be in DMA protected memory – SINIT will
>>>>> not enforce it.
>>>>>
>>>>> I was thinking about it and it seems to me that the TPM event log does
>>>>> not require any special protections. Any changes in it can be quickly
>>>>> detected by comparing hashes with the TPM PCRs. Does not it?
>>>>>
>>>>
>>>> I think it would be beneficial to consider the following in deciding
>>>> where the log is placed. There are two areas of attack/manipulation that
>>>> need to be considered. The first area is the log contents itself, which
>>>> as Daniel has pointed out, the log contents do not really need to be
>>>> protected from tampering as that would/should be detected during
>>>> verification by the attestor. The second area that we need to consider
>>>> is the log descriptors themselves. If these are in an area that can be
>>>> manipulated, it is an opportunity for an attacker to attempt to
>>>> influence the ACM's execution. For a little perspective, the ACM
>>>> executes from CRAM to take the most possible precaution to ensure that
>>>> it cannot be tampered with during execution. This is very important
>>>> since it runs a CPU mode (ACM Mode) that I would consider to be higher
>>>> (or lower depending on how you view it) than SMM. As a result, the txt
>>>> heap is also included in what is mapped into CRAM. If the event log is
>>>> place in the heap, this ensures that the ACM is not using memory outside
>>>> of CRAM during execution. Now as Daniel has pointed out, the down side
>>>> to this is that it greatly restricts the log size and can only be
>>>> managed by a combination of limiting the number of events and
>>>> restricting what content is carried in the event data field.
>>>
>>> Can you clarify what the actual flow of control is? If I had to guess, it's:
>>>
>>> GRUB (or other bootloader) writes log.
>>>
>>> GRUB transfers control to the ACM. At this point, GRUB is done
>>> running and GRUB code will not run again.
>>>
>>> ACM validates system configuration and updates TPM state using magic
>>> privileged TPM access.
>>>
>>> ACM transfers control to the shiny new Linux secure launch entry point
>>>
>>> Maybe this is right, and maybe this is wrong. But I have some
>>> questions about this whole setup. Is the ACM code going to inspect
>>> this log at all? If so, why? Who supplies the ACM code? If the ACM
>>> can be attacked by putting its inputs (e.g. this log) in the wrong
>>> place in memory, why should this be considered anything other than a
>>> bug in the ACM?
>>
>> There is a lot behind that, so to get a complete detail of the event
>> sequence I would recommend looking at Section Vol. 2D 6.2.3 (pg Vol. 2D
>> 6-5/ pdf pg 2531), 6.3 GETSEC[ENTERACCS](pg 6-10 Vol. 2D/pdf pg 2546),
>> and 6.3 GETSEC[SENTER](pg Vol. 2D 6-21/pdf pg 2557) in the Intel SDM[1].
>> Section 6.2.3 gives a slightly detailed overview. Section
>> GETSEC[ENTERACCS] details the requirements/procedures for entering AC
>> execution mode and ACRAM (Authenticated CRAM) and section GETSEC[SENTER]
>> will detail requirements/procedures for SENTER.
>>
>> To answer you additional questions I would say if you look at all the
>> work that goes into protecting the ACM execution, why would you want to
>> then turn around and have it use memory outside of the protected region.
>> On the other hand, you are right, if the Developer's Guide says it
>> doesn't need to be protected and someone somehow finds a way to cause a
>> failure in the ACM through the use of a log outside of CRAM, then
>> rightfully that is a bug in the ACM. This is why I asked about making it
>> configurable, paranoid people could set the configuration to use the
>> heap and all others could just use an external location.
>
> And this provides no protection whatsoever to paranoid people AFAICS, unless the location gets hashed before any processing occurs.

Apologies but that is exactly what it says. From section 6.2.3,

"After the GETSEC[SENTER] rendezvous handshake is performed between all
logical processors in the platform, the ILP loads the chipset
authenticated code module (SINIT) and performs an authentication check.
If the check passes, the processor hashes the SINIT AC module and stores
the result into TPM PCR 17. It then switches execution context to the
SINIT AC module."

To get a little into the details, the ACM is signed with an RSA key that
is deeply embedded into the CPU, thus why there is an ACM per
architecture as each one gets a new key. The authentication check is
detailed in the section GETSEC[ENTERACCS], but in the end the ACM has
had a crypto signature check carried out by the CPU (not firmware) and
is also cryptographically hashed by the CPU (again not firmware). On top
of this, both operations are executed after all interrupts have been
disabled and the ACM has been loaded into ACRAM from memory that was
protected by the IOMMU. Only after all this succeeds is the ACM allowed
to execute.

> But you haven’t answered the most important question: what is the ACM doing with this log? I feel like a lot of details are being covered but the big picture is missing.

To the ACM, this is just an allocated buffer for it to record a TPM
event log for all the measurements it takes. It has been a while since I
have manually reviewed a TXT event log but I want to say there are
about five measurements taken before the ACM exits, including recording
the CRTM taken by the CPU. As such, it initialize the beginning of the
buffer with a TXT log header and records a UEFI TPM Event for each
measurement it takes. In theory, it will only ever write to this memory
but seeing that the ACM is a binary blob, I have never seen
programmatically if it ever tries reading from the memory.

>>
>>> If GRUB is indeed done by the time anyone consumes the log, why does
>>> GRUB care where the log ends up?
>>
>> This is because the log buffer allocation was made the responsibility of
>> the pre-launch environment, in this case GRUB, and is communicated to
>> the ACM via the os_to_mle structure.
>>
>>> And finally, the log presumably has nonzero size, and it would be nice
>>> not to pin some physical memory forever for the log. Could the kernel
>>> copy it into tmpfs during boot so it's at least swappable and then
>>> allow userspace to delete it when userspace is done with it?
>>>
>>
>> Actually yes we will want to do that because when we move to enabling
>> relaunching, an implementation may want to preserve the log from the
>> last launch before triggering the new launch which will result in a
>> reset of the DRTM PCRs and an overwriting the log.
>
> I’m having a bit of trouble understanding how this log is useful for a relaunch. At the point that you’re relaunching, the log is even less trustworthy than on first launch. At least on first launch, if you dial up your secure and verified boot settings tight enough, you can have some degree of confidence in the boot environment. But on a relaunch, I don’t see how the log is useful for anything more than any other piece of kernel memory.
>
> What am I missing?
>

Before relaunch you can have the TPM do a sign quote of the log to bind
the contents to the state of the PCRs. The why you would do that is more
about enterprise use-cases concerned with the lifecycle of enterprise
devices.When relaunch occurs, the DRTM PCRs will be reset by the CPU
before the CRTM for the relaunch is taken by the CPU and the ACM will
overwrite the existing log with new log entries. As highlighted above,
the CRTM and all measurements take by the ACM will have an extremely low
risk of external/attacker influence. When the MLE takes control,
interrupts will still be disabled and any measurements it takes prior to
enabling them will have a low risk of external/attacker influence. Once
the MLE enables the interrupts is when you have the situation whereby
the integrity you just established about the runtime can be compromised
by hostile firmware (UEFI runtime services, SMI handler, EC firmware,
PCI devices, etc.), hostile applications, and a hostile network. At this
point we could devolve into discourse over how long can load time
integrity measurements be considered trustworthy but I don't think that
is relevant to the issue at hand.

In other words, the log for the relaunch to attest what is currently
running is really no less useful than using the first launch log to
attest to the what was running in the first launch.

2020-06-02 00:51:16

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher



> On Jun 1, 2020, at 5:14 PM, Daniel P. Smith <[email protected]> wrote:
>
> On 6/1/20 3:39 PM, Andy Lutomirski wrote:
>>>> .
>
> In other words, the log for the relaunch to attest what is currently
> running is really no less useful than using the first launch log to
> attest to the what was running in the first launch.
>

Maybe it would help if you give some examples of what’s actually in this log and why anyone, Linux or otherwise, cares for any purpose other than debugging. We’re talking about a log written by something like GRUB, right? If so, I’m imagining things like:

GRUB: loading such-and-such module
GRUB: loading the other module
GRUB: loading Linux at /boot/vmlinuz-whatever
GRUB: about to do the DRTM launch. Bye-bye.

This is surely useful for debugging. But, if I understand your security model correctly, it’s untrustworthy in the sense that this all comes from before the DRTM launch and it could have been tampered with by SMM code or even just a malicious USB stick. Or even a malicious compromised kernel on the same machine. So you could hash this log into a PCR, but I don’t see what you’ve accomplished by doing so.

Or have I misunderstood what this log is? Perhaps you’re talking about something else entirely.

2020-06-02 01:32:05

by Daniel P. Smith

[permalink] [raw]
Subject: Re: [GRUB PATCH RFC 00/18] i386: Intel TXT secure launcher

On 6/1/20 8:49 PM, Andy Lutomirski wrote:
>
>
>> On Jun 1, 2020, at 5:14 PM, Daniel P. Smith <[email protected]> wrote:
>>
>> On 6/1/20 3:39 PM, Andy Lutomirski wrote:
>>>>> .
>>
>> In other words, the log for the relaunch to attest what is currently
>> running is really no less useful than using the first launch log to
>> attest to the what was running in the first launch.
>>
>
> Maybe it would help if you give some examples of what’s actually in this log and why anyone, Linux or otherwise, cares for any purpose other than debugging. We’re talking about a log written by something like GRUB, right? If so, I’m imagining things like:
>
> GRUB: loading such-and-such module
> GRUB: loading the other module
> GRUB: loading Linux at /boot/vmlinuz-whatever
> GRUB: about to do the DRTM launch. Bye-bye.
>
> This is surely useful for debugging. But, if I understand your security model correctly, it’s untrustworthy in the sense that this all comes from before the DRTM launch and it could have been tampered with by SMM code or even just a malicious USB stick. Or even a malicious compromised kernel on the same machine. So you could hash this log into a PCR, but I don’t see what you’ve accomplished by doing so.
>
> Or have I misunderstood what this log is? Perhaps you’re talking about something else entirely.
>

Oh I see! Yes we are discussing two different logs and yes there are two
"logs" in play here. The start of this thread by Lukasz was on the TPM
Event log. This is the log that is a record of TPM events for the DRTM
chain, you can see the equivalent for SRTM with `cat
/sys/kernel/security/tpm0/ascii_bios_measurements`(provided you system
configuration is set up for SRTM). The second log, which for lack of a
better name is the "debug log", is what you are referring to and is in
another proposal that just recently came out, "[BOOTLOADER SPECIFICATION
RFC] The bootloader log format for TrenchBoot"[1].

You are correct, the "debug log" is just filled with messages from
components during the DRTM launch chain. While relaunch is being kept in
mind as we work through getting first launch complete, not all aspects
have been considered. What happens to the debug log on relaunch has no
security relevance, as you called out earlier, and at this point I would
say is an exercise for those integrating relaunch when it becomes
available. I would expect them to treat it no different than the kmesg
and syslog buffers on kexec/reboot, some may opt to kept a historical
record and others will just let it disappear into the ether.

Apologies that we got disconnected, I hope we are in sync now.

[1] https://lists.gnu.org/archive/html/grub-devel/2020-05/msg00223.html