From 0e20cf1a7adcb8d2c1beefdaf18c3a5c705a6eaa Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 17 Dec 2019 17:51:29 +0100 Subject: [PATCH 01/31] i386/msr: Merge rdmsr.h and wrmsr.h into msr.h It does not make sense to have separate headers for separate static functions. Additionally, we have to add some constants with MSR addresses in subsequent patches. So, make one common place to store them. Signed-off-by: Daniel Kiper --- grub-core/commands/i386/rdmsr.c | 2 +- grub-core/commands/i386/wrmsr.c | 2 +- include/grub/i386/{wrmsr.h => msr.h} | 16 +++++++++--- include/grub/i386/rdmsr.h | 37 ---------------------------- 4 files changed, 15 insertions(+), 42 deletions(-) rename include/grub/i386/{wrmsr.h => msr.h} (78%) delete mode 100644 include/grub/i386/rdmsr.h diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c index 46c4346da..fa4622f9e 100644 --- a/grub-core/commands/i386/rdmsr.c +++ b/grub-core/commands/i386/rdmsr.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include GRUB_MOD_LICENSE("GPLv3+"); diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c index 1b143b888..8f352f205 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include GRUB_MOD_LICENSE("GPLv3+"); diff --git a/include/grub/i386/wrmsr.h b/include/grub/i386/msr.h similarity index 78% rename from include/grub/i386/wrmsr.h rename to include/grub/i386/msr.h index dea60aed1..7b52b5d61 100644 --- a/include/grub/i386/wrmsr.h +++ b/include/grub/i386/msr.h @@ -16,14 +16,24 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_WRMSR_H -#define GRUB_WRMSR_H 1 +#ifndef GRUB_I386_MSR_H +#define GRUB_I386_MSR_H 1 /* * TODO: Add a general protection exception handler. * Accessing a reserved or unimplemented MSR address results in a GP#. */ +static inline grub_uint64_t +grub_msr_read (grub_uint32_t msr_id) +{ + grub_uint32_t low, high; + + asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr_id)); + + return ((grub_uint64_t) high << 32) | low; +} + static inline void grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value) { @@ -32,4 +42,4 @@ grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value) asm volatile ("wrmsr" : : "c" (msr_id), "a" (low), "d" (high)); } -#endif /* GRUB_WRMSR_H */ +#endif /* GRUB_I386_MSR_H */ diff --git a/include/grub/i386/rdmsr.h b/include/grub/i386/rdmsr.h deleted file mode 100644 index c0a0c717a..000000000 --- a/include/grub/i386/rdmsr.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2019 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 . - */ - -#ifndef GRUB_RDMSR_H -#define GRUB_RDMSR_H 1 - -/* - * TODO: Add a general protection exception handler. - * Accessing a reserved or unimplemented MSR address results in a GP#. - */ - -static inline grub_uint64_t -grub_msr_read (grub_uint32_t msr_id) -{ - grub_uint32_t low, high; - - asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr_id)); - - return ((grub_uint64_t)high << 32) | low; -} - -#endif /* GRUB_RDMSR_H */ From 69d6d826f755cc582b2fce8b8e95405b06c7a90f Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 17 Dec 2019 20:02:37 +0100 Subject: [PATCH 02/31] 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 --- 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 8f352f205..cf6bf6c8f 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -77,7 +77,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; From e349ad1f89719382f8a4f0c4fb9a2e5ac4809497 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sat, 25 Apr 2020 16:43:52 +0200 Subject: [PATCH 03/31] i386/msr: Extract and improve MSR support detection code Currently rdmsr and wrmsr commands have own MSR support detection code. This code is the same. So, it is duplicated. Additionally, this code cannot be reused by others. Hence, extract this code to a function and make it public. By the way, improve a code a bit. Additionally, use GRUB_ERR_BAD_DEVICE instead of GRUB_ERR_BUG to signal an error because errors encountered by this new routine are not bugs. Signed-off-by: Daniel Kiper --- grub-core/commands/i386/rdmsr.c | 21 +++++---------------- grub-core/commands/i386/wrmsr.c | 21 +++++---------------- include/grub/i386/msr.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c index 89ece7657..2e42f6197 100644 --- a/grub-core/commands/i386/rdmsr.c +++ b/grub-core/commands/i386/rdmsr.c @@ -42,27 +42,16 @@ static const struct grub_arg_option options[] = static grub_err_t grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv) { - grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr; + grub_err_t err; + grub_uint32_t addr; grub_uint64_t value; const char *ptr; char buf[sizeof("1122334455667788")]; - /* - * The CPUID instruction should be used to determine whether MSRs - * are supported. (CPUID.01H:EDX[5] = 1) - */ - if (! grub_cpu_is_cpuid_supported ()) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + err = grub_cpu_is_msr_supported (); - grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); - - if (max_cpuid < 1) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); - - grub_cpuid (1, a, b, c, features); - - if (!(features & (1 << 5))) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("RDMSR is unsupported")); if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c index cf6bf6c8f..7fbedaed9 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -36,26 +36,15 @@ static grub_command_t cmd_write; static grub_err_t grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char **argv) { - grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr; + grub_err_t err; + grub_uint32_t addr; grub_uint64_t value; const char *ptr; - /* - * The CPUID instruction should be used to determine whether MSRs - * are supported. (CPUID.01H:EDX[5] = 1) - */ - if (!grub_cpu_is_cpuid_supported ()) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + err = grub_cpu_is_msr_supported (); - grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); - - if (max_cpuid < 1) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); - - grub_cpuid (1, a, b, c, features); - - if (!(features & (1 << 5))) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("WRMSR is unsupported")); if (argc != 2) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h index 4fba1b8e0..1e838c022 100644 --- a/include/grub/i386/msr.h +++ b/include/grub/i386/msr.h @@ -19,6 +19,35 @@ #ifndef GRUB_I386_MSR_H #define GRUB_I386_MSR_H 1 +#include +#include +#include + +static inline grub_err_t +grub_cpu_is_msr_supported (void) +{ + grub_uint32_t eax, ebx, ecx, edx; + + /* + * The CPUID instruction should be used to determine whether MSRs + * are supported, CPUID.01H:EDX[5] = 1. + */ + if (!grub_cpu_is_cpuid_supported ()) + return GRUB_ERR_BAD_DEVICE; + + grub_cpuid (0, eax, ebx, ecx, edx); + + if (eax < 1) + return GRUB_ERR_BAD_DEVICE; + + grub_cpuid (1, eax, ebx, ecx, edx); + + if (!(edx & (1 << 5))) + return GRUB_ERR_BAD_DEVICE; + + return GRUB_ERR_NONE; +} + /* * TODO: Add a general protection exception handler. * Accessing a reserved or unimplemented MSR address results in a GP#. From 68e33447be826da40a04dd9d159760834f7f9760 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 20 Nov 2019 12:40:42 +0100 Subject: [PATCH 04/31] i386/memory: Rename PAGE_SHIFT to GRUB_PAGE_SHIFT ...to avoid potential conflicts and confusion. Signed-off-by: Daniel Kiper --- grub-core/lib/i386/xen/relocator.S | 6 +++--- grub-core/lib/x86_64/xen/relocator.S | 4 ++-- grub-core/loader/i386/xen.c | 28 ++++++++++++++-------------- include/grub/i386/memory.h | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/grub-core/lib/i386/xen/relocator.S b/grub-core/lib/i386/xen/relocator.S index 96e51b59a..dab4d8ace 100644 --- a/grub-core/lib/i386/xen/relocator.S +++ b/grub-core/lib/i386/xen/relocator.S @@ -75,10 +75,10 @@ VARIABLE(grub_relocator_xen_mfn_list) .long 0 movl 0(%eax, %ebp, 4), %ecx /* mfn */ movl %ebp, %ebx - shll $PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ + shll $GRUB_PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ movl %ecx, %edx - shll $PAGE_SHIFT, %ecx /* prepare pte low part */ - shrl $(32 - PAGE_SHIFT), %edx /* pte high part */ + shll $GRUB_PAGE_SHIFT, %ecx /* prepare pte low part */ + shrl $(32 - GRUB_PAGE_SHIFT), %edx /* pte high part */ orl $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %ecx /* pte low */ movl $UVMF_INVLPG, %esi movl $__HYPERVISOR_update_va_mapping, %eax diff --git a/grub-core/lib/x86_64/xen/relocator.S b/grub-core/lib/x86_64/xen/relocator.S index f5364ed0f..852cd40aa 100644 --- a/grub-core/lib/x86_64/xen/relocator.S +++ b/grub-core/lib/x86_64/xen/relocator.S @@ -60,9 +60,9 @@ LOCAL(cont): jz 3f 2: movq %r12, %rdi - shlq $PAGE_SHIFT, %rdi /* virtual address (1:1 mapping) */ + shlq $GRUB_PAGE_SHIFT, %rdi /* virtual address (1:1 mapping) */ movq (%rbx, %r12, 8), %rsi /* mfn */ - shlq $PAGE_SHIFT, %rsi + shlq $GRUB_PAGE_SHIFT, %rsi orq $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %rsi /* Build pte */ movq $UVMF_INVLPG, %rdx movq %rcx, %r9 /* %rcx clobbered by hypercall */ diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c index cd24874ca..1810c1dbc 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -92,7 +92,7 @@ static struct xen_loader_state xen_state; static grub_dl_t my_mod; -#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) #define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) #define STACK_SIZE 1048576 #define ADDITIONAL_SIZE (1 << 19) @@ -103,7 +103,7 @@ static grub_dl_t my_mod; static grub_uint64_t page2offset (grub_uint64_t page) { - return page << PAGE_SHIFT; + return page << GRUB_PAGE_SHIFT; } static grub_err_t @@ -142,7 +142,7 @@ get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn) continue; } - bits = PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; + bits = GRUB_PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; mask = (1ULL << bits) - 1; map->lvls[i].virt_start = map->area.virt_start & ~mask; map->lvls[i].virt_end = map->area.virt_end | mask; @@ -247,11 +247,11 @@ generate_page_table (grub_xen_mfn_t *mfn_list) if (lvl->virt_start >= end || lvl->virt_end <= start) continue; p_s = (grub_max (start, lvl->virt_start) - start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); p_e = (grub_min (end, lvl->virt_end) - start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; grub_dprintf ("xen", "write page table entries level %d pg %p " "mapping %d/%d index %lx-%lx pfn %llx\n", l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn); @@ -329,16 +329,16 @@ grub_xen_p2m_alloc (void) { err = get_pgtable_size (xen_state.xen_inf.p2m_base, xen_state.xen_inf.p2m_base + p2msize, - (xen_state.max_addr + p2msize) >> PAGE_SHIFT); + (xen_state.max_addr + p2msize) >> GRUB_PAGE_SHIFT); if (err) return err; - map->area.pfn_start = xen_state.max_addr >> PAGE_SHIFT; + map->area.pfn_start = xen_state.max_addr >> GRUB_PAGE_SHIFT; p2malloc = p2msize + page2offset (map->area.n_pt_pages); xen_state.n_mappings++; xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base; xen_state.next_start.first_p2m_pfn = map->area.pfn_start; - xen_state.next_start.nr_p2m_frames = p2malloc >> PAGE_SHIFT; + xen_state.next_start.nr_p2m_frames = p2malloc >> GRUB_PAGE_SHIFT; } else { @@ -381,7 +381,7 @@ grub_xen_special_alloc (void) xen_state.virt_start_info = get_virtual_current_address (ch); xen_state.max_addr = ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE); - xen_state.console_pfn = xen_state.max_addr >> PAGE_SHIFT; + xen_state.console_pfn = xen_state.max_addr >> GRUB_PAGE_SHIFT; xen_state.max_addr += 2 * PAGE_SIZE; xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages; @@ -413,7 +413,7 @@ grub_xen_pt_alloc (void) xen_state.next_start.pt_base = xen_state.max_addr + xen_state.xen_inf.virt_base; - nr_info_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_info_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT; nr_need_pages = nr_info_pages; while (1) @@ -461,7 +461,7 @@ grub_xen_pt_alloc (void) xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base; xen_state.next_start.nr_pt_frames = nr_need_pages; xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base; - xen_state.pgtbl_end = xen_state.max_addr >> PAGE_SHIFT; + xen_state.pgtbl_end = xen_state.max_addr >> GRUB_PAGE_SHIFT; xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where + page2offset (map->area.n_pt_pages)); @@ -515,7 +515,7 @@ grub_xen_boot (void) if (err) return err; - nr_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT; grub_dprintf ("xen", "bootstrap domain %llx+%llx\n", (unsigned long long) xen_state.xen_inf.virt_base, @@ -819,7 +819,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), if (xen_state.xen_inf.unmapped_initrd) { xen_state.next_start.flags |= SIF_MOD_START_PFN; - xen_state.next_start.mod_start = xen_state.max_addr >> PAGE_SHIFT; + xen_state.next_start.mod_start = xen_state.max_addr >> GRUB_PAGE_SHIFT; } else xen_state.next_start.mod_start = diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h index 5cb607fb4..7be57d6d7 100644 --- a/include/grub/i386/memory.h +++ b/include/grub/i386/memory.h @@ -20,7 +20,7 @@ #ifndef GRUB_MEMORY_CPU_HEADER #define GRUB_MEMORY_CPU_HEADER 1 -#define PAGE_SHIFT 12 +#define GRUB_PAGE_SHIFT 12 /* The flag for protected mode. */ #define GRUB_MEMORY_CPU_CR0_PE_ON 0x1 From 89a387c50c8782e10d1fcb3237250208a70cb6f4 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 20 Nov 2019 12:52:16 +0100 Subject: [PATCH 05/31] i386/memory: Rename PAGE_SIZE to GRUB_PAGE_SIZE and make it global Subsequent patches will use that constant. Signed-off-by: Daniel Kiper --- grub-core/loader/i386/xen.c | 35 +++++++++++++++++------------------ include/grub/i386/memory.h | 1 + 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c index 1810c1dbc..45c24c45f 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -92,8 +92,7 @@ static struct xen_loader_state xen_state; static grub_dl_t my_mod; -#define PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) -#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) +#define MAX_MODULES (GRUB_PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) #define STACK_SIZE 1048576 #define ADDITIONAL_SIZE (1 << 19) #define ALIGN_SIZE (1 << 22) @@ -229,7 +228,7 @@ generate_page_table (grub_xen_mfn_t *mfn_list) for (m1 = 0; m1 < xen_state.n_mappings; m1++) grub_memset (xen_state.mappings[m1].where, 0, - xen_state.mappings[m1].area.n_pt_pages * PAGE_SIZE); + xen_state.mappings[m1].area.n_pt_pages * GRUB_PAGE_SIZE); for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--) { @@ -324,7 +323,7 @@ grub_xen_p2m_alloc (void) map = xen_state.mappings + xen_state.n_mappings; p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) * - grub_xen_start_page_addr->nr_pages, PAGE_SIZE); + grub_xen_start_page_addr->nr_pages, GRUB_PAGE_SIZE); if (xen_state.xen_inf.has_p2m_base) { err = get_pgtable_size (xen_state.xen_inf.p2m_base, @@ -380,9 +379,9 @@ grub_xen_special_alloc (void) xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base; xen_state.virt_start_info = get_virtual_current_address (ch); xen_state.max_addr = - ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE); + ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), GRUB_PAGE_SIZE); xen_state.console_pfn = xen_state.max_addr >> GRUB_PAGE_SHIFT; - xen_state.max_addr += 2 * PAGE_SIZE; + xen_state.max_addr += 2 * GRUB_PAGE_SIZE; xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages; grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic, @@ -431,9 +430,9 @@ grub_xen_pt_alloc (void) /* Map the relocator page either at virtual 0 or after end of area. */ nr_need_pages = nr_info_pages + map->area.n_pt_pages; if (xen_state.xen_inf.virt_base) - err = get_pgtable_size (0, PAGE_SIZE, nr_need_pages); + err = get_pgtable_size (0, GRUB_PAGE_SIZE, nr_need_pages); else - err = get_pgtable_size (try_virt_end, try_virt_end + PAGE_SIZE, + err = get_pgtable_size (try_virt_end, try_virt_end + GRUB_PAGE_SIZE, nr_need_pages); if (err) return err; @@ -538,7 +537,7 @@ grub_xen_boot (void) return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages, xen_state.xen_inf.virt_base < - PAGE_SIZE ? page2offset (nr_pages) : 0, + GRUB_PAGE_SIZE ? page2offset (nr_pages) : 0, xen_state.pgtbl_end - 1, page2offset (xen_state.pgtbl_end - 1) + xen_state.xen_inf.virt_base); @@ -677,7 +676,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), goto fail; } - if (xen_state.xen_inf.virt_base & (PAGE_SIZE - 1)) + if (xen_state.xen_inf.virt_base & (GRUB_PAGE_SIZE - 1)) { grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base"); goto fail; @@ -700,10 +699,10 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page - xen_state.xen_inf.virt_base); kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page - - xen_state.xen_inf.virt_base + PAGE_SIZE); + xen_state.xen_inf.virt_base + GRUB_PAGE_SIZE); } - xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (kern_end, GRUB_PAGE_SIZE); if (grub_sub (kern_end, kern_start, &sz)) @@ -730,7 +729,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), if (xen_state.xen_inf.has_hypercall_page) { unsigned i; - for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) + for (i = 0; i < GRUB_PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) set_hypercall_interface ((grub_uint8_t *) kern_chunk_src + i * HYPERCALL_INTERFACE_SIZE + xen_state.xen_inf.hypercall_page - @@ -829,7 +828,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base), (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: grub_initrd_close (&initrd_ctx); @@ -883,7 +882,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), { xen_state.xen_inf.unmapped_initrd = 0; xen_state.n_modules = 0; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); xen_state.modules_target_start = xen_state.max_addr; xen_state.next_start.mod_start = xen_state.max_addr + xen_state.xen_inf.virt_base; @@ -903,7 +902,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), MAX_MODULES * sizeof (xen_state.module_info_page[0]); } - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)); @@ -926,7 +925,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.module_info_page[xen_state.n_modules].cmdline = xen_state.max_addr - xen_state.modules_target_start; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, GRUB_PAGE_SIZE); if (size) { @@ -953,7 +952,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.n_modules++; grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", (unsigned) xen_state.max_addr, (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h index 7be57d6d7..c64529630 100644 --- a/include/grub/i386/memory.h +++ b/include/grub/i386/memory.h @@ -21,6 +21,7 @@ #define GRUB_MEMORY_CPU_HEADER 1 #define GRUB_PAGE_SHIFT 12 +#define GRUB_PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) /* The flag for protected mode. */ #define GRUB_MEMORY_CPU_CR0_PE_ON 0x1 From a65ac35fa6ad04b2427b62dca224dfe902d7cf1b Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Mon, 22 Jan 2024 13:14:39 +0100 Subject: [PATCH 06/31] i386/memory: Define GRUB_PAGE_MASK constant and GRUB_PAGE_{UP,DOWN} macros Subsequent patches will use those macros and constant. Signed-off-by: Krystian Hebel --- include/grub/i386/memory.h | 5 +++++ 1 file changed, 5 insertions(+) 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 #include +#define GRUB_PAGE_UP(p) ALIGN_UP (p, GRUB_PAGE_SIZE) +#define GRUB_PAGE_DOWN(p) ALIGN_DOWN (p, GRUB_PAGE_SIZE) + struct grub_e820_mmap_entry { grub_uint64_t addr; From 19c1993e61564f001328e9fa9ee6468a8dcc8216 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 4 May 2020 22:34:59 +0200 Subject: [PATCH 07/31] 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 --- grub-core/mmap/mmap.c | 79 +++++++++++++++++++++++++++++++++++++++++++ include/grub/memory.h | 3 ++ 2 files changed, 82 insertions(+) diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c index c8c8312c5..5b18b1a4c 100644 --- a/grub-core/mmap/mmap.c +++ b/grub-core/mmap/mmap.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -343,6 +344,84 @@ grub_mmap_unregister (int handle) #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */ +typedef struct +{ + grub_uint64_t addr; + grub_uint64_t limit; +} addr_limit_t; + +/* Helper for grub_mmap_get_lowest(). */ +static int +lowest_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + addr_limit_t *al = data; + grub_uint64_t end; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_add (addr, size, &end)) + return 0; + + if (addr >= al->limit) + al->addr = grub_min (al->addr, addr); + + if ((addr < al->limit) && (end > al->limit)) + al->addr = al->limit; + + return 0; +} + +/* This function calculates lowest available RAM address that is at or above + the passed limit. If no RAM exists above the limit, ~0 is returned. */ +grub_uint64_t +grub_mmap_get_lowest (grub_uint64_t limit) +{ + addr_limit_t al = {~0, limit}; + + grub_mmap_iterate (lowest_hook, &al); + + return al.addr; +} + +/* Helper for grub_mmap_get_highest(). */ +static int +highest_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + addr_limit_t *al = data; + grub_uint64_t end; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_add (addr, size, &end)) + return 0; + + if (end < al->limit) + al->addr = grub_max (al->addr, end); + + if ((addr < al->limit) && (end >= al->limit)) + al->addr = al->limit; + + return 0; +} + +/* This function calculates highest available RAM address that is below the + passed limit. Returned address is either one byte after last byte of RAM or + equal to limit, whichever is lower. If no RAM exists below limit, 0 is + returned. */ +grub_uint64_t +grub_mmap_get_highest (grub_uint64_t limit) +{ + addr_limit_t al = {0, limit}; + + grub_mmap_iterate (highest_hook, &al); + + return al.addr; +} + #define CHUNK_SIZE 0x400 struct badram_entry { diff --git a/include/grub/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 From 4213c395d03b02192a44ca0ed2ff4f4c2fc9d957 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 22 Apr 2020 21:41:24 +0200 Subject: [PATCH 08/31] 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 --- 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 f8b4b3b21..20cf82a81 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -5991,10 +5991,10 @@ grub-mkimage -O x86_64-efi -o grubx64.efi -p '(tftp)/grub' --sbat sbat.csv efine @node Measured Boot @section Measuring boot components -If the tpm module is loaded and the platform has a Trusted Platform Module -installed, GRUB will log each command executed and each file loaded into the -TPM event log and extend the PCR values in the TPM correspondingly. All events -will be logged into the PCR described below with a type of EV_IPL and an +If the tpm_verifier module is loaded and the platform has a Trusted Platform +Module installed, GRUB will log each command executed and each file loaded into +the TPM event log and extend the PCR values in the TPM correspondingly. All +events will be logged into the PCR described below with a type of EV_IPL and an event description as described below. @multitable @columnfractions 0.3 0.1 0.6 @@ -6019,9 +6019,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 8022e1c0a..f482cc01e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2490,8 +2490,8 @@ module = { }; module = { - name = tpm; - common = commands/tpm.c; + name = tpm_verifier; + common = commands/tpm_verifier.c; efi = commands/efi/tpm.c; enable = 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 2052c36ea..1d820a774 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm_verifier.c @@ -78,18 +78,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); } From 137e1c04cd01a854d8a1d49d8311bf26a4aa6c3f Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 4 May 2020 22:30:58 +0200 Subject: [PATCH 09/31] 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 Signed-off-by: Krystian Hebel --- grub-core/commands/i386/tpm.c | 149 ++++++++++++++++++++++++++++++++++ include/grub/i386/tpm.h | 36 ++++++++ 2 files changed, 185 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..f3b6f51ec --- /dev/null +++ b/grub-core/commands/i386/tpm.c @@ -0,0 +1,149 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * TPM TIS and CRB driver. + * + * Note: It is suggested to not use this driver together with UEFI TPM driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Code based on TCG PC Client Platform TPM Profile Specification for TPM 2.0, + Version 1.05 Revision 14 released September 4, 2020. */ + +#define TPM_MMIO_BASE 0xfed40000 + +/* 6.3.2 Register Space Addresses */ +/* TIS registers. */ +#define TPM_ACCESS 0x0000 +#define TPM_INTF_CAPABILITY 0x0014 +#define INTF_CAP_INTERFACE_VERSION_SHIFT 28 +#define INTF_CAP_INTERFACE_VERSION_MASK 7 +#define TPM_INTERFACE_ID 0x0030 +#define INTERFACE_ID_INTERFACE_TYPE_SHIFT 0 +#define INTERFACE_ID_INTERFACE_TYPE_MASK 0xF + +/* CRB registers. */ +#define TPM_LOC_CTRL 0x0008 + + +#define TIS_RELINQUISH_LCL 0x20 +#define CRB_RELINQUISH_LCL 0x0002 + +/* 6.4.2 Interface Identifier Register */ +#define TPM_CRB_INTF_ACTIVE 0x1 + +/* 6.5.2.7 Interface Capability */ +#define TPM_12_TIS_INTF_12 0x0 +#define TPM_12_TIS_INTF_13 0x2 +#define TPM_20_TIS_INTF_13 0x3 + +typedef enum + { + TPM_INTF_NONE = 0, + TPM_INTF_TIS, + TPM_INTF_CRB + } +tpm_intf_t; + +static grub_tpm_ver_t tpm_ver = GRUB_TPM_NONE; +static tpm_intf_t tpm_intf = TPM_INTF_NONE; + +grub_tpm_ver_t +grub_get_tpm_ver (void) +{ + return tpm_ver; +} + +/* Localities 0-4 are supported only. */ +void +grub_tpm_relinquish_locality (grub_uint8_t lcl) +{ + grub_addr_t addr = TPM_MMIO_BASE + lcl * GRUB_PAGE_SIZE; + + if (tpm_intf == TPM_INTF_TIS) + grub_write8 (TIS_RELINQUISH_LCL, addr + TPM_ACCESS); + else if (tpm_intf == TPM_INTF_CRB) + grub_write32 (CRB_RELINQUISH_LCL, addr + TPM_LOC_CTRL); +} + +static grub_err_t +grub_cmd_tpm_type (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + const char *tpm_ver_s = "NONE"; + const char *tpm_intf_s = "NONE"; + + if (tpm_ver == GRUB_TPM_12) + tpm_ver_s = "1.2"; + else if (tpm_ver == GRUB_TPM_20) + tpm_ver_s = "2.0"; + + if (tpm_intf == TPM_INTF_TIS) + tpm_intf_s = "TIS"; + else if (tpm_intf == TPM_INTF_CRB) + tpm_intf_s = "CRB"; + + grub_printf ("TPM family: %s\nTPM interface: %s\n", tpm_ver_s, tpm_intf_s); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_tpm_type; + +GRUB_MOD_INIT (tpm) +{ + grub_uint32_t intf_id; + grub_uint32_t intf_cap; + + cmd_tpm_type = grub_register_command ("tpm_type", grub_cmd_tpm_type, + NULL, N_("Show TPM version and interface type.")); + + tpm_ver = GRUB_TPM_20; + + intf_id = grub_read32 (TPM_MMIO_BASE + TPM_INTERFACE_ID); + intf_id >>= INTERFACE_ID_INTERFACE_TYPE_SHIFT; + intf_id &= INTERFACE_ID_INTERFACE_TYPE_MASK; + + tpm_intf = (intf_id == TPM_CRB_INTF_ACTIVE) ? TPM_INTF_CRB : TPM_INTF_TIS; + + /* CRB exists only in TPM 2.0 */ + if (tpm_intf == TPM_INTF_CRB) + return; + + intf_cap = grub_read32 (TPM_MMIO_BASE + TPM_INTF_CAPABILITY); + intf_cap >>= INTF_CAP_INTERFACE_VERSION_SHIFT; + intf_cap &= INTF_CAP_INTERFACE_VERSION_MASK; + + if (intf_cap == TPM_12_TIS_INTF_12 || intf_cap == TPM_12_TIS_INTF_13) + tpm_ver = GRUB_TPM_12; +} + +GRUB_MOD_FINI (tpm) +{ + grub_unregister_command (cmd_tpm_type); +} diff --git a/include/grub/i386/tpm.h b/include/grub/i386/tpm.h new file mode 100644 index 000000000..d22a2507e --- /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 . + * + */ + +#ifndef GRUB_I386_TPM_H +#define GRUB_I386_TPM_H 1 + +#include + +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_locality (grub_uint8_t lcl); + +#endif /* GRUB_I386_TPM_H */ From 7d89e5d8d9f6e9a7337dece763bb6234467b63c9 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:50:14 -0400 Subject: [PATCH 10/31] i386/slaunch: Add basic platform support for secure launch Some of the commands declared in header files will be implemented in the follow-up commits. Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Krystian Hebel --- include/grub/i386/cpuid.h | 12 ++++ include/grub/i386/crfr.h | 127 ++++++++++++++++++++++++++++++++++++ include/grub/i386/mmio.h | 72 ++++++++++++++++++++ include/grub/i386/msr.h | 63 ++++++++++++++++++ include/grub/i386/slaunch.h | 62 ++++++++++++++++++ 5 files changed, 336 insertions(+) create mode 100644 include/grub/i386/crfr.h create mode 100644 include/grub/i386/mmio.h create mode 100644 include/grub/i386/slaunch.h diff --git a/include/grub/i386/cpuid.h b/include/grub/i386/cpuid.h index f7ae4b0a4..0ddd87b15 100644 --- a/include/grub/i386/cpuid.h +++ b/include/grub/i386/cpuid.h @@ -19,6 +19,18 @@ #ifndef GRUB_CPU_CPUID_HEADER #define GRUB_CPU_CPUID_HEADER 1 +/* General */ +#define GRUB_X86_CPUID_VENDOR 0x00000000 +#define GRUB_X86_CPUID_FEATURES 0x00000001 +/* Intel */ +#define GRUB_X86_CPUID_FEATURES_ECX_VMX (1<<5) +#define GRUB_X86_CPUID_FEATURES_ECX_SMX (1<<6) + +/* AMD */ +#define GRUB_AMD_CPUID_FEATURES 0x80000001 +#define GRUB_AMD_CPUID_FEATURES_ECX_SVM (1<<2) +#define GRUB_AMD_CPUID_FUNC 0x8000000a + extern unsigned char grub_cpuid_has_longmode; extern unsigned char grub_cpuid_has_pae; diff --git a/include/grub/i386/crfr.h b/include/grub/i386/crfr.h new file mode 100644 index 000000000..2efd42a17 --- /dev/null +++ b/include/grub/i386/crfr.h @@ -0,0 +1,127 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_CRFR_H +#define GRUB_CRFR_H 1 + +#include + +/* Routines for R/W of control and flags registers */ + +#define GRUB_CR0_X86_PE 0x00000001 /* Enable Protected Mode */ +#define GRUB_CR0_X86_MP 0x00000002 /* "Math" (FPU) Present */ +#define GRUB_CR0_X86_EM 0x00000004 /* EMulate FPU */ +#define GRUB_CR0_X86_TS 0x00000008 /* Task Switched */ +#define GRUB_CR0_X86_PG 0x80000000 /* Enable PaGing */ + +#define GRUB_CR0_X86_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */ +#define GRUB_CR0_X86_WP 0x00010000 /* Write Protect */ +#define GRUB_CR0_X86_AM 0x00040000 /* Alignment Mask */ +#define GRUB_CR0_X86_NW 0x20000000 /* Not Write-through */ +#define GRUB_CR0_X86_CD 0x40000000 /* Cache Disable */ + +#define GRUB_CR4_X86_VME 0x00000001 /* Virtual 8086 mode extensions */ +#define GRUB_CR4_X86_PVI 0x00000002 /* Protected-mode virtual interrupts */ +#define GRUB_CR4_X86_TSD 0x00000004 /* Time stamp disable */ +#define GRUB_CR4_X86_DE 0x00000008 /* Debugging extensions */ +#define GRUB_CR4_X86_PSE 0x00000010 /* Page size extensions */ +#define GRUB_CR4_X86_PAE 0x00000020 /* Physical address extension */ +#define GRUB_CR4_X86_MCE 0x00000040 /* Enable Machine check enable */ +#define GRUB_CR4_X86_PGE 0x00000080 /* Enable Page global */ +#define GRUB_CR4_X86_PCE 0x00000100 /* Enable Performance monitoring counter */ +#define GRUB_CR4_X86_FXSR 0x00000200 /* Fast FPU save/restore */ +#define GRUB_CR4_X86_XMM 0x00000400 /* Generate #XM instead of #UD for SIMD */ +#define GRUB_CR4_X86_VMXE 0x00002000 /* Enable VMX */ +#define GRUB_CR4_X86_SMXE 0x00004000 /* Enable SMX */ +#define GRUB_CR4_X86_PCIDE 0x00020000 /* Enable PCID */ + +static inline unsigned long +grub_read_cr0 (void) +{ + unsigned long val; + + asm volatile ("mov %%cr0, %0" : "=r" (val) : : "memory"); + + return val; +} + +static inline void +grub_write_cr0 (unsigned long val) +{ + asm volatile ("mov %0, %%cr0" : : "r" (val) : "memory"); +} + +static inline unsigned long +grub_read_cr4 (void) +{ + unsigned long val; + + asm volatile ("mov %%cr4, %0" : "=r" (val) : : "memory"); + + return val; +} + +static inline void +grub_write_cr4 (unsigned long val) +{ + asm volatile ("mov %0, %%cr4" : : "r" (val) : "memory"); +} + +#define GRUB_EFLAGS_X86_CF 0x00000001 /* Carry Flag */ +#define GRUB_EFLAGS_X86_PF 0x00000004 /* Parity Flag */ +#define GRUB_EFLAGS_X86_AF 0x00000010 /* Auxillary carry Flag */ +#define GRUB_EFLAGS_X86_ZF 0x00000040 /* Zero Flag */ +#define GRUB_EFLAGS_X86_SF 0x00000080 /* Sign Flag */ +#define GRUB_EFLAGS_X86_TF 0x00000100 /* Trap Flag */ +#define GRUB_EFLAGS_X86_IF 0x00000200 /* Interrupt Flag */ +#define GRUB_EFLAGS_X86_DF 0x00000400 /* Direction Flag */ +#define GRUB_EFLAGS_X86_OF 0x00000800 /* Overflow Flag */ +#define GRUB_EFLAGS_X86_IOPL 0x00003000 /* IOPL mask */ +#define GRUB_EFLAGS_X86_NT 0x00004000 /* Nested Task */ +#define GRUB_EFLAGS_X86_RF 0x00010000 /* Resume Flag */ +#define GRUB_EFLAGS_X86_VM 0x00020000 /* Virtual Mode */ +#define GRUB_EFLAGS_X86_AC 0x00040000 /* Alignment Check */ +#define GRUB_EFLAGS_X86_VIF 0x00080000 /* Virtual Interrupt Flag */ +#define GRUB_EFLAGS_X86_VIP 0x00100000 /* Virtual Interrupt Pending */ +#define GRUB_EFLAGS_X86_ID 0x00200000 /* CPUID detection flag */ + +static inline unsigned long +grub_read_flags_register(void) +{ + unsigned long flags; + +#ifdef __x86_64__ + asm volatile ("pushfq; popq %0" : "=r" (flags)); +#else + asm volatile ("pushfl; popl %0" : "=r" (flags)); +#endif + + return flags; +} + +static inline void +grub_write_flags_register(unsigned long flags) +{ +#ifdef __x86_64__ + asm volatile ("pushq %0; popfq" : : "r" (flags)); +#else + asm volatile ("pushl %0; popfl" : : "r" (flags)); +#endif +} + +#endif diff --git a/include/grub/i386/mmio.h b/include/grub/i386/mmio.h new file mode 100644 index 000000000..97a30f7d8 --- /dev/null +++ b/include/grub/i386/mmio.h @@ -0,0 +1,72 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_I386_MMIO_H +#define GRUB_I386_MMIO_H 1 + +#include + +static inline grub_uint8_t +grub_read8 (const grub_addr_t addr) +{ + grub_uint8_t val; + + val = (*(volatile grub_uint8_t *) (addr)); + + return val; +} + +static inline grub_uint32_t +grub_read32 (const grub_addr_t addr) +{ + grub_uint32_t val; + + val = (*(volatile grub_uint32_t *) (addr)); + + return val; +} + +static inline grub_uint64_t +grub_read64 (const grub_addr_t addr) +{ + grub_uint64_t val; + + val = (*(volatile grub_uint64_t *) (addr)); + + return val; +} + +static inline void +grub_write8 (grub_uint8_t val, grub_addr_t addr) +{ + (*(volatile grub_uint8_t *) (addr)) = val; +} + +static inline void +grub_write32 (grub_uint32_t val, grub_addr_t addr) +{ + (*(volatile grub_uint32_t *) (addr)) = val; +} + +static inline void +grub_write64 (grub_uint64_t val, grub_addr_t addr) +{ + (*(volatile grub_uint64_t *) (addr)) = val; +} + +#endif /* GRUB_I386_MMIO_H */ diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h index 1e838c022..52c923ab9 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,64 @@ #ifndef GRUB_I386_MSR_H #define GRUB_I386_MSR_H 1 +/* General */ +#define GRUB_MSR_X86_PLATFORM_ID 0x00000017 + +#define GRUB_MSR_X86_APICBASE 0x0000001b +#define GRUB_MSR_X86_APICBASE_BSP (1<<8) +#define GRUB_MSR_X86_APICBASE_ENABLE (1<<11) +#define GRUB_MSR_X86_APICBASE_BASE (0xfffff<<12) /* Mask for APIC base address */ + +#define GRUB_MSR_X86_FEATURE_CONTROL 0x0000003a +#define GRUB_MSR_X86_FEATURE_CTRL_LOCK (1<<0) /* Lock writes to this register */ +#define GRUB_MSR_X86_ENABLE_VMX_IN_SMX (1<<1) /* Enable VMX inside SMX */ +#define GRUB_MSR_X86_ENABLE_VMX_OUT_SMX (1<<2) /* Enable VMX outside SMX */ +#define GRUB_MSR_X86_SENTER_FUNCTIONS (0x7f<<8) /* Bitmap of SENTER function enables */ +#define GRUB_MSR_X86_SENTER_ENABLE (1<<15) /* SENTER global enable */ + +#define GRUB_MSR_X86_MTRRCAP 0x000000fe +#define GRUB_MSR_X86_VCNT_MASK 0xff /* Number of variable MTRRs */ + +#define GRUB_MSR_X86_MCG_CAP 0x00000179 +#define GRUB_MSR_MCG_BANKCNT_MASK 0xff /* Number of banks */ +#define GRUB_MSR_X86_MCG_STATUS 0x0000017a +#define GRUB_MSR_MCG_STATUS_MCIP (1ULL<<2) /* MC in progress */ + +#define GRUB_MSR_X86_MISC_ENABLE 0x000001a0 +#define GRUB_MSR_X86_ENABLE_MONITOR_FSM (1<<18) + +#define GRUB_MSR_X86_MTRR_PHYSBASE0 0x00000200 +#define GRUB_MSR_X86_MTRR_PHYSMASK0 0x00000201 +#define GRUB_MSR_X86_MTRR_PHYSBASE(n) (GRUB_MSR_X86_MTRR_PHYSBASE0 + 2 * (n)) +#define GRUB_MSR_X86_MTRR_PHYSMASK(n) (GRUB_MSR_X86_MTRR_PHYSMASK0 + 2 * (n)) +#define GRUB_MSR_X86_BASE_DEF_TYPE_MASK 0xff +#define GRUB_MSR_X86_MASK_VALID (1<<11) + +#define GRUB_MSR_X86_MTRR_DEF_TYPE 0x000002ff +#define GRUB_MSR_X86_DEF_TYPE_MASK 0xff +#define GRUB_MSR_X86_MTRR_ENABLE_FIXED (1<<10) +#define GRUB_MSR_X86_MTRR_ENABLE (1<<11) + +#define GRUB_MSR_X86_MC0_STATUS 0x00000401 + +#define GRUB_MSR_X86_EFER 0xc0000080 /* Extended features */ +#define GRUB_MSR_EFER_LME (1<<8) /* Enable Long Mode/IA-32e */ +#define GRUB_MSR_EFER_LMA (1<<10) /* Long Mode/IA-32e Active */ +#define GRUB_MSR_EFER_SVME (1<<12) /* Enable SVM (AMD-V) */ + +/* AMD Specific */ +#define GRUB_MSR_AMD64_VM_CR 0xc0010114 /* SVM control register */ +#define GRUB_MSR_SVM_VM_CR_SVM_DISABLE (1<<4) /* Disable writes to EFER.SVME */ + +/* MTRR Specific */ +#define GRUB_MTRR_MEMORY_TYPE_UC 0 +#define GRUB_MTRR_MEMORY_TYPE_WC 1 +#define GRUB_MTRR_MEMORY_TYPE_WT 4 +#define GRUB_MTRR_MEMORY_TYPE_WP 5 +#define GRUB_MTRR_MEMORY_TYPE_WB 6 + +#ifndef ASM_FILE + #include #include #include @@ -71,4 +132,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 */ diff --git a/include/grub/i386/slaunch.h b/include/grub/i386/slaunch.h new file mode 100644 index 000000000..7f7709cda --- /dev/null +++ b/include/grub/i386/slaunch.h @@ -0,0 +1,62 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Main secure launch definitions header file. + */ + +#ifndef GRUB_I386_SLAUNCH_H +#define GRUB_I386_SLAUNCH_H 1 + +/* Secure launch platform types. */ +#define SLP_NONE 0 +#define SLP_INTEL_TXT 1 + +/* PCRs used by Secure launch. */ +#define GRUB_SLAUNCH_CODE_PCR 17 +#define GRUB_SLAUNCH_DATA_PCR 18 + +#define GRUB_SLAUNCH_TPM_EVT_LOG_SIZE (8 * GRUB_PAGE_SIZE) + +#ifndef ASM_FILE + +#include +#include + +struct grub_slaunch_params +{ + grub_uint32_t boot_params_addr; + 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_uint32_t ap_wake_block; + grub_uint32_t ap_wake_block_size; + grub_uint32_t dce_base; + grub_uint32_t dce_size; + grub_uint64_t tpm_evt_log_base; + grub_uint32_t tpm_evt_log_size; +}; + +extern grub_uint32_t grub_slaunch_platform_type (void); +extern void *grub_slaunch_module (void); +extern struct grub_slaunch_params *grub_slaunch_params (void); + +#endif /* ASM_FILE */ + +#endif /* GRUB_I386_SLAUNCH_H */ From 1ec91a681b97b993135892207485f7e8e77e6522 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Mon, 9 Jan 2023 12:55:42 -0500 Subject: [PATCH 11/31] i386/txt: Initial commit of the Secure Launch Resource Table (SLRT) Provide definitions of structures and basic functions for constructing and parsing of SLRT. Signed-off-by: Ross Philipson Signed-off-by: Sergii Dmytruk --- include/grub/slr_table.h | 330 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 include/grub/slr_table.h diff --git a/include/grub/slr_table.h b/include/grub/slr_table.h new file mode 100644 index 000000000..b6bbb0d54 --- /dev/null +++ b/include/grub/slr_table.h @@ -0,0 +1,330 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Secure Launch Resource Table definitions + */ + +#ifndef GRUB_SLR_TABLE_H +#define GRUB_SLR_TABLE_H 1 + +#include + +#define GRUB_UEFI_SLR_TABLE_GUID \ + { 0x877a9b2a, 0x0385, 0x45d1, { 0xa0, 0x34, 0x9d, 0xac, 0x9c, 0x9e, 0x56, 0x5f }} + +/* SLR table header values */ +#define GRUB_SLR_TABLE_MAGIC 0x4452544d +#define GRUB_SLR_TABLE_REVISION 1 + +/* Current revisions for the policy and UEFI config */ +#define GRUB_SLR_POLICY_REVISION 1 +#define GRUB_SLR_UEFI_CONFIG_REVISION 1 + +/* SLR defined architectures */ +#define GRUB_SLR_INTEL_TXT 1 +#define GRUB_SLR_AMD_SKINIT 2 + +/* SLR defined bootloaders */ +#define GRUB_SLR_BOOTLOADER_INVALID 0 +#define GRUB_SLR_BOOTLOADER_GRUB 1 + +/* Log formats */ +#define GRUB_SLR_DRTM_TPM12_LOG 1 +#define GRUB_SLR_DRTM_TPM20_LOG 2 + +/* DRTM Policy Entry Flags */ +#define GRUB_SLR_POLICY_FLAG_MEASURED 0x1 +#define GRUB_SLR_POLICY_IMPLICIT_SIZE 0x2 + +/* Array Lengths */ +#define GRUB_TPM_EVENT_INFO_LENGTH 32 +#define GRUB_TXT_VARIABLE_MTRRS_LENGTH 32 + +/* Tags */ +#define GRUB_SLR_ENTRY_INVALID 0x0000 +#define GRUB_SLR_ENTRY_DL_INFO 0x0001 +#define GRUB_SLR_ENTRY_LOG_INFO 0x0002 +#define GRUB_SLR_ENTRY_DRTM_POLICY 0x0003 +#define GRUB_SLR_ENTRY_INTEL_INFO 0x0004 +#define GRUB_SLR_ENTRY_AMD_INFO 0x0005 +#define GRUB_SLR_ENTRY_ARM_INFO 0x0006 +#define GRUB_SLR_ENTRY_UEFI_INFO 0x0007 +#define GRUB_SLR_ENTRY_UEFI_CONFIG 0x0008 +#define GRUB_SLR_ENTRY_END 0xffff + +/* Entity Types */ +#define GRUB_SLR_ET_UNSPECIFIED 0x0000 +#define GRUB_SLR_ET_SLRT 0x0001 +#define GRUB_SLR_ET_BOOT_PARAMS 0x0002 +#define GRUB_SLR_ET_SETUP_DATA 0x0003 +#define GRUB_SLR_ET_CMDLINE 0x0004 +#define GRUB_SLR_ET_UEFI_MEMMAP 0x0005 +#define GRUB_SLR_ET_RAMDISK 0x0006 +#define GRUB_SLR_ET_MULTIBOOT2_INFO 0x0007 +#define GRUB_SLR_ET_MULTIBOOT2_MODULE 0x0008 +#define GRUB_SLR_ET_TXT_OS2MLE 0x0010 +#define GRUB_SLR_ET_UNUSED 0xffff + +/* + * Primary SLR Table Header + */ +struct grub_slr_table +{ + grub_uint32_t magic; + grub_uint16_t revision; + grub_uint16_t architecture; + grub_uint32_t size; + grub_uint32_t max_size; + /* entries[] */ +} GRUB_PACKED; + +/* + * Common SLRT Table Header + */ +struct grub_slr_entry_hdr +{ + grub_uint16_t tag; + grub_uint16_t size; +} GRUB_PACKED; + +/* + * Boot loader context + */ +struct grub_slr_bl_context +{ + grub_uint16_t bootloader; + grub_uint16_t reserved; + grub_uint64_t context; +} GRUB_PACKED; + +/* + * DRTM Dynamic Launch Configuration + */ +struct grub_slr_entry_dl_info +{ + struct grub_slr_entry_hdr hdr; + struct grub_slr_bl_context bl_context; + grub_uint64_t dl_handler; + grub_uint64_t dce_base; + grub_uint32_t dce_size; + grub_uint64_t dlme_entry; +} GRUB_PACKED; + +/* + * TPM Log Information + */ +struct grub_slr_entry_log_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t format; + grub_uint16_t reserved; + grub_uint64_t addr; + grub_uint32_t size; +} GRUB_PACKED; + +/* + * DRTM Measurement Policy + */ +struct grub_slr_entry_policy +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t revision; + grub_uint16_t nr_entries; + /* policy_entries[] */ +} GRUB_PACKED; + +/* + * DRTM Measurement Entry + */ +struct grub_slr_policy_entry +{ + grub_uint16_t pcr; + grub_uint16_t entity_type; + grub_uint16_t flags; + grub_uint16_t reserved; + grub_uint64_t entity; + grub_uint64_t size; + char evt_info[GRUB_TPM_EVENT_INFO_LENGTH]; +} GRUB_PACKED; + +/* + * Secure Launch defined MTRR saving structures + */ +struct grub_slr_txt_mtrr_pair +{ + grub_uint64_t mtrr_physbase; + grub_uint64_t mtrr_physmask; +} GRUB_PACKED; + +struct grub_slr_txt_mtrr_state +{ + grub_uint64_t default_mem_type; + grub_uint64_t mtrr_vcnt; + struct grub_slr_txt_mtrr_pair mtrr_pair[GRUB_TXT_VARIABLE_MTRRS_LENGTH]; +} GRUB_PACKED; + +/* + * Intel TXT Info table + */ +struct grub_slr_entry_intel_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint64_t saved_misc_enable_msr; + struct grub_slr_txt_mtrr_state saved_bsp_mtrrs; +} GRUB_PACKED; + +/* + * AMD SKINIT Info table + */ +struct grub_slr_entry_amd_info +{ + struct grub_slr_entry_hdr hdr; + char reserved[]; /* Reserved for future use */ +} GRUB_PACKED; + +/* + * ARM DRTM Info table + */ +struct grub_slr_entry_arm_info +{ + struct grub_slr_entry_hdr hdr; + char reserved[]; /* Reserved for future use */ +} GRUB_PACKED; + +struct grub_slr_uefi_cfg_entry +{ + grub_uint16_t pcr; + grub_uint16_t reserved; + grub_uint64_t cfg; /* address or value */ + grub_uint32_t size; + char evt_info[GRUB_TPM_EVENT_INFO_LENGTH]; +} GRUB_PACKED; + +struct grub_slr_entry_uefi_config +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t revision; + grub_uint16_t nr_entries; + struct grub_slr_uefi_cfg_entry uefi_cfg_entries[]; +} GRUB_PACKED; + +static inline grub_addr_t +grub_slr_end_of_entries (struct grub_slr_table *table) +{ + return (grub_addr_t) table + table->size; +} + +static inline struct grub_slr_entry_hdr * +grub_slr_next_entry (struct grub_slr_table *table, + struct grub_slr_entry_hdr *curr) +{ + grub_addr_t addr; + struct grub_slr_entry_hdr *next; + + /* Can read the size field of current entry? */ + if ( grub_add ((grub_addr_t) curr, sizeof(*curr), &addr) ) + return NULL; + + /* Does current size overflow? */ + if ( grub_add ((grub_addr_t) curr, curr->size, &addr) ) + return NULL; + + /* Can read the size field of next entry? */ + if ( grub_add (addr, sizeof(*next), &addr) ) + return NULL; + + /* Does next element's header fit within the table? */ + if (addr >= grub_slr_end_of_entries (table)) + return NULL; + + next = (struct grub_slr_entry_hdr *) (addr - sizeof(*next)); + + /* Does next element fit within the table? */ + if (grub_slr_end_of_entries (table) - (addr - sizeof(*next)) < next->size) + return NULL; + + /* Is this the last element? */ + if (next->tag == GRUB_SLR_ENTRY_END) + return NULL; + + return next; +} + +static inline struct grub_slr_entry_hdr * +grub_slr_next_entry_by_tag (struct grub_slr_table *table, + struct grub_slr_entry_hdr *entry, + grub_uint16_t tag) +{ + if (!entry) /* Start from the beginning */ + entry = (struct grub_slr_entry_hdr *)((grub_uint8_t *) table + sizeof(*table)); + + for ( ; ; ) + { + if (entry->tag == tag) + return entry; + + entry = grub_slr_next_entry (table, entry); + if (!entry) + return NULL; + } + + return NULL; +} + +static inline int +grub_slr_add_entry (struct grub_slr_table *table, + struct grub_slr_entry_hdr *entry) +{ + struct grub_slr_entry_hdr *end; + grub_uint32_t new_size; + + if (entry->size < sizeof(*end) || + grub_add (table->size, entry->size, &new_size) || + new_size > table->max_size) + return -1; + + grub_memcpy ((grub_uint8_t *) table + table->size - sizeof(*end), entry, entry->size); + table->size += entry->size; + + end = (struct grub_slr_entry_hdr *)((grub_uint8_t *) table + table->size - sizeof(*end)); + end->tag = GRUB_SLR_ENTRY_END; + end->size = sizeof(*end); + + return 0; +} + +static inline void +grub_slr_init_table(struct grub_slr_table *slrt, grub_uint16_t architecture, + grub_uint32_t max_size) +{ + struct grub_slr_entry_hdr *end; + + if (max_size < sizeof(*slrt) + sizeof(*end)) + grub_fatal ("Requested SLRT max size (%" PRIuGRUB_UINT32_T + " B) is too small\n", max_size); + + slrt->magic = GRUB_SLR_TABLE_MAGIC; + slrt->revision = GRUB_SLR_TABLE_REVISION; + slrt->architecture = architecture; + slrt->size = sizeof(*slrt) + sizeof(*end); + slrt->max_size = max_size; + end = (struct grub_slr_entry_hdr *)((grub_uint8_t *) slrt + sizeof(*slrt)); + end->tag = GRUB_SLR_ENTRY_END; + end->size = sizeof(*end); +} + +#endif /* GRUB_SLR_TABLE_H */ From d8a93559a83235e6d3d384b13ca344188becb7e1 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:53:06 -0400 Subject: [PATCH 12/31] i386/txt: Add Intel TXT definitions header file Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper --- include/grub/i386/txt.h | 703 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 703 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..a7ddd3a66 --- /dev/null +++ b/include/grub/i386/txt.h @@ -0,0 +1,703 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Intel TXT definitions header file. + */ + +#ifndef GRUB_TXT_H +#define GRUB_TXT_H 1 + +#include +#include +#include +#include +#include + +/* Intel TXT Software Developers Guide Revision 017.4 */ + +/* Chapter 2, Table 4. MLE/SINIT Capabilities Field Bit Definitions */ +#define GRUB_TXT_PLATFORM_TYPE_LEGACY 0 +#define GRUB_TXT_PLATFORM_TYPE_CLIENT 1 +#define GRUB_TXT_PLATFORM_TYPE_SERVER 2 +#define GRUB_TXT_PLATFORM_TYPE_RESERVED 3 + +#define GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT (1<<0) +#define GRUB_TXT_CAPS_MONITOR_SUPPORT (1<<1) +#define GRUB_TXT_CAPS_ECX_PT_SUPPORT (1<<2) +#define GRUB_TXT_CAPS_STM_SUPPORT (1<<3) +#define GRUB_TXT_CAPS_TPM_12_NO_LEGACY_PCR_USAGE (1<<4) +#define GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE (1<<5) /* Must be 1 for TPM 2.0 */ +#define GRUB_TXT_CAPS_PLATFORM_TYPE (3<<6) +#define GRUB_TXT_CAPS_MAXPHYSADDR_SUPPORT (1<<8) +#define GRUB_TXT_CAPS_TPM_20_EVTLOG_SUPPORT (1<<9) +#define GRUB_TXT_CAPS_CBNT_SUPPORT (1<<10) +#define GRUB_TXT_CAPS_STARTUP_ACM_SUPPORT (7<<11) /* Reserved for MLE, must be 0 */ +#define GRUB_TXT_CAPS_DMA_PROTECTION (1<<14) /* 0 = Legacy, 1 = TPR */ +/* Rest is reserved */ + +/* Appendix A TXT Execution Technology Authenticated Code Modules */ +/* A.1 Authenticated Code Module Format */ +#define GRUB_TXT_ACM_MODULE_TYPE 2 + +#define GRUB_TXT_ACM_MODULE_SUB_TYPE_TXT_ACM 0 +#define GRUB_TXT_ACM_MODULE_SUB_TYPE_S_ACM 1 + +#define GRUB_TXT_ACM_FLAG_PREPRODUCTION (1<<14) +#define GRUB_TXT_ACM_FLAG_DEBUG_SIGNED (1<<15) + +#define GRUB_TXT_ACM_MODULE_VENDOR_INTEL 0x00008086 + +#define GRUB_TXT_MLE_MAX_SIZE 0x40000000 + +#define GRUB_MLE_AP_WAKE_BLOCK_SIZE (4 * GRUB_PAGE_SIZE) + +struct grub_txt_acm_header +{ + grub_uint16_t module_type; + grub_uint16_t module_sub_type; + grub_uint32_t header_len; + grub_uint32_t header_version; + grub_uint16_t chipset_id; + grub_uint16_t flags; + grub_uint32_t module_vendor; + grub_uint32_t date; /* e.g 20131231H == December 31, 2013 */ + grub_uint32_t size; /* multiples of 4 bytes */ + grub_uint16_t txt_svn; + grub_uint16_t se_svn; + grub_uint32_t code_control; + grub_uint32_t error_entry_point; + grub_uint32_t gdt_limit; + grub_uint32_t gdt_base; + grub_uint32_t seg_sel; + grub_uint32_t entry_point; + grub_uint8_t reserved2[64]; + grub_uint32_t key_size; + grub_uint32_t scratch_size; + /* RSA Pub Key and Signature */ +} GRUB_PACKED; + +/* A.1.2 ACM Information Table */ +#define GRUB_TXT_ACM_UUID "\xaa\x3a\xc0\x7f\xa7\x46\xdb\x18\x2e\xac\x69\x8f\x8d\x41\x7f\x5a" + +#define GRUB_TXT_ACM_CHIPSET_TYPE_BIOS 0 +#define GRUB_TXT_ACM_CHIPSET_TYPE_SINIT 1 +/* Revocation ACMs */ +#define GRUB_TXT_ACM_CHIPSET_TYPE_BIOS_RACM 8 +#define GRUB_TXT_ACM_CHIPSET_TYPE_SINIT_RACM 9 + +struct grub_txt_acm_info_table +{ + grub_uint8_t uuid[16]; + grub_uint8_t chipset_acm_type; + grub_uint8_t version; + grub_uint16_t length; + grub_uint32_t chipset_id_list; + grub_uint32_t os_sinit_data_ver; + grub_uint32_t min_mle_header_ver; + grub_uint32_t capabilities; + grub_uint32_t acm_version_revision; + grub_uint32_t processor_id_list; + /* Version >= 5 */ + grub_uint32_t tpm_info_list; +} GRUB_PACKED; + +struct grub_txt_acm_chipset_id_list +{ + grub_uint32_t count; + /* Array of chipset ID structs */ +} GRUB_PACKED; + +#define GRUB_TXT_ACM_REVISION_ID_MASK (1<<0) + +struct grub_txt_acm_chipset_id +{ + grub_uint32_t flags; + grub_uint16_t vendor_id; + grub_uint16_t device_id; + grub_uint16_t revision_id; + grub_uint16_t reserved; + grub_uint32_t extended_id; +} GRUB_PACKED; + +struct grub_txt_acm_processor_id_list +{ + grub_uint32_t count; + /* Array of processor ID structs */ +} GRUB_PACKED; + +struct grub_txt_acm_processor_id +{ + grub_uint32_t fms; + grub_uint32_t fms_mask; + grub_uint64_t platform_id; + grub_uint64_t platform_mask; +} GRUB_PACKED; + +struct grub_txt_acm_tpm_info +{ + grub_uint32_t capabilities; + grub_uint16_t count; + /* List of supported hash algorithm per TPM2 spec */ +} GRUB_PACKED; + +/* Appendix B SMX Interaction with Platform */ +/* B.1 Intel Trusted Execution Technology Configuration Registers */ + +#ifdef __x86_64__ +#define GRUB_TXT_CFG_REGS_PUB 0xfed30000ULL +#else +#define GRUB_TXT_CFG_REGS_PUB 0xfed30000 +#endif + +#define GRUB_TXT_STS 0x0000 +#define GRUB_TXT_ESTS 0x0008 +#define GRUB_TXT_ERRORCODE 0x0030 +#define GRUB_TXT_CMD_RESET 0x0038 +#define GRUB_TXT_CMD_CLOSE_PRIVATE 0x0048 +/* VER_FSBIF is considered deprecated, but some CPUs still use it */ +#define GRUB_TXT_VER_FSBIF 0x0100 +#define GRUB_TXT_DIDVID 0x0110 +#define GRUB_TXT_VER_QPIIF 0x0200 +#define GRUB_TXT_CMD_UNLOCK_MEM_CONFIG 0x0218 +#define GRUB_TXT_SINIT_BASE 0x0270 +#define GRUB_TXT_SINIT_SIZE 0x0278 +#define GRUB_TXT_MLE_JOIN 0x0290 +#define GRUB_TXT_HEAP_BASE 0x0300 +#define GRUB_TXT_HEAP_SIZE 0x0308 +/* DPR is considered deprecated, but some CPUs still use it */ +#define GRUB_TXT_DPR 0x0330 +#define GRUB_TXT_CMD_OPEN_LOCALITY1 0x0380 +#define GRUB_TXT_CMD_CLOSE_LOCALITY1 0x0388 +#define GRUB_TXT_CMD_OPEN_LOCALITY2 0x0390 +#define GRUB_TXT_CMD_CLOSE_LOCALITY2 0x0398 +#define GRUB_TXT_PUBLIC_KEY 0x0400 +#define GRUB_TXT_CMD_SECRETS 0x08e0 +#define GRUB_TXT_CMD_NO_SECRETS 0x08e8 +#define GRUB_TXT_E2STS 0x08f0 + +#define GRUB_TXT_STS_SENTER_DONE (1 << 0) +#define GRUB_TXT_STS_SEXIT_DONE (1 << 1) +#define GRUB_TXT_STS_MEM_CONFIG_LOCK (1 << 6) +#define GRUB_TXT_STS_PRIVATE_OPEN (1 << 7) +#define GRUB_TXT_STS_LOCALITY1_OPEN (1 << 15) +#define GRUB_TXT_STS_LOCALITY2_OPEN (1 << 16) + +#define GRUB_TXT_ESTS_TXT_RESET (1 << 0) + +#define GRUB_TXT_VER_FSBIF_DEBUG_FUSE (1 << 31) + +#define GRUB_TXT_VER_QPIIF_DEBUG_FUSE (1 << 31) + +#define GRUB_TXT_E2STS_SECRETS (1 << 1) + +union grub_txt_didvid +{ + grub_uint64_t value; + struct + { + grub_uint16_t vid; + grub_uint16_t did; + grub_uint16_t rid; + grub_uint16_t id_ext; + }; +} GRUB_PACKED; + +#define GRUB_TXT_VERSION_PROD_FUSED (1<<31) + +/* Appendix C Intel TXT Heap Memory */ + +/* Ext Data Structs */ + +struct grub_txt_heap_uuid +{ + grub_uint32_t data1; + grub_uint16_t data2; + grub_uint16_t data3; + grub_uint16_t data4; + grub_uint8_t data5[6]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_END 0 + +struct grub_txt_heap_end_element +{ + /* Empty, just the common header with type and size */ +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER 1 + +struct grub_txt_heap_bios_spec_ver_element +{ + grub_uint16_t spec_ver_major; + grub_uint16_t spec_ver_minor; + grub_uint16_t spec_ver_revision; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_ACM 2 + +struct grub_txt_heap_acm_element +{ + grub_uint32_t num_acms; + /* Array of num_acms grub_uint64_t addresses */ + grub_uint64_t addr[]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_STM 3 + +struct grub_txt_heap_stm_element +{ + /* STM specific BIOS properties */ + /* GNU extension used to counteract "error: flexible array member in a struct + * with no named members". */ + grub_uint8_t data[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_CUSTOM 4 + +struct grub_txt_heap_custom_element +{ + struct grub_txt_heap_uuid uuid; + /* Vendor Data */ + grub_uint8_t data[]; +} GRUB_PACKED; + +/* Deprecated, but still used for TPM 1.2 */ +#define GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR 5 + +struct grub_txt_heap_tpm_event_log_element +{ + grub_uint64_t event_log_phys_addr; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_MADT 6 + +struct grub_txt_heap_madt_element +{ + /* Copy of ACPI MADT table */ + /* GNU extension used to counteract "error: flexible array member in a struct + * with no named members". */ + grub_uint8_t madt[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 8 + +struct grub_txt_heap_event_log_pointer2_1_element +{ + grub_uint64_t phys_addr; + grub_uint32_t allocated_event_container_size; + grub_uint32_t first_record_offset; + grub_uint32_t next_record_offset; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG 9 + +struct grub_txt_heap_mcfg_element +{ + /* Copy of ACPI MCFG table */ + /* GNU extension used to counteract "error: flexible array member in a struct + * with no named members". */ + grub_uint8_t data[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE (2 * sizeof(grub_uint32_t)) + +struct grub_txt_heap_ext_data_element +{ + grub_uint32_t type; + grub_uint32_t size; /* Must be at least 8 bytes, includes size of this struct */ + union { + struct grub_txt_heap_end_element end; + struct grub_txt_heap_bios_spec_ver_element bios_spec_ver; + struct grub_txt_heap_acm_element acm; + struct grub_txt_heap_stm_element stm; + struct grub_txt_heap_custom_element custom; + struct grub_txt_heap_tpm_event_log_element tpm_event_log; + struct grub_txt_heap_madt_element madt; + struct grub_txt_heap_event_log_pointer2_1_element event_log_pointer2_1; + struct grub_txt_heap_mcfg_element mcfg; + }; +} GRUB_PACKED; + +/* TXT Heap Tables */ + +struct grub_txt_bios_data +{ + grub_uint32_t version; /* Currently 5 for TPM 1.2 and 6 for TPM 2.0 */ + grub_uint32_t bios_sinit_size; + grub_uint64_t reserved1; + grub_uint64_t reserved2; + grub_uint32_t num_logical_procs; + /* Versions >= 3 */ + grub_uint32_t sinit_flags; + /* Versions >= 5 with updates in version 6 */ + grub_uint32_t mle_flags; + /* Versions >= 4 */ + /* Ext Data Elements */ +} GRUB_PACKED; + +/* GRUB SLAUNCH specific definitions OS-MLE data */ +#define GRUB_SL_OS_MLE_STRUCT_VERSION 1 + +struct grub_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 boot_params_addr; + grub_uint64_t saved_misc_enable_msr; + struct grub_slaunch_mtrr_state saved_bsp_mtrrs; + grub_uint32_t ap_wake_block; + grub_uint32_t ap_wake_block_size; + grub_uint64_t evtlog_addr; + grub_uint32_t evtlog_size; + grub_uint8_t mle_scratch[64]; +} GRUB_PACKED; + +/* Table 29. OS to SINIT Data Table */ +#define GRUB_TXT_PCR_EXT_MAX_AGILITY_POLICY 0 +#define GRUB_TXT_PCR_EXT_MAX_PERF_POLICY 1 + +struct grub_txt_os_sinit_data +{ + grub_uint32_t version; /* Currently 6 for TPM 1.2 and 7 for TPM 2.0 */ + grub_uint32_t flags; /* Version 7+ only, otherwise reserved */ + grub_uint64_t mle_ptab; + grub_uint64_t mle_size; + grub_uint64_t mle_hdr_base; + grub_uint64_t vtd_pmr_lo_base; + grub_uint64_t vtd_pmr_lo_size; + grub_uint64_t vtd_pmr_hi_base; + grub_uint64_t vtd_pmr_hi_size; + grub_uint64_t lcp_po_base; + grub_uint64_t lcp_po_size; + grub_uint32_t capabilities; + /* Versions >= 5 */ + /* Warning: version 5 has pointer to RSDT here, not RSDP */ + grub_uint64_t efi_rsdp_ptr; + /* Versions >= 6 */ + /* Ext Data Elements */ + grub_uint8_t ext_data_elts[]; +} GRUB_PACKED; + +struct grub_txt_sinit_mle_data +{ + grub_uint32_t version; /* Current values are 6 through 9 */ + /* Reserved for versions >= 9, must be 0 */ + grub_uint8_t bios_acm_id[20]; + grub_uint32_t edx_senter_flags; + grub_uint64_t mseg_valid; + grub_uint8_t sinit_hash[20]; + grub_uint8_t mle_hash[20]; + grub_uint8_t stm_hash[20]; + grub_uint8_t lcp_policy_hash[20]; + grub_uint32_t lcp_policy_control; + /* Versions >= 7 */ + grub_uint32_t rlp_wakeup_addr; + grub_uint32_t reserved; + grub_uint32_t num_of_sinit_mdrs; + grub_uint32_t sinit_mdrs_table_offset; + grub_uint32_t sinit_vtd_dmar_table_size; + grub_uint32_t sinit_vtd_dmar_table_offset; + /* Versions >= 8 */ + grub_uint32_t processor_scrtm_status; /* Reserved for version 9 */ + /* Versions >= 9 */ + /* Ext Data Elements */ +} GRUB_PACKED; + +struct grub_txt_sinit_memory_descriptor_records +{ + grub_uint64_t address; + grub_uint64_t length; + grub_uint8_t type; + grub_uint8_t reserved[7]; +} GRUB_PACKED; + +/* Section 2 Measured Launch Environment */ +/* 2.1 MLE Architecture Overview */ +/* Table 3. MLE Header structure */ + +#define GRUB_TXT_MLE_UUID "\x5a\xac\x82\x90\x6f\x47\xa7\x74\x0f\x5c\x55\xa2\xcb\x51\xb6\x42" + +struct grub_txt_mle_header +{ + grub_uint8_t uuid[16]; + grub_uint32_t header_len; + grub_uint32_t version; + grub_uint32_t entry_point; + grub_uint32_t first_valid_page; + grub_uint32_t mle_start; + grub_uint32_t mle_end; + grub_uint32_t capabilities; + grub_uint32_t cmdline_start; + grub_uint32_t cmdline_end; +} GRUB_PACKED; + +struct grub_txt_heap_event_log_ptr_elt +{ + grub_uint64_t event_log_phys_addr; +} GRUB_PACKED; + +struct grub_txt_heap_event_log_ptr_elt2_1 +{ + grub_uint64_t phys_addr; + grub_uint32_t allcoated_event_container_size; + grub_uint32_t first_record_offset; + grub_uint32_t next_record_offset; +} GRUB_PACKED; + +/* TXT register and heap access */ + +static inline grub_uint8_t +grub_txt_reg_pub_read8 (grub_uint16_t reg) +{ + return grub_read8 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint32_t +grub_txt_reg_pub_read32 (grub_uint16_t reg) +{ + return grub_read32 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint64_t +grub_txt_reg_pub_read64 (grub_uint16_t reg) +{ + return grub_read64 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint8_t * +grub_txt_get_heap (void) +{ + return (grub_uint8_t *)(grub_addr_t) grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); +} + +static inline grub_uint32_t +grub_txt_get_heap_size (void) +{ + return grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); +} + +/* + * Each block of data on heap begins with 64-bit size field, followed by proper + * data. Specified size includes size field, so the minimal value of that field + * is 8. TXT SDG mentions that all sizes must be multiples of 8 bytes, but even + * BiosData produced by code signed by Intel doesn't follow that requirement. + * This means that we can't just cast pointer to arbitrary location on TXT heap + * with (grub_uint64_t *), because unaligned pointer is UB. + */ +static inline grub_uint64_t +grub_txt_bios_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)heap; +} + +static inline struct grub_txt_bios_data* +grub_txt_bios_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_bios_data*)(heap + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_os_mle_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + grub_txt_bios_data_size (heap)); +} + +static inline struct grub_txt_os_mle_data* +grub_txt_os_mle_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_os_mle_data*)(heap + + grub_txt_bios_data_size (heap) + + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_os_sinit_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap)); +} + +static inline struct grub_txt_os_sinit_data * +grub_txt_os_sinit_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_os_sinit_data*)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_sinit_mle_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + grub_txt_os_sinit_data_size (heap)); +} + +static inline struct grub_txt_sinit_mle_data* +grub_txt_sinit_mle_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_sinit_mle_data*)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + grub_txt_os_sinit_data_size (heap) + + sizeof (grub_uint64_t)); +} + +/* Intel 64 and IA-32 Architectures Software Developer’s Manual */ +/* Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z */ +/* Order Number: 325383-082US December 2023 */ + +/* CHAPTER 7 SAFER MODE EXTENSIONS REFERENCE */ + +/* Table 7-2. GETSEC Leaf Functions */ +#define GRUB_SMX_LEAF_CAPABILITIES 0 +#define GRUB_SMX_LEAF_UNDEFINED 1 +#define GRUB_SMX_LEAF_ENTERACCS 2 +#define GRUB_SMX_LEAF_EXITAC 3 +#define GRUB_SMX_LEAF_SENTER 4 +#define GRUB_SMX_LEAF_SEXIT 5 +#define GRUB_SMX_LEAF_PARAMETERS 6 +#define GRUB_SMX_LEAF_SMCTRL 7 +#define GRUB_SMX_LEAF_WAKEUP 8 + +/* Table 7-3. GETSEC Capability Result Encoding */ +#define GRUB_SMX_CAPABILITY_CHIPSET_PRESENT (1<<0) +#define GRUB_SMX_CAPABILITY_UNDEFINED (1<<1) +#define GRUB_SMX_CAPABILITY_ENTERACCS (1<<2) +#define GRUB_SMX_CAPABILITY_EXITAC (1<<3) +#define GRUB_SMX_CAPABILITY_SENTER (1<<4) +#define GRUB_SMX_CAPABILITY_SEXIT (1<<5) +#define GRUB_SMX_CAPABILITY_PARAMETERS (1<<6) +#define GRUB_SMX_CAPABILITY_SMCTRL (1<<7) +#define GRUB_SMX_CAPABILITY_WAKEUP (1<<8) +#define GRUB_SMX_CAPABILITY_EXTENDED_LEAFS (1<<31) + +static inline grub_uint32_t +grub_txt_getsec_capabilities (grub_uint32_t index) +{ + grub_uint32_t caps; + + asm volatile ("getsec" + : "=a" (caps) + : "a" (GRUB_SMX_LEAF_CAPABILITIES), "b" (index)); + return caps; +} + +static inline void +grub_txt_getsec_parameters (grub_uint32_t index, grub_uint32_t *eax_out, + grub_uint32_t *ebx_out, grub_uint32_t *ecx_out) +{ + if (!eax_out || !ebx_out || !ecx_out) + return; + + asm volatile ("getsec" + : "=a" (*eax_out), "=b" (*ebx_out), "=c" (*ecx_out) + : "a" (GRUB_SMX_LEAF_PARAMETERS), "b" (index)); +} + +#define GRUB_SMX_PARAMETER_TYPE_MASK 0x1f +#define GRUB_SMX_PARAMETER_NULL 0 +#define GRUB_SMX_PARAMETER_ACM_VERSIONS 1 +#define GRUB_SMX_PARAMETER_MAX_ACM_SIZE 2 +#define GRUB_SMX_PARAMETER_ACM_MEMORY_TYPES 3 +#define GRUB_SMX_PARAMETER_SENTER_CONTROLS 4 +#define GRUB_SMX_PARAMETER_TXT_EXTENSIONS 5 + +#define GRUB_SMX_PARAMETER_MAX_VERSIONS 0x20 + +#define GRUB_SMX_GET_MAX_ACM_SIZE(v) ((v) & ~(__typeof__(v))GRUB_SMX_PARAMETER_TYPE_MASK) + +#define GRUB_SMX_ACM_MEMORY_TYPE_UC 0x00000100 +#define GRUB_SMX_ACM_MEMORY_TYPE_WC 0x00000200 +#define GRUB_SMX_ACM_MEMORY_TYPE_WT 0x00001000 +#define GRUB_SMX_ACM_MEMORY_TYPE_WP 0x00002000 +#define GRUB_SMX_ACM_MEMORY_TYPE_WB 0x00004000 + +#define GRUB_SMX_GET_ACM_MEMORY_TYPES(v) ((v) & ~(__typeof__(v))GRUB_SMX_PARAMETER_TYPE_MASK) + +#define GRUB_SMX_GET_SENTER_CONTROLS(v) ((v & 0x7f00) >> 8) + +#define GRUB_SMX_PROCESSOR_BASE_SCRTM 0x00000020 +#define GRUB_SMX_MACHINE_CHECK_HANDLING 0x00000040 +#define GRUB_SMX_GET_TXT_EXT_FEATURES(v) (v & (__typeof__(v))(GRUB_SMX_PROCESSOR_BASE_SCRTM|GRUB_SMX_MACHINE_CHECK_HANDLING)) + +#define GRUB_SMX_DEFAULT_VERSION 0x0 +#define GRUB_SMX_DEFAULT_VERSION_MASK 0xffffffff +#define GRUB_SMX_DEFAULT_MAX_ACM_SIZE 0x8000 /* 32K */ +#define GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE GRUB_SMX_ACM_MEMORY_TYPE_UC +#define GRUB_SMX_DEFAULT_SENTER_CONTROLS 0x0 + +/* + * Measured Launch Environment Developer’s Guide, + * Table 29. OS to SINIT Data Table + */ +#define GRUB_TXT_PMR_ALIGN_SHIFT 21 +#define GRUB_TXT_PMR_ALIGN (1 << GRUB_TXT_PMR_ALIGN_SHIFT) + +struct grub_smx_supported_versions +{ + grub_uint32_t mask; + grub_uint32_t version; +} GRUB_PACKED; + +struct grub_smx_parameters +{ + struct grub_smx_supported_versions versions[GRUB_SMX_PARAMETER_MAX_VERSIONS]; + grub_uint32_t version_count; + grub_uint32_t max_acm_size; + grub_uint32_t acm_memory_types; + grub_uint32_t senter_controls; + grub_uint32_t txt_feature_ext_flags; +} GRUB_PACKED; + +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 From 9736d0573f4be14fe33acd3363b8600003e21eac Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:57:33 -0400 Subject: [PATCH 13/31] i386/txt: Add Intel TXT core implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Michał Żygowski Signed-off-by: Krystian Hebel --- grub-core/loader/i386/txt/txt.c | 997 ++++++++++++++++++++++++++++++++ include/grub/i386/slaunch.h | 1 - 2 files changed, 997 insertions(+), 1 deletion(-) 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..b92107d50 --- /dev/null +++ b/grub-core/loader/i386/txt/txt.c @@ -0,0 +1,997 @@ +/* + * txt.c: Intel(r) TXT support functions, including initiating measured + * launch, post-launch, AP wakeup, etc. + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + grub_uint64_t feat_ctrl = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if (!(feat_ctrl & GRUB_MSR_X86_FEATURE_CTRL_LOCK)) + { + grub_dprintf ("slaunch", "Firmware didn't lock FEATURE_CONTROL MSR," + "locking it now\n"); + /* Not setting SENTER_FUNCTIONS and SENTER_ENABLE because they were tested + * in grub_txt_verify_platform() */ + feat_ctrl |= GRUB_MSR_X86_ENABLE_VMX_OUT_SMX | + GRUB_MSR_X86_ENABLE_VMX_IN_SMX | + GRUB_MSR_X86_FEATURE_CTRL_LOCK; + grub_wrmsr (GRUB_MSR_X86_FEATURE_CONTROL, feat_ctrl); + } + + /* Enable SMX mode. */ + grub_write_cr4 (grub_read_cr4 () | GRUB_CR4_X86_SMXE); + + caps = grub_txt_getsec_capabilities (0); + + if (!(caps & GRUB_SMX_CAPABILITY_CHIPSET_PRESENT)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("TXT-capable chipset is not present")); + goto fail; + } + + if (!(caps & GRUB_SMX_CAPABILITY_SENTER)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[SENTER] is not available")); + goto fail; + } + + if (!(caps & GRUB_SMX_CAPABILITY_PARAMETERS)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[PARAMETERS] is not available")); + goto fail; + } + + return GRUB_ERR_NONE; + + fail: + /* Disable SMX mode on failure. */ + grub_write_cr4 (grub_read_cr4 () & ~GRUB_CR4_X86_SMXE); + + return grub_errno; +} + +static grub_err_t +grub_txt_smx_parameters (struct grub_smx_parameters *params) +{ + grub_uint32_t index = 0, eax, ebx, ecx, param_type; + + grub_memset (params, 0, sizeof(*params)); + + params->max_acm_size = GRUB_SMX_DEFAULT_MAX_ACM_SIZE; + params->acm_memory_types = GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE; + params->senter_controls = GRUB_SMX_DEFAULT_SENTER_CONTROLS; + + do + { + grub_txt_getsec_parameters (index, &eax, &ebx, &ecx); + param_type = eax & GRUB_SMX_PARAMETER_TYPE_MASK; + + switch (param_type) + { + case GRUB_SMX_PARAMETER_NULL: + break; /* This means done. */ + + case GRUB_SMX_PARAMETER_ACM_VERSIONS: + if (params->version_count >= GRUB_SMX_PARAMETER_MAX_VERSIONS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Too many ACM versions")); + params->versions[params->version_count].mask = ebx; + params->versions[params->version_count++].version = ecx; + break; + + case GRUB_SMX_PARAMETER_MAX_ACM_SIZE: + params->max_acm_size = GRUB_SMX_GET_MAX_ACM_SIZE (eax); + break; + + case GRUB_SMX_PARAMETER_ACM_MEMORY_TYPES: + params->acm_memory_types = GRUB_SMX_GET_ACM_MEMORY_TYPES (eax); + break; + + case GRUB_SMX_PARAMETER_SENTER_CONTROLS: + params->senter_controls = GRUB_SMX_GET_SENTER_CONTROLS (eax); + break; + + case GRUB_SMX_PARAMETER_TXT_EXTENSIONS: + params->txt_feature_ext_flags = GRUB_SMX_GET_TXT_EXT_FEATURES (eax); + break; + + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SMX parameter")); + } + + ++index; + + } while (param_type != GRUB_SMX_PARAMETER_NULL); + + /* If no ACM versions were found, set the default one. */ + if (!params->version_count) + { + params->versions[0].mask = GRUB_SMX_DEFAULT_VERSION_MASK; + params->versions[0].version = GRUB_SMX_DEFAULT_VERSION; + params->version_count++; + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_txt_prepare_cpu (void) +{ + struct grub_smx_parameters params; + grub_uint32_t i; + grub_uint64_t mcg_cap, mcg_stat; + unsigned long cr0; + grub_err_t err; + + cr0 = grub_read_cr0 (); + + /* Cache must be enabled (CR0.CD = CR0.NW = 0). */ + cr0 &= ~(GRUB_CR0_X86_CD | GRUB_CR0_X86_NW); + + /* Native FPU error reporting must be enabled for proper interaction behavior. */ + cr0 |= GRUB_CR0_X86_NE; + + grub_write_cr0 (cr0); + + /* Disable virtual-8086 mode (EFLAGS.VM = 0). */ + grub_write_flags_register (grub_read_flags_register () & ~GRUB_EFLAGS_X86_VM); + + /* + * Verify all machine check status registers are clear (unless + * support preserving them). + */ + + /* Is machine check in progress? */ + if ( grub_rdmsr (GRUB_MSR_X86_MCG_STATUS) & GRUB_MSR_MCG_STATUS_MCIP ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("machine check in progress during secure launch")); + + err = grub_txt_smx_parameters (¶ms); + if (err != GRUB_ERR_NONE) + return err; + + if (params.txt_feature_ext_flags & GRUB_SMX_PROCESSOR_BASE_SCRTM) + grub_dprintf ("slaunch", "CPU supports processor-based S-CRTM\n"); + + if (params.txt_feature_ext_flags & GRUB_SMX_MACHINE_CHECK_HANDLING) + grub_dprintf ("slaunch", "CPU supports preserving machine check errors\n"); + else + { + grub_dprintf ("slaunch", "CPU does not support preserving machine check errors\n"); + + /* Check if all machine check registers are clear. */ + mcg_cap = grub_rdmsr (GRUB_MSR_X86_MCG_CAP); + for (i = 0; i < (mcg_cap & GRUB_MSR_MCG_BANKCNT_MASK); ++i) + { + mcg_stat = grub_rdmsr (GRUB_MSR_X86_MC0_STATUS + i * 4); + if (mcg_stat & (1ULL << 63)) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("secure launch MCG[%u] = %" PRIxGRUB_UINT64_T " ERROR"), i, + mcg_stat); + } + } + + return GRUB_ERR_NONE; +} + +static void +save_mtrrs (struct grub_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_PHYSMASK (i)); + os_mle_data->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physbase = + grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (i)); + } +} + +static void +set_all_mtrrs (int enable) +{ + grub_uint64_t mtrr_def_type; + + mtrr_def_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + + if ( enable ) + mtrr_def_type |= GRUB_MSR_X86_MTRR_ENABLE; + else + mtrr_def_type &= ~GRUB_MSR_X86_MTRR_ENABLE; + + grub_wrmsr (GRUB_MSR_X86_MTRR_DEF_TYPE, mtrr_def_type); +} + +#define SINIT_MTRR_MASK 0xFFFFFF /* SINIT requires 36b mask */ + +/* + * Note: bitfields in following structures are assumed to work on x86 and + * nothing else. All compilers supported by GRUB agree when it comes to layout + * of bits that is consistent with hardware implementation. It was decided to + * use bitfields for better readability instead of manual shifting and masking. + */ +union mtrr_physbase_t +{ + grub_uint64_t raw; + struct + { + grub_uint64_t type : 8; + grub_uint64_t reserved1 : 4; + grub_uint64_t base : 52; /* Define as max width and mask w/ */ + /* MAXPHYADDR when using */ + }; +} GRUB_PACKED; + +union mtrr_physmask_t +{ + grub_uint64_t raw; + struct + { + grub_uint64_t reserved1 : 11; + grub_uint64_t v : 1; /* valid */ + grub_uint64_t mask : 52; /* define as max width and mask w/ */ + /* MAXPHYADDR when using */ + }; +} GRUB_PACKED; + +static inline grub_uint32_t +bsrl (grub_uint32_t mask) +{ + grub_uint32_t result; + + asm ("bsrl %1,%0" : "=r" (result) : "rm" (mask) : "cc"); + + return result; +} + +static inline int +fls (int mask) +{ + return (mask == 0 ? mask : (int)bsrl ((grub_uint32_t)mask) + 1); +} + +/* + * set the memory type for specified range (base to base+size) + * to mem_type and everything else to UC + */ +static grub_err_t +set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, + grub_uint32_t mem_type) +{ + grub_uint64_t mtrr_def_type; + grub_uint64_t mtrr_cap; + union mtrr_physbase_t mtrr_physbase; + union mtrr_physmask_t mtrr_physmask; + grub_uint32_t vcnt, pages_in_range; + unsigned long ndx, base_v; + int i = 0, j, num_pages, mtrr_s; + + /* Disable all fixed MTRRs, set default type to UC */ + mtrr_def_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + mtrr_def_type &= ~(GRUB_MSR_X86_MTRR_ENABLE_FIXED | GRUB_MSR_X86_DEF_TYPE_MASK); + mtrr_def_type |= GRUB_MTRR_MEMORY_TYPE_UC; + grub_wrmsr (GRUB_MSR_X86_MTRR_DEF_TYPE, mtrr_def_type); + + /* Initially disable all variable MTRRs (we'll enable the ones we use) */ + mtrr_cap = grub_rdmsr (GRUB_MSR_X86_MTRRCAP); + vcnt = (mtrr_cap & GRUB_MSR_X86_VCNT_MASK); + + for ( ndx = 0; ndx < vcnt; ndx++ ) + { + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.v = 0; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + } + + /* Map all AC module pages as mem_type */ + num_pages = GRUB_PAGE_UP(size) >> GRUB_PAGE_SHIFT; + + grub_dprintf ("slaunch", "setting MTRRs for acmod: base=%p, size=%x, num_pages=%d\n", + base, size, num_pages); + + /* Each VAR MTRR base must be a multiple of that MTRR's Size */ + /* grub_txt_sinit_select() made sure that base is at least 4K-aligned */ + base_v = (unsigned long)base; + /* MTRR size in pages */ + mtrr_s = 1; + + while ( (base_v & 0x01) == 0 ) + { + i++; + base_v = base_v >> 1; + } + + for (j = i - 12; j > 0; j--) + mtrr_s = mtrr_s*2; /* mtrr_s = mtrr_s << 1 */ + + grub_dprintf ("slaunch", "The maximum allowed MTRR range size=%d Pages \n", mtrr_s); + + ndx = 0; + + while ( num_pages >= mtrr_s ) + { + mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx)); + mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & + SINIT_MTRR_MASK; + mtrr_physbase.type = mem_type; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx), mtrr_physbase.raw); + + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.mask = ~(mtrr_s - 1) & SINIT_MTRR_MASK; + mtrr_physmask.v = 1; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + + base += (mtrr_s * GRUB_PAGE_SIZE); + num_pages -= mtrr_s; + ndx++; + if ( ndx == vcnt ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("exceeded number of var MTRRs when mapping range")); + } + + while ( num_pages > 0 ) + { + /* Set the base of the current MTRR */ + mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx)); + mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & + SINIT_MTRR_MASK; + mtrr_physbase.type = mem_type; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx), mtrr_physbase.raw); + + /* + * Calculate MTRR mask + * MTRRs can map pages in power of 2 + * may need to use multiple MTRRS to map all of region + */ + pages_in_range = 1 << (fls (num_pages) - 1); + + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.mask = ~(pages_in_range - 1) & SINIT_MTRR_MASK; + mtrr_physmask.v = 1; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + + /* + * Prepare for the next loop depending on number of pages + * We figure out from the above how many pages could be used in this + * mtrr. Then we decrement the count, increment the base, + * increment the mtrr we are dealing with, and if num_pages is + * still not zero, we do it again. + */ + base += (pages_in_range * GRUB_PAGE_SIZE); + num_pages -= pages_in_range; + ndx++; + if ( ndx == vcnt ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("exceeded number of var MTRRs when mapping range")); + } + + return GRUB_ERR_NONE; +} + +/* + * this must be done for each processor so that all have the same + * memory types + */ +static grub_err_t +set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) +{ + unsigned long eflags; + unsigned long cr0, cr4; + grub_err_t err; + + /* + * need to do some things before we start changing MTRRs + * + * since this will modify some of the MTRRs, they should be saved first + * so that they can be restored once the AC mod is done + */ + + /* Disable interrupts */ + eflags = grub_read_flags_register (); + grub_write_flags_register (eflags & ~GRUB_EFLAGS_X86_IF); + + /* Save CR0 then disable cache (CRO.CD=1, CR0.NW=0) */ + cr0 = grub_read_cr0 (); + grub_write_cr0 ( (cr0 & ~GRUB_CR0_X86_NW) | GRUB_CR0_X86_CD ); + + /* Flush caches */ + asm volatile ("wbinvd"); + + /* Save CR4 and disable global pages (CR4.PGE=0) */ + cr4 = grub_read_cr4 (); + grub_write_cr4 (cr4 & ~GRUB_CR4_X86_PGE); + + /* Disable MTRRs */ + set_all_mtrrs (0); + + /* Set MTRRs for AC mod and rest of memory */ + err = set_mtrr_mem_type ((grub_uint8_t*)hdr, hdr->size*4, + GRUB_MTRR_MEMORY_TYPE_WB); + + /* Undo some of earlier changes and enable our new settings */ + + /* Flush caches */ + asm volatile ("wbinvd"); + + /* Enable MTRRs */ + set_all_mtrrs (1); + + /* Restore CR0 (caching) */ + grub_write_cr0 (cr0); + + /* Restore CR4 (global pages) */ + grub_write_cr4 (cr4); + + /* Restore flags */ + grub_write_flags_register (eflags); + + return err; +} + +/* Adds new element to the end. `size` does not include common header. */ +/* Assume that heap was cleared and there is enough space to add the element. */ +static inline struct grub_txt_heap_ext_data_element * +add_ext_data_elt (struct grub_txt_os_sinit_data *os_sinit_data, + grub_uint32_t type, grub_uint32_t size) +{ + struct grub_txt_heap_ext_data_element *elt = + (struct grub_txt_heap_ext_data_element *) os_sinit_data->ext_data_elts; + + while (elt->type != GRUB_TXT_HEAP_EXTDATA_TYPE_END) + elt = (struct grub_txt_heap_ext_data_element *)((grub_uint8_t *)elt + elt->size); + + elt->type = type; + elt->size = size + GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE; + + return elt; +} + +static grub_err_t +init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header *sinit) +{ + grub_uint8_t *txt_heap; + grub_uint32_t os_sinit_data_ver, sinit_caps; + grub_uint64_t *size; + grub_uint64_t size_total; + struct grub_txt_os_mle_data *os_mle_data; + struct grub_txt_os_sinit_data *os_sinit_data; + struct grub_txt_heap_ext_data_element *elt; +#ifdef GRUB_MACHINE_EFI + struct grub_acpi_rsdp_v20 *rsdp; +#endif + + /* BIOS data already verified in grub_txt_verify_platform(). */ + + txt_heap = grub_txt_get_heap (); + + grub_dprintf ("slaunch", "TXT heap %p\n", txt_heap); + + /* OS/loader to MLE data. */ + + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + grub_dprintf ("slaunch", "OS MLE data: %p\n", os_mle_data); + size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t); + + if (grub_add (grub_txt_bios_data_size (txt_heap), *size, &size_total) || + (size_total > grub_txt_get_heap_size ())) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsMleData")); + } + + grub_memset (os_mle_data, 0, sizeof (*os_mle_data)); + + os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION; + os_mle_data->boot_params_addr = slparams->boot_params_addr; + os_mle_data->saved_misc_enable_msr = grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE); + + os_mle_data->ap_wake_block = slparams->ap_wake_block; + os_mle_data->ap_wake_block_size = slparams->ap_wake_block_size; + + os_mle_data->evtlog_addr = slparams->tpm_evt_log_base; + os_mle_data->evtlog_size = slparams->tpm_evt_log_size; + + grub_dprintf ("slaunch", "Saving MTRRs to OS MLE data\n"); + save_mtrrs (os_mle_data); + + /* OS/loader to SINIT data. */ + grub_dprintf ("slaunch", "Get supported OS SINIT data version\n"); + os_sinit_data_ver = grub_txt_supported_os_sinit_data_ver (sinit); + + if (os_sinit_data_ver < OS_SINIT_DATA_MIN_VER) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("unsupported OS to SINIT data version in SINIT ACM: %d" + " expected >= %d"), os_sinit_data_ver, OS_SINIT_DATA_MIN_VER); + + os_sinit_data = grub_txt_os_sinit_data_start (txt_heap); + grub_dprintf ("slaunch", "OS SINIT data: %p\n", os_sinit_data); + size = (grub_uint64_t *) ((grub_addr_t) os_sinit_data - sizeof (grub_uint64_t)); + + *size = sizeof(grub_uint64_t) + sizeof (struct grub_txt_os_sinit_data) + + GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE /* End element */; + + if (grub_get_tpm_ver () == GRUB_TPM_12) + *size += GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_tpm_event_log_element); + else if (grub_get_tpm_ver () == GRUB_TPM_20) + *size += GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_event_log_pointer2_1_element); + else + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM version")); + + if (grub_add (size_total, *size, &size_total) || + (size_total > grub_txt_get_heap_size ())) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsSinitData")); + } + + grub_memset (os_sinit_data, 0, *size); + +#ifdef GRUB_MACHINE_EFI + rsdp = grub_acpi_get_rsdpv2 (); + + if (rsdp == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("ACPI RSDP 2.0 missing\n")); + + os_sinit_data->efi_rsdp_ptr = (grub_uint64_t)(grub_addr_t) rsdp; +#endif + + os_sinit_data->mle_ptab = slparams->mle_ptab_target; + os_sinit_data->mle_size = slparams->mle_size; + + os_sinit_data->mle_hdr_base = slparams->mle_header_offset; + + /* TODO: Check low PMR with RMRR. Look at relevant tboot code too. */ + /* TODO: Kernel should not allocate any memory outside of PMRs regions!!! */ + os_sinit_data->vtd_pmr_lo_base = 0; + os_sinit_data->vtd_pmr_lo_size = ALIGN_DOWN (grub_mmap_get_highest (0x100000000), + GRUB_TXT_PMR_ALIGN); + + os_sinit_data->vtd_pmr_hi_base = ALIGN_UP (grub_mmap_get_lowest (0x100000000), + GRUB_TXT_PMR_ALIGN); + os_sinit_data->vtd_pmr_hi_size = ALIGN_DOWN (grub_mmap_get_highest (0xffffffffffffffff), + GRUB_TXT_PMR_ALIGN); + os_sinit_data->vtd_pmr_hi_size -= os_sinit_data->vtd_pmr_hi_base; + + grub_dprintf ("slaunch", + "vtd_pmr_lo_base: 0x%" PRIxGRUB_UINT64_T " vtd_pmr_lo_size: 0x%" + PRIxGRUB_UINT64_T " vtd_pmr_hi_base: 0x%" PRIxGRUB_UINT64_T + " vtd_pmr_hi_size: 0x%" PRIxGRUB_UINT64_T "\n", + os_sinit_data->vtd_pmr_lo_base, os_sinit_data->vtd_pmr_lo_size, + os_sinit_data->vtd_pmr_hi_base, os_sinit_data->vtd_pmr_hi_size); + + sinit_caps = grub_txt_get_sinit_capabilities (sinit); + + /* + * In the latest TXT Software Development Guide as of now (April 2023, + * Revision 017.4) bits 4 and 5 (used to be "no legacy PCR usage" and + * "auth PCR usage" respectively) of capabilities field bit are reserved. + * Bit 4 is ignored, but it is returned as 0 by SINIT[CAPABILITIES], + * while bit 5 is forced to 1 for compatibility reasons. This is related + * to support for TPM 1.2 devices, which always had this bit set. + * + * There are TPM 2.0 platforms that will have both bits set. TXT + * specification doesn't specify when this has changed, so we can't + * reliably test for those. + */ + os_sinit_data->capabilities = GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE; + + /* + * APs (application processors) can't be brought up by usual INIT-SIPI-SIPI + * sequence after Measured Launch, otherwise the MLE integrity is lost. + * Choose monitor RLP (responding logical processor, fancy name for AP) wakeup + * mechanism first, if that isn't supported fall back to GETSEC[WAKEUP]. */ + if (sinit_caps & GRUB_TXT_CAPS_MONITOR_SUPPORT) + os_sinit_data->capabilities |= GRUB_TXT_CAPS_MONITOR_SUPPORT; + else if (sinit_caps & GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT) + os_sinit_data->capabilities |= GRUB_TXT_CAPS_GETSEC_WAKE_SUPPORT; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("lack of RLP wakeup mechanism")); + + if (sinit_caps & GRUB_TXT_CAPS_ECX_PT_SUPPORT) + os_sinit_data->capabilities |= GRUB_TXT_CAPS_ECX_PT_SUPPORT; + + if (grub_get_tpm_ver () == GRUB_TPM_12) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("TPM 1.2 detected, but not implemented yet")); + 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; + + elt = add_ext_data_elt(os_sinit_data, + GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1, + sizeof (struct grub_txt_heap_event_log_pointer2_1_element)); + elt->event_log_pointer2_1.phys_addr = slparams->tpm_evt_log_base; + elt->event_log_pointer2_1.allocated_event_container_size = slparams->tpm_evt_log_size; + } + + elt = add_ext_data_elt(os_sinit_data, GRUB_TXT_HEAP_EXTDATA_TYPE_END, 0); + + if ((grub_uint8_t *)elt + elt->size > + (grub_uint8_t *)grub_txt_sinit_mle_data_start (txt_heap) - sizeof(grub_uint64_t)) + return grub_error (GRUB_ERR_BUG, + N_("error in OsSinitData size requirements calculation")); + + /* SinitMleDataSize isn't known at this point, it is crafted by SINIT ACM. + * We can only test if size field fits and hope that ACM checks the rest. */ + if (grub_add (size_total, sizeof (grub_uint64_t), &size_total) || + (size_total > grub_txt_get_heap_size ())) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for SinitMleDataSize")); + + grub_dprintf ("slaunch", "TXT HEAP init done\n"); + + return GRUB_ERR_NONE; +} + +/* + * TODO: Why 1 GiB limit? It does not seem that it is required by TXT spec. + * If there is a limit then it should be checked before allocation and image load. + * + * If enough room is available in front of the MLE, the maximum size of an + * MLE that can be covered is 1G. This is due to having 512 PDEs pointing + * to 512 page tables with 512 PTEs each. + */ +grub_uint32_t +grub_txt_get_mle_ptab_size (grub_uint32_t mle_size) +{ + /* + * #PT + 1 PT + #PD + 1 PD + 1 PDT + * + * Why do we need 2 extra PTEs and PDEs? Because MLE image may not + * start and end at PTE (page) and PDE (2 MiB) boundary... + */ + return ((((mle_size / GRUB_PAGE_SIZE) + 2) / 512) + /* Number of PTs */ +// 1 + /* PT */ + (((mle_size / (512 * GRUB_PAGE_SIZE)) + 2) / 512) + /* Number of PDs */ +// 1 + /* PD */ + 1) /* PDT */ + * GRUB_PAGE_SIZE; +} + +/* Page directory and table entries only need Present set */ +#define MAKE_PT_MLE_ENTRY(addr) (((grub_uint64_t)(grub_addr_t)(addr) & GRUB_PAGE_MASK) | 0x01) + +/* + * The MLE page tables have to be below the MLE and have no special regions in + * between them and the MLE (this is a bit of an unwritten rule). + * 20 pages are carved out of memory below the MLE. That leave 18 page table + * pages that can cover up to 36M . + * can only contain 4k pages + * + * TODO: TXT Spec p.32; List section name and number with PT MLE requirements here. + * + * TODO: This function is not able to cover MLEs larger than 1 GiB. Fix it!!! + * After fixing increase GRUB_TXT_MLE_MAX_SIZE too. + */ +void +grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams) +{ + grub_uint8_t *pg_dir, *pg_dir_ptr_tab = slparams->mle_ptab_mem, *pg_tab; + grub_uint32_t mle_off = 0, pd_off = 0; + grub_uint64_t *pde, *pte; + + grub_memset (pg_dir_ptr_tab, 0, slparams->mle_ptab_size); + + pg_dir = pg_dir_ptr_tab + GRUB_PAGE_SIZE; + pg_tab = pg_dir + GRUB_PAGE_SIZE; + + /* Only use first entry in page dir ptr table */ + *(grub_uint64_t *)pg_dir_ptr_tab = MAKE_PT_MLE_ENTRY(pg_dir); + + /* Start with first entry in page dir */ + *(grub_uint64_t *)pg_dir = MAKE_PT_MLE_ENTRY(pg_tab); + + pte = (grub_uint64_t *)pg_tab; + pde = (grub_uint64_t *)pg_dir; + + do + { + /* mle_start may be unaligned, handled by mask in MAKE_PT_MLE_ENTRY */ + *pte = MAKE_PT_MLE_ENTRY(slparams->mle_start + mle_off); + + pte++; + mle_off += GRUB_PAGE_SIZE; + + if (!(++pd_off % 512)) + { + /* Break if we don't need any additional page entries */ + if (mle_off >= slparams->mle_size) + break; + pde++; + *pde = MAKE_PT_MLE_ENTRY(pte); + } + /* Add one page in case mle_start isn't aligned */ + } while (mle_off - GRUB_PAGE_SIZE + 1 < slparams->mle_size); +} + +grub_err_t +grub_txt_init (void) +{ + grub_err_t err; + + err = grub_txt_verify_platform (); + + if (err != GRUB_ERR_NONE) + return err; + + err = enable_smx_mode (); + + if (err != GRUB_ERR_NONE) + return err; + + return GRUB_ERR_NONE; +} + +void +grub_txt_shutdown (void) +{ + /* Disable SMX mode. */ + grub_write_cr4 (grub_read_cr4 () & ~GRUB_CR4_X86_SMXE); +} + +void +grub_txt_state_show (void) +{ + union { + grub_uint64_t d64; + grub_uint32_t d32; + grub_uint8_t d8; + grub_uint8_t a8[8]; + } data; + int i; + union grub_txt_didvid didvid; + + data.d64 = grub_txt_reg_pub_read64 (GRUB_TXT_STS); + grub_printf (" TXT.STS: 0x%016" PRIxGRUB_UINT64_T "\n" + " SENTER.DONE.STS: %d\n" + " SEXIT.DONE.STS: %d\n" + " MEM-CONFIGLOCK.STS: %d\n" + " PRIVATEOPEN.STS: %d\n" + " TXT.LOCALITY1.OPEN.STS: %d\n" + " TXT.LOCALITY2.OPEN.STS: %d\n", + data.d64, !!(data.d64 & GRUB_TXT_STS_SENTER_DONE), + !!(data.d64 & GRUB_TXT_STS_SEXIT_DONE), + !!(data.d64 & GRUB_TXT_STS_MEM_CONFIG_LOCK), + !!(data.d64 & GRUB_TXT_STS_PRIVATE_OPEN), + !!(data.d64 & GRUB_TXT_STS_LOCALITY1_OPEN), + !!(data.d64 & GRUB_TXT_STS_LOCALITY2_OPEN)); + + /* Only least significant byte has a meaning. */ + data.d8 = grub_txt_reg_pub_read8 (GRUB_TXT_ESTS); + grub_printf (" TXT.ESTS: 0x%02x\n" + " TXT_RESET.STS: %d\n", data.d8, + !!(data.d8 & GRUB_TXT_ESTS_TXT_RESET)); + + data.d64 = grub_txt_reg_pub_read64 (GRUB_TXT_E2STS); + grub_printf (" TXT.E2STS: 0x%016" PRIxGRUB_UINT64_T "\n" + " SECRETS.STS: %d\n", data.d64, + !!(data.d64 & GRUB_TXT_E2STS_SECRETS)); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_ERRORCODE); + grub_printf (" TXT.ERRORCODE: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + didvid.value = grub_txt_reg_pub_read64 (GRUB_TXT_DIDVID); + grub_printf (" TXT.DIDVID: 0x%016" PRIxGRUB_UINT64_T "\n" + " VID: 0x%04x\n" + " DID: 0x%04x\n" + " RID: 0x%04x\n" + " ID-EXT: 0x%04x\n", + didvid.value, didvid.vid, didvid.did, didvid.rid, didvid.id_ext); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_VER_FSBIF); + grub_printf (" TXT.VER.FSBIF: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + if ((data.d32 != 0x00000000) && (data.d32 != 0xffffffff)) + grub_printf (" DEBUG.FUSE: %d\n", !!(data.d32 & GRUB_TXT_VER_FSBIF_DEBUG_FUSE)); + else + { + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_VER_QPIIF); + grub_printf (" TXT.VER.QPIIF: 0x%08" PRIxGRUB_UINT32_T "\n" + " DEBUG.FUSE: %d\n", data.d32, + !!(data.d32 & GRUB_TXT_VER_QPIIF_DEBUG_FUSE)); + } + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_BASE); + grub_printf (" TXT.SINIT.BASE: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_SIZE); + grub_printf (" TXT.SINIT.SIZE: %" PRIuGRUB_UINT32_T + " B (0x%" PRIxGRUB_UINT32_T ")\n", data.d32, data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); + grub_printf (" TXT.HEAP.BASE: 0x%08" PRIxGRUB_UINT32_T "\n", data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); + grub_printf (" TXT.HEAP.SIZE: %" PRIuGRUB_UINT32_T + " B (0x%" PRIxGRUB_UINT32_T ")\n", data.d32, data.d32); + + /* Only least significant 4 bytes have a meaning. */ + data.d32 = grub_txt_reg_pub_read32 (GRUB_TXT_DPR); + grub_printf (" TXT.DPR: 0x%08" PRIxGRUB_UINT32_T "\n" + " LOCK: %d\n" + " TOP: 0x%08" PRIxGRUB_UINT32_T "\n" + " SIZE: %" PRIuGRUB_UINT32_T " MiB\n", + data.d32, !!(data.d32 & (1 << 0)), (data.d32 & 0xfff00000), + (data.d32 & 0x00000ff0) >> 4); + + grub_printf (" TXT.PUBLIC.KEY:\n"); + + for (i = 0; i < 4; ++i) + { + /* TODO: Check relevant MSRs on SGX platforms. */ + data.d64 = grub_txt_reg_pub_read64 (GRUB_TXT_PUBLIC_KEY + i * sizeof (grub_uint64_t)); + grub_printf (" %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x%s", data.a8[0], data.a8[1], + data.a8[2], data.a8[3], data.a8[4], data.a8[5], data.a8[6], data.a8[7], + (i < 3) ? ":\n" : "\n"); + } +} + +grub_err_t +grub_txt_boot_prepare (struct grub_slaunch_params *slparams) +{ + grub_err_t err; + struct grub_txt_mle_header *mle_header; + struct grub_txt_acm_header *sinit_base; + int i; + + sinit_base = grub_txt_sinit_select (grub_slaunch_module ()); + + if (sinit_base == NULL) + return grub_errno; + + grub_dprintf ("slaunch", "Init TXT heap\n"); + err = init_txt_heap (slparams, sinit_base); + + if (err != GRUB_ERR_NONE) + return err; + + grub_dprintf ("slaunch", "TXT heap successfully prepared\n"); + + /* 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->dce_base = (grub_uint32_t)(grub_addr_t) sinit_base; + slparams->dce_size = sinit_base->size * 4; + + /* + * Access to locality 4 isn't available to software, skip it. Don't bother + * checking TPM status, we have no tools for recovering from bad state better + * than command abort, which is part of locality relinquish. Write performed + * by the following function is no-op if locality is neither active nor + * requested. + */ + for (i = 0; i < 4; ++i) + grub_tpm_relinquish_locality (i); + + grub_dprintf ("slaunch", "TPM localities relinquished\n"); + + err = set_mtrrs_for_acmod (sinit_base); + if (err) + return grub_error (err, N_("secure launch failed to set MTRRs for ACM")); + + grub_dprintf ("slaunch", "MTRRs set for ACMOD\n"); + + err = grub_txt_prepare_cpu (); + if ( err ) + return err; + + grub_dprintf ("slaunch", "CPU prepared for secure launch\n"); + + if (!(grub_rdmsr (GRUB_MSR_X86_APICBASE) & GRUB_MSR_X86_APICBASE_BSP)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("secure launch must run on BSP")); + + return GRUB_ERR_NONE; +} diff --git a/include/grub/i386/slaunch.h b/include/grub/i386/slaunch.h index 7f7709cda..93a491e4f 100644 --- a/include/grub/i386/slaunch.h +++ b/include/grub/i386/slaunch.h @@ -33,7 +33,6 @@ #ifndef ASM_FILE -#include #include struct grub_slaunch_params From f00c4fc282bd38cf54d31d41faa17c28e8f47ecd Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:59:18 -0400 Subject: [PATCH 14/31] i386/txt: Add Intel TXT ACM module support Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Krystian Hebel --- grub-core/loader/i386/txt/acmod.c | 600 ++++++++++++++++++++++++++++++ 1 file changed, 600 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..6a8338abf --- /dev/null +++ b/grub-core/loader/i386/txt/acmod.c @@ -0,0 +1,600 @@ +/* + * acmod.c: support functions for use of Intel(r) TXT Authenticated + * Code (AC) Modules + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Macro that returns value of it->field if it's inside info table, 0 otherwise. + * Fields at or below 'length' ('uuid', 'chipset_acm_type', 'version') don't + * benefit from this macro, because it requires that 'length' (and by extension + * fields below that) is valid. + */ +#define info_table_get(it, field) \ + (__builtin_offsetof(struct grub_txt_acm_info_table, field) + sizeof(it->field)\ + <= it->length ? it->field : 0) + +/* + * Returns hdr + offset if [offset, offset + count * size) is within the bounds + * of the ACM, NULL otherwise. + */ +static void* +n_fit_in_acm (struct grub_txt_acm_header *hdr, grub_uint32_t offset, + grub_uint32_t size, grub_uint32_t count) +{ + grub_uint32_t total_size, elem_end; + /* ACM size overflow was checked in is_acmod() */ + grub_uint32_t acm_len = hdr->size * 4; + + /* + * `offset` will often come from `info_table_get`, and this is the most + * convenient place to check for the macro returning zero. This is fine, since + * there is no legitimate reason to access the zero offset in this manner. + */ + if ( offset == 0 ) + return NULL; + + if ( grub_mul (size, count, &total_size) ) + return NULL; + + if ( grub_add (offset, total_size, &elem_end) ) + return NULL; + + if ( elem_end > acm_len ) + return NULL; + + /* Not checking if (hdr + elem_end) overflows. We know that (hdr + acm_len) + * doesn't, and that elem_end <= acm_len. For the same reason we don't have to + * check if (hdr + offset) overflows. */ + + return (void *)((unsigned long)hdr + offset); +} + +static void* +fits_in_acm (struct grub_txt_acm_header *hdr, grub_uint32_t offset, + grub_uint32_t size) +{ + return n_fit_in_acm(hdr, offset, size, 1); +} + +/* + * Returns pointer to ACM information table. If the table is located outside of + * ACM or its reported size is too small to cover at least 'length' field, + * NULL is returned instead. + */ +static struct grub_txt_acm_info_table* +get_acmod_info_table (struct grub_txt_acm_header* hdr) +{ + grub_uint32_t user_area_off, info_table_size; + struct grub_txt_acm_info_table *ptr = NULL; + /* Minimum size required to read full size of table */ + info_table_size = __builtin_offsetof (struct grub_txt_acm_info_table, length) + + sizeof(ptr->length); + + /* Overflow? */ + if ( grub_add (hdr->header_len, hdr->scratch_size, &user_area_off) ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length plus scratch size overflows")); + return NULL; + } + + if ( grub_mul (user_area_off, 4, &user_area_off) ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length and scratch size in bytes overflows")); + return NULL; + } + + ptr = fits_in_acm(hdr, user_area_off, info_table_size); + + if ( ptr != NULL ) + { + if ( info_table_get (ptr, length) < info_table_size ) + return NULL; + + info_table_size = info_table_get (ptr, length); + ptr = fits_in_acm(hdr, user_area_off, info_table_size); + } + + return ptr; +} + +/* + * Function returns pointer to chipset ID list, after checking that + * grub_txt_acm_chipset_id_list and all grub_txt_acm_chipset_id structures are + * within ACM. Otherwise, NULL is returned. + */ +static struct grub_txt_acm_chipset_id_list* +get_acmod_chipset_list (struct grub_txt_acm_header *hdr) +{ + struct grub_txt_acm_info_table *info_table; + grub_uint32_t id_entries_off; + struct grub_txt_acm_chipset_id_list *chipset_id_list; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table (hdr); + if ( info_table == NULL ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table out of bounds")); + return NULL; + } + + chipset_id_list = fits_in_acm(hdr, info_table_get (info_table, chipset_id_list), + sizeof(struct grub_txt_acm_chipset_id_list)); + if ( chipset_id_list == NULL ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset ID list out of bounds")); + return NULL; + } + + /* Overflows were checked by fits_in_acm() */ + id_entries_off = info_table->chipset_id_list + sizeof(*chipset_id_list); + + if ( n_fit_in_acm( hdr, id_entries_off, sizeof(struct grub_txt_acm_chipset_id), + chipset_id_list->count ) == NULL ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset ID entries out of bounds")); + return NULL; + } + + return chipset_id_list; +} + +/* + * Function returns pointer to processor ID list, after checking that + * grub_txt_acm_processor_id_list and all grub_txt_acm_processor_id structures + * are within ACM. Otherwise, NULL is returned. + */ +static struct grub_txt_acm_processor_id_list* +get_acmod_processor_list (struct grub_txt_acm_header* hdr) +{ + struct grub_txt_acm_info_table *info_table; + grub_uint32_t id_entries_off; + struct grub_txt_acm_processor_id_list *proc_id_list; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table out of bounds")); + return NULL; + } + + proc_id_list = fits_in_acm(hdr, info_table_get (info_table, processor_id_list), + sizeof(*proc_id_list)); + if ( proc_id_list == NULL ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor ID list out of bounds")); + return NULL; + } + + /* Overflows were checked by fits_in_acm() */ + id_entries_off = info_table->processor_id_list + sizeof(*proc_id_list); + + if ( n_fit_in_acm ( hdr, id_entries_off, sizeof(*proc_id_list), + proc_id_list->count ) == NULL ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor ID entries out of bounds")); + return NULL; + } + + return proc_id_list; +} + +static int +is_acmod (const void *acmod_base, grub_uint32_t acmod_size, + grub_uint8_t *type_out) +{ + struct grub_txt_acm_header *acm_hdr = (struct grub_txt_acm_header *)acmod_base; + struct grub_txt_acm_info_table *info_table; + grub_uint32_t size_from_hdr; + + /* First check size */ + if ( acmod_size < sizeof (*acm_hdr) ) + return 0; + + /* Then check overflow */ + if ( grub_mul (acm_hdr->size, 4, &size_from_hdr) ) + return 0; + + /* Then check size equivalency */ + if ( acmod_size != size_from_hdr ) + return 0; + + /* Then check type, sub-type and vendor */ + if ( (acm_hdr->module_type != GRUB_TXT_ACM_MODULE_TYPE) || + (acm_hdr->module_sub_type != GRUB_TXT_ACM_MODULE_SUB_TYPE_TXT_ACM) || + (acm_hdr->module_vendor != GRUB_TXT_ACM_MODULE_VENDOR_INTEL) ) + return 0; + + info_table = get_acmod_info_table (acm_hdr); + if ( info_table == NULL ) + return 0; + + /* Check if ACM UUID is present */ + if ( grub_memcmp (&(info_table->uuid), GRUB_TXT_ACM_UUID, 16) ) + return 0; + + /* + * TXT specification doesn't give clear mapping of info table size to version, + * so just warn if the size is different than expected but try to use it + * anyway. info_table_get() macro does enough testing to not read outside + * of info table. + */ + if ( info_table->length < sizeof(*info_table) ) + grub_dprintf ("slaunch", "Info table size (%x) smaller than expected (%" + PRIxGRUB_SIZE ")\n", + info_table->length, sizeof(*info_table)); + + if ( type_out ) + *type_out = info_table_get (info_table, chipset_acm_type); + + return 1; +} + +static struct grub_txt_acm_header* +get_bios_sinit (void *sinit_region_base) +{ + grub_uint8_t *txt_heap = grub_txt_get_heap (); + struct grub_txt_bios_data *bios_data = grub_txt_bios_data_start (txt_heap); + struct grub_txt_acm_header *bios_sinit; + grub_uint32_t tmp; + + if ( sinit_region_base == NULL ) + return NULL; + + if ( bios_data->bios_sinit_size == 0 ) + return NULL; + + /* Check if ACM crosses 4G */ + if ( grub_add ( (unsigned long)sinit_region_base, bios_data->bios_sinit_size, + &tmp) ) + return NULL; + + /* BIOS has loaded an SINIT module, so verify that it is valid */ + grub_dprintf ("slaunch", "BIOS has already loaded an SINIT module\n"); + + bios_sinit = (struct grub_txt_acm_header *)sinit_region_base; + + /* Is it a valid SINIT module? */ + if ( !grub_txt_is_sinit_acmod (sinit_region_base, bios_data->bios_sinit_size) || + !grub_txt_acmod_match_platform (bios_sinit) ) + { + grub_dprintf("slaunch", "BIOS SINIT module did not pass reasonableness checks"); + return NULL; + } + + return bios_sinit; +} + +grub_uint32_t +grub_txt_supported_os_sinit_data_ver (struct grub_txt_acm_header* hdr) +{ + static struct grub_txt_acm_info_table *info_table; + + /* Assumes that it passed grub_txt_is_sinit_acmod() */ + info_table = get_acmod_info_table (hdr); + + if ( info_table == NULL ) + return 0; + + return info_table_get (info_table, os_sinit_data_ver); +} + +grub_uint32_t +grub_txt_get_sinit_capabilities (struct grub_txt_acm_header* hdr) +{ + static struct grub_txt_acm_info_table *info_table; + + /* Assumes that it passed grub_txt_is_sinit_acmod() */ + info_table = get_acmod_info_table (hdr); + + if ( info_table == NULL || info_table->version < 3 ) + return 0; + + return info_table_get (info_table, capabilities); +} + +int +grub_txt_is_sinit_acmod (const void *acmod_base, grub_uint32_t acmod_size) +{ + grub_uint8_t type; + + if ( !is_acmod (acmod_base, acmod_size, &type) ) + return 0; + + if ( type != GRUB_TXT_ACM_CHIPSET_TYPE_SINIT ) + return 0; + + return 1; +} + +static int +didvid_matches(union grub_txt_didvid didvid, + struct grub_txt_acm_chipset_id *chipset_id) +{ + if ( didvid.vid != chipset_id->vendor_id ) + return 0; + + if ( didvid.did != chipset_id->device_id ) + return 0; + + /* If RevisionIdMask is 0, the RevisionId field must exactly match the + * TXT.DIDVID.RID field. */ + if ( (chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) == 0 && + ( didvid.rid == chipset_id->revision_id ) ) + return 1; + + /* If RevisionIdMask is 1, the RevisionId field is a bitwise mask that can be + * used to test for any bits set in the TXT.DIDVID.RID field. If any bits are + * set, the RevisionId is a match. */ + if ( (chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) != 0 && + ( didvid.rid & chipset_id->revision_id ) != 0 ) + return 1; + + return 0; +} + +int +grub_txt_acmod_match_platform (struct grub_txt_acm_header *hdr) +{ + union grub_txt_didvid didvid; + grub_uint32_t fms, ign, i, ver; + grub_uint64_t platform_id; + struct grub_txt_acm_chipset_id_list *chipset_id_list; + struct grub_txt_acm_chipset_id *chipset_id; + struct grub_txt_acm_processor_id_list *proc_id_list; + struct grub_txt_acm_processor_id *proc_id; + struct grub_txt_acm_info_table *info_table; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + info_table = get_acmod_info_table (hdr); + if ( info_table == NULL ) + return 0; + + /* Get chipset fusing, device, and vendor id info */ + didvid.value = grub_txt_reg_pub_read64 (GRUB_TXT_DIDVID); + + ver = grub_txt_reg_pub_read32 (GRUB_TXT_VER_QPIIF); + if ( ver == 0xffffffff || ver == 0x00 ) /* Old CPU, need to use VER.FSBIF */ + ver = grub_txt_reg_pub_read32 (GRUB_TXT_VER_FSBIF); + + grub_dprintf ("slaunch", "chipset production fused: %s, " + "chipset vendor: 0x%x, device: 0x%x, revision: 0x%x\n", + (ver & GRUB_TXT_VERSION_PROD_FUSED) ? "yes" : "no" , didvid.vid, + didvid.did, didvid.rid); + + grub_cpuid (1, fms, ign, ign, ign); + platform_id = grub_rdmsr (GRUB_MSR_X86_PLATFORM_ID); + + grub_dprintf ("slaunch", "processor family/model/stepping: 0x%x, " + "platform id: 0x%" PRIxGRUB_UINT64_T "\n", fms, platform_id); + + /* + * Check if chipset fusing is same. Note the DEBUG.FUSE bit in the version + * is 0 when debug fused so the logic below checking a mismatch is valid. + */ + if ( !!(ver & GRUB_TXT_VERSION_PROD_FUSED) == + !!(hdr->flags & GRUB_TXT_ACM_FLAG_DEBUG_SIGNED) ) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("production/debug mismatch between chipset and ACM")); + return 0; + } + + /* Check if chipset vendor/device/revision IDs match */ + chipset_id_list = get_acmod_chipset_list (hdr); + if ( chipset_id_list == NULL ) + return 0; + + grub_dprintf ("slaunch", "%d SINIT ACM chipset id entries:\n", chipset_id_list->count); + + chipset_id = (struct grub_txt_acm_chipset_id *) ((grub_addr_t)chipset_id_list + sizeof (chipset_id_list->count)); + for (i = 0; i < chipset_id_list->count; i++, chipset_id++) + { + grub_dprintf ("slaunch", " vendor: 0x%x, device: 0x%x, flags: 0x%x, " + "revision: 0x%x, extended: 0x%x\n", chipset_id->vendor_id, + chipset_id->device_id, chipset_id->flags, + chipset_id->revision_id, chipset_id->extended_id); + + if ( didvid_matches ( didvid, chipset_id) ) + break; + } + + if ( i >= chipset_id_list->count ) + { + /* + * Version 9 introduces flexible ACM information table format, not yet + * supported by this code. + * + * TXT spec says that 9 will be the final version and further changes will + * be reflected elsewhere, but check for higher values too in case they + * change their mind. + */ + if ( info_table->version >= 9 ) + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("chipset id mismatch, flexible ACM info list may contain" + " matching entry but it isn't yet supported by code")); + else + grub_error (GRUB_ERR_BAD_DEVICE, N_("chipset id mismatch")); + + return 0; + } + + /* + * Unfortunately the spec isn't too clear on what the changes to the info + * table were, across the different versions, but an old version of the entire + * spec document shows that the processor table field didn't exist when the + * latest version of the info table was 3. + */ + if ( info_table->version < 4 ) + return 1; + + /* Check if processor family/model/stepping and platform IDs match */ + proc_id_list = get_acmod_processor_list(hdr); + if ( proc_id_list == NULL ) + return 0; + + grub_dprintf ("slaunch", "%d SINIT ACM processor id entries:\n", proc_id_list->count); + + proc_id = (struct grub_txt_acm_processor_id *) ((grub_addr_t)proc_id_list + sizeof (proc_id_list->count)); + for (i = 0; i < proc_id_list->count; i++, proc_id++) + { + grub_dprintf ("slaunch", " fms: 0x%x, fms_mask: 0x%x, platform_id: 0x%" PRIxGRUB_UINT64_T + ", platform_mask: 0x%" PRIxGRUB_UINT64_T "\n", proc_id->fms, proc_id->fms_mask, + proc_id->platform_id, proc_id->platform_mask); + + if ( (proc_id->fms == (fms & proc_id->fms_mask)) && + (proc_id->platform_id == (platform_id & proc_id->platform_mask)) ) + break; + } + + if ( i >= proc_id_list->count ) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("processor id mismatch")); + return 0; + } + + return 1; +} + +/* + * Choose between the BIOS-provided and user-provided SINIT ACMs, and copy the + * chosen module to the SINIT memory. + */ +struct grub_txt_acm_header * +grub_txt_sinit_select (struct grub_txt_acm_header *sinit) +{ + struct grub_txt_acm_header *bios_sinit; + void *sinit_region_base; + grub_uint32_t sinit_size, sinit_region_size; + + sinit_region_base = (void *)(grub_addr_t) grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_BASE); + sinit_region_size = (grub_uint32_t) grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_SIZE); + + grub_dprintf ("slaunch", "TXT.SINIT.BASE: %p\nTXT.SINIT.SIZE: 0x%" + PRIxGRUB_UINT32_T "\n", sinit_region_base, sinit_region_size); + + if (sinit_region_base == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("no SINIT ACM final resting place")); + return NULL; + } + + if ( ((grub_addr_t) sinit_region_base & ((1 << GRUB_PAGE_SHIFT) - 1)) != 0 ) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM base not properly aligned")); + return NULL; + } + + if (sinit != NULL) + grub_dprintf ("slaunch", "SINIT ACM date: %" PRIxGRUB_UINT32_T "\n", sinit->date); + + bios_sinit = get_bios_sinit (sinit_region_base); + + /* Does BIOS provide SINIT ACM? */ + if (bios_sinit != NULL) + { + grub_dprintf ("slaunch", "BIOS SINIT ACM date: %" PRIxGRUB_UINT32_T "\n", + bios_sinit->date); + + if (sinit == NULL) + { + grub_dprintf ("slaunch", "no SINIT ACM provided. Using BIOS SINIT ACM\n"); + return bios_sinit; + } + + if (bios_sinit->date >= sinit->date) + { + grub_dprintf ("slaunch", "BIOS provides newer or same SINIT ACM, so, using BIOS one\n"); + return bios_sinit; + } + + grub_dprintf ("slaunch", "BIOS provides older SINIT ACM, so, ignoring BIOS one\n"); + } + + /* Fail if there is no SINIT ACM. */ + if (sinit == NULL) + return NULL; + + /* Our SINIT ACM is newer than BIOS one or BIOS does not have one. */ + + if (grub_mul (sinit->size, 4, &sinit_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM size in bytes overflows")); + return NULL; + } + + if (sinit_size > sinit_region_size) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("SINIT ACM does not fit into final resting place: 0x%" + PRIxGRUB_UINT32_T "\n"), sinit_size); + return NULL; + } + + grub_memcpy (sinit_region_base, sinit, sinit_size); + + return sinit_region_base; +} From fdf96621e79c0b0a02046f07fd28b149889a52ab Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 14:46:17 -0400 Subject: [PATCH 15/31] i386/txt: Add Intel TXT verification routines Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Krystian Hebel --- grub-core/loader/i386/txt/verify.c | 278 +++++++++++++++++++++++++++++ 1 file changed, 278 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..5205c931c --- /dev/null +++ b/grub-core/loader/i386/txt/verify.c @@ -0,0 +1,278 @@ +/* + * verify.c: verify that platform and processor supports Intel(r) TXT + * + * Copyright (c) 2003-2010, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Current max that the secure launch can handle */ +#define TXT_MAX_CPUS 512 + +static grub_err_t +verify_bios_spec_ver_elt (struct grub_txt_heap_ext_data_element *elt) +{ + if ( elt->size != GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof(struct grub_txt_heap_bios_spec_ver_element) ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_BIOS_SPEC_VER element has wrong size (%d)"), + elt->size); + + /* Any values are allowed */ + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_acm_elt (struct grub_txt_heap_ext_data_element *elt) +{ + grub_uint64_t acm_addrs_size; + grub_uint32_t i; + + if ( elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof(struct grub_txt_heap_acm_element) || + grub_mul (elt->acm.num_acms, sizeof(grub_uint64_t), &acm_addrs_size) || + elt->size - (GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + sizeof(elt->acm)) != + acm_addrs_size ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element has wrong size (%d)"), + elt->size); + + /* No addrs is not error, but print warning. */ + if ( elt->acm.num_acms == 0 ) + grub_printf ("WARNING: HEAP_ACM element has no ACM addrs\n"); + + for ( i = 0; i < elt->acm.num_acms; i++ ) + { + if ( elt->acm.addr[i] == 0 ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element ACM addr (%d) is NULL"), i); + + if ( elt->acm.addr[i] >= 0x100000000UL ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element ACM addr (%d) is >4GB"), i); + + /* Not going to check if ACM addrs are valid ACMs */ + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_custom_elt (struct grub_txt_heap_ext_data_element *elt) +{ + if ( elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof(struct grub_txt_heap_custom_element) ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_CUSTOM element has wrong size (%d)"), + elt->size); + + /* Any values are allowed */ + + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_ext_data_elts(struct grub_txt_heap_ext_data_element *elts, + grub_uint64_t elts_size) +{ + struct grub_txt_heap_ext_data_element *elt = elts; + grub_err_t err; + + if ( elts_size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("TXT heap ext data elements too small")); + + for ( ; ; ) + { + if ( elts_size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE || + elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE || + elts_size < elt->size ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("TXT heap invalid element size: type: %d, size: %d"), + elt->type, elt->size); + + switch ( elt->type ) + { + case GRUB_TXT_HEAP_EXTDATA_TYPE_END: + return GRUB_ERR_NONE; + case GRUB_TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER: + err = verify_bios_spec_ver_elt (elt); + if ( err ) + 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; + /* These shouldn't be present in BIOS data, treat them as errors */ + case GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR: + case GRUB_TXT_HEAP_EXTDATA_TYPE_MADT: + case GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1: + case GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG: + default: + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("unknown element: type: %u, size: %u\n"), + elt->type, elt->size); + } + + elts_size -= elt->size; + elt = (struct grub_txt_heap_ext_data_element *)((grub_uint8_t *)elt + elt->size); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_txt_verify_platform (void) +{ + grub_uint8_t *txt_heap; + grub_uint32_t eax, ebx, ecx, edx, errorcode, heap_base, heap_size; + grub_uint64_t bios_size, msr; + grub_err_t err = GRUB_ERR_NONE; + struct grub_txt_bios_data *bios_data; + struct grub_txt_heap_ext_data_element *elts; + + grub_cpuid (GRUB_X86_CPUID_FEATURES, eax, ebx, ecx, edx); + + if (!(ecx & GRUB_X86_CPUID_FEATURES_ECX_SMX)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPU does not support SMX")); + + msr = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if ((msr & (GRUB_MSR_X86_SENTER_FUNCTIONS | GRUB_MSR_X86_SENTER_ENABLE)) != + (GRUB_MSR_X86_SENTER_FUNCTIONS | GRUB_MSR_X86_SENTER_ENABLE)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[SENTER] is not enabled")); + + /* + * TODO + * TXT Specification + * 4.5 SGX Requirement for TXT Platform + * Secure Launch currently does not support interop with SGX since it does + * not have TPM support to write the SE NVRAM index. + * Eventually need the verify_IA32_se_svn_status routine to be called here. + */ + + errorcode = grub_txt_reg_pub_read32 (GRUB_TXT_ERRORCODE); + /* 0 - no previous SENTER, 0xC0000001 - previous SENTER succeeded */ + if ( errorcode != 0 && errorcode != 0xC0000001 ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT_ERRORCODE reports failure: 0x%08" PRIxGRUB_UINT32_T), + errorcode); + + if (grub_txt_reg_pub_read8 (GRUB_TXT_ESTS) & GRUB_TXT_ESTS_TXT_RESET) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT_RESET.STS is set and GETSEC[SENTER] is disabled")); + + /* + * Verify that the BIOS information in the TXT heap that was setup by the + * BIOS ACM is reasonable. + */ + + txt_heap = grub_txt_get_heap (); + heap_base = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); + heap_size = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); + + if ( txt_heap == NULL || heap_base == 0 || heap_size == 0 ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT heap is not configured correctly")); + + bios_size = grub_txt_bios_data_size (txt_heap); + if ( bios_size < sizeof (grub_uint64_t) + sizeof (*bios_data) || + bios_size > heap_size ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("invalid size of the TXT heap BIOS data table")); + + bios_data = grub_txt_bios_data_start (txt_heap); + + /* Check version */ + if ( bios_data->version < 3 ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("unsupported BIOS data version (%d)"), bios_data->version); + + if ( bios_data->num_logical_procs > TXT_MAX_CPUS ) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("BIOS reports too many CPUs for secure launch (%d)"), + bios_data->num_logical_procs); + + /* + * grub_uint32_t mle_flags is supposed to be added in version 5, however, the + * only ACM in 630744_003 package that is version 4 (Sandy Bridge & Ivy Bridge + * SNB_IVB_SINIT_20190708_PW.bin) seems to also have this field, or at least + * a placeholder for it. + */ + if ( bios_data->version >= 4 && bios_size > sizeof(*bios_data) + sizeof(bios_size) ) + { + elts = (struct grub_txt_heap_ext_data_element *) ((grub_uint8_t *)bios_data + + sizeof(*bios_data)); + err = verify_ext_data_elts(elts, bios_size - sizeof(*bios_data)); + } + + return err; +} From b1adb3a15b9ddaa6a7f428aad77dfcf1758d7866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 31 Aug 2022 14:37:49 +0200 Subject: [PATCH 16/31] i386/txt: Initialize TPM 1.2 event log in TXT heap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski Signed-off-by: Krystian Hebel --- grub-core/loader/i386/txt/txt.c | 62 +++++++++++++++++++++++++++++++-- include/grub/i386/txt.h | 31 +++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/i386/txt/txt.c b/grub-core/loader/i386/txt/txt.c index b92107d50..44a0b5c01 100644 --- a/grub-core/loader/i386/txt/txt.c +++ b/grub-core/loader/i386/txt/txt.c @@ -519,6 +519,32 @@ set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) return err; } +void +grub_txt_init_tpm_event_log (void *buf, grub_size_t size) +{ + struct event_log_container *elog; + + if (buf == NULL || size == 0) + return; + + /* For TPM 2.0 just clear the area, only TPM 1.2 requires initialization. */ + grub_memset (buf, 0, size); + + if (grub_get_tpm_ver () != GRUB_TPM_12) + return; + + elog = (struct event_log_container *) buf; + + grub_memcpy((void *)elog->signature, EVTLOG_SIGNATURE, sizeof(elog->signature)); + elog->container_ver_major = EVTLOG_CNTNR_MAJOR_VER; + elog->container_ver_minor = EVTLOG_CNTNR_MINOR_VER; + elog->pcr_event_ver_major = EVTLOG_EVT_MAJOR_VER; + elog->pcr_event_ver_minor = EVTLOG_EVT_MINOR_VER; + elog->size = size; + elog->pcr_events_offset = sizeof(*elog); + elog->next_event_offset = sizeof(*elog); +} + /* Adds new element to the end. `size` does not include common header. */ /* Assume that heap was cleared and there is enough space to add the element. */ static inline struct grub_txt_heap_ext_data_element * @@ -657,6 +683,8 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header sinit_caps = grub_txt_get_sinit_capabilities (sinit); + grub_dprintf ("slaunch", "SINIT capabilities %08x\n", sinit_caps); + /* * In the latest TXT Software Development Guide as of now (April 2023, * Revision 017.4) bits 4 and 5 (used to be "no legacy PCR usage" and @@ -671,6 +699,25 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header */ os_sinit_data->capabilities = GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE; + if (grub_get_tpm_ver () == GRUB_TPM_20) + { + if ((sinit_caps & os_sinit_data->capabilities) != os_sinit_data->capabilities) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Details/authorities PCR usage is not supported")); + } + else + { + if (!(sinit_caps & GRUB_TXT_CAPS_TPM_12_AUTH_PCR_USAGE)) + { + grub_dprintf ("slaunch", "Details/authorities PCR usage is not supported. Trying legacy"); + if (sinit_caps & GRUB_TXT_CAPS_TPM_12_NO_LEGACY_PCR_USAGE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Not a single PCR usage available in SINIT capabilities")); + + os_sinit_data->capabilities = 0; + } + } + /* * APs (application processors) can't be brought up by usual INIT-SIPI-SIPI * sequence after Measured Launch, otherwise the MLE integrity is lost. @@ -687,10 +734,21 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header os_sinit_data->capabilities |= GRUB_TXT_CAPS_ECX_PT_SUPPORT; if (grub_get_tpm_ver () == GRUB_TPM_12) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("TPM 1.2 detected, but not implemented yet")); + { + grub_dprintf ("slaunch", "TPM 1.2 detected\n"); + grub_dprintf ("slaunch", "Setting up TXT HEAP TPM event log element\n"); + os_sinit_data->flags = GRUB_TXT_PCR_EXT_MAX_PERF_POLICY; + os_sinit_data->version = OS_SINIT_DATA_TPM_12_VER; + + elt = add_ext_data_elt(os_sinit_data, + GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR, + sizeof (struct grub_txt_heap_tpm_event_log_element)); + elt->tpm_event_log.event_log_phys_addr = slparams->tpm_evt_log_base; + } else { + grub_dprintf ("slaunch", "TPM 2.0 detected\n"); + grub_dprintf ("slaunch", "Setting up TXT HEAP TPM event log element\n"); if (!(sinit_caps & GRUB_TXT_CAPS_TPM_20_EVTLOG_SUPPORT)) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("original TXT TPM 2.0 event log format is not supported")); diff --git a/include/grub/i386/txt.h b/include/grub/i386/txt.h index a7ddd3a66..155ebab0f 100644 --- a/include/grub/i386/txt.h +++ b/include/grub/i386/txt.h @@ -22,6 +22,7 @@ #define GRUB_TXT_H 1 #include +#include #include #include #include @@ -679,6 +680,34 @@ struct grub_smx_parameters grub_uint32_t txt_feature_ext_flags; } GRUB_PACKED; +/* Structures and constants used for TPM 1.2 event log initialization */ +struct tpm12_pcr_event { + grub_uint32_t pcr_index; + grub_uint32_t type; + grub_uint8_t digest[SHA1_DIGEST_SIZE]; + grub_uint32_t data_size; + grub_uint8_t data[]; +} GRUB_PACKED; + +#define EVTLOG_SIGNATURE "TXT Event Container" +#define EVTLOG_CNTNR_MAJOR_VER 1 +#define EVTLOG_CNTNR_MINOR_VER 0 +#define EVTLOG_EVT_MAJOR_VER 1 +#define EVTLOG_EVT_MINOR_VER 0 + +struct event_log_container { + grub_uint8_t signature[20]; + grub_uint8_t reserved[12]; + grub_uint8_t container_ver_major; + grub_uint8_t container_ver_minor; + grub_uint8_t pcr_event_ver_major; + grub_uint8_t pcr_event_ver_minor; + grub_uint32_t size; + grub_uint32_t pcr_events_offset; + grub_uint32_t next_event_offset; + struct tpm12_pcr_event pcr_events[]; +} GRUB_PACKED; + extern grub_uint32_t grub_txt_supported_os_sinit_data_ver (struct grub_txt_acm_header* hdr); extern grub_uint32_t grub_txt_get_sinit_capabilities (struct grub_txt_acm_header* hdr); @@ -692,6 +721,8 @@ extern struct grub_txt_acm_header* grub_txt_sinit_select (struct grub_txt_acm_he extern grub_err_t grub_txt_verify_platform (void); extern grub_err_t grub_txt_prepare_cpu (void); +extern void grub_txt_init_tpm_event_log(void *buf, grub_size_t size); + extern grub_uint32_t grub_txt_get_mle_ptab_size (grub_uint32_t mle_size); extern void grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams); From e2fcca0f201428e452988263981af44c6713ab3e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Fri, 13 Jan 2023 15:11:26 -0500 Subject: [PATCH 17/31] i386/txt: switch to using Secure Launch Resource Table interface Signed-off-by: Ross Philipson Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/txt/txt.c | 109 ++++++++++++++++++++++++-------- include/grub/i386/slaunch.h | 30 ++++++++- include/grub/i386/txt.h | 26 +++----- 3 files changed, 121 insertions(+), 44 deletions(-) diff --git a/grub-core/loader/i386/txt/txt.c b/grub-core/loader/i386/txt/txt.c index 44a0b5c01..cc4db716d 100644 --- a/grub-core/loader/i386/txt/txt.c +++ b/grub-core/loader/i386/txt/txt.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,8 @@ #define OS_SINIT_DATA_MIN_VER OS_SINIT_DATA_TPM_12_VER +static struct grub_slr_entry_intel_info slr_intel_info_staging = {0}; + static grub_err_t enable_smx_mode (void) { @@ -249,33 +252,39 @@ grub_txt_prepare_cpu (void) } static void -save_mtrrs (struct grub_txt_os_mle_data *os_mle_data) +save_mtrrs (struct grub_slr_txt_mtrr_state *saved_bsp_mtrrs) { grub_uint64_t i; - os_mle_data->saved_bsp_mtrrs.default_mem_type = + saved_bsp_mtrrs->default_mem_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); - os_mle_data->saved_bsp_mtrrs.mtrr_vcnt = + 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) + if (saved_bsp_mtrrs->mtrr_vcnt > GRUB_TXT_VARIABLE_MTRRS_LENGTH) { /* Print warning but continue saving what we can... */ grub_printf ("WARNING: Actual number of variable MTRRs (%" PRIuGRUB_UINT64_T ") > GRUB_SL_MAX_VARIABLE_MTRRS (%d)\n", - 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; + saved_bsp_mtrrs->mtrr_vcnt, + GRUB_TXT_VARIABLE_MTRRS_LENGTH); + saved_bsp_mtrrs->mtrr_vcnt = GRUB_TXT_VARIABLE_MTRRS_LENGTH; } - for (i = 0; i < os_mle_data->saved_bsp_mtrrs.mtrr_vcnt; ++i) + for (i = 0; i < saved_bsp_mtrrs->mtrr_vcnt; ++i) { - os_mle_data->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physmask = + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (i)); - os_mle_data->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physbase = + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (i)); } + /* Zero unused array items. */ + for ( ; i < GRUB_TXT_VARIABLE_MTRRS_LENGTH; ++i) + { + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = 0; + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = 0; + } } static void @@ -545,6 +554,20 @@ grub_txt_init_tpm_event_log (void *buf, grub_size_t size) elog->next_event_offset = sizeof(*elog); } +static void +setup_txt_slrt_entry (struct grub_slaunch_params *slparams, + struct grub_txt_os_mle_data *os_mle_data) +{ + struct grub_slr_table *slr_table = slparams->slr_table_mem; + struct grub_slr_entry_hdr *txt_info; + + grub_slr_add_entry (slr_table, &slr_intel_info_staging.hdr); + + txt_info = grub_slr_next_entry_by_tag (slr_table, NULL, GRUB_SLR_ENTRY_INTEL_INFO); + os_mle_data->txt_info = (grub_addr_t) slparams->slr_table_base + + ((grub_addr_t) txt_info - (grub_addr_t) slparams->slr_table_mem); +} + /* Adds new element to the end. `size` does not include common header. */ /* Assume that heap was cleared and there is enough space to add the element. */ static inline struct grub_txt_heap_ext_data_element * @@ -590,6 +613,20 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t); + if (slparams->slr_table_base == GRUB_SLAUNCH_STORE_IN_OS2MLE) + { + /* SLRT needs to be at least 4-byte aligned per specification. */ + slparams->slr_table_base = + ALIGN_UP ((grub_addr_t) os_mle_data + sizeof (*os_mle_data), 4); + + /* Recompute size including SLRT table in it. */ + *size = (slparams->slr_table_base + slparams->slr_table_size) + - ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + + /* Size of heap sections should be a multiple of 8. */ + *size = ALIGN_UP (*size, 8); + } + if (grub_add (grub_txt_bios_data_size (txt_heap), *size, &size_total) || (size_total > grub_txt_get_heap_size ())) { @@ -602,16 +639,20 @@ init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION; os_mle_data->boot_params_addr = slparams->boot_params_addr; - os_mle_data->saved_misc_enable_msr = grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE); + os_mle_data->slrt = slparams->slr_table_base; os_mle_data->ap_wake_block = slparams->ap_wake_block; os_mle_data->ap_wake_block_size = slparams->ap_wake_block_size; - os_mle_data->evtlog_addr = slparams->tpm_evt_log_base; - os_mle_data->evtlog_size = slparams->tpm_evt_log_size; + /* Setup the TXT specific SLR information */ + slr_intel_info_staging.hdr.tag = GRUB_SLR_ENTRY_INTEL_INFO; + slr_intel_info_staging.hdr.size = sizeof(struct grub_slr_entry_intel_info); + slr_intel_info_staging.saved_misc_enable_msr = + grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE); + /* Save the BSPs MTRR state so post launch can restore it. */ grub_dprintf ("slaunch", "Saving MTRRs to OS MLE data\n"); - save_mtrrs (os_mle_data); + save_mtrrs (&slr_intel_info_staging.saved_bsp_mtrrs); /* OS/loader to SINIT data. */ grub_dprintf ("slaunch", "Get supported OS SINIT data version\n"); @@ -999,9 +1040,10 @@ grub_err_t grub_txt_boot_prepare (struct grub_slaunch_params *slparams) { grub_err_t err; + grub_uint8_t *txt_heap; + struct grub_txt_os_mle_data *os_mle_data; struct grub_txt_mle_header *mle_header; struct grub_txt_acm_header *sinit_base; - int i; sinit_base = grub_txt_sinit_select (grub_slaunch_module ()); @@ -1017,24 +1059,22 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) grub_dprintf ("slaunch", "TXT heap successfully prepared\n"); /* Update the MLE header. */ - mle_header = (struct grub_txt_mle_header *)(grub_addr_t) (slparams->mle_start + slparams->mle_header_offset); + mle_header = + (struct grub_txt_mle_header *) ((grub_uint8_t *) slparams->mle_mem + slparams->mle_header_offset); mle_header->first_valid_page = 0; mle_header->mle_end = slparams->mle_size; slparams->dce_base = (grub_uint32_t)(grub_addr_t) sinit_base; slparams->dce_size = sinit_base->size * 4; - /* - * Access to locality 4 isn't available to software, skip it. Don't bother - * checking TPM status, we have no tools for recovering from bad state better - * than command abort, which is part of locality relinquish. Write performed - * by the following function is no-op if locality is neither active nor - * requested. - */ - for (i = 0; i < 4; ++i) - grub_tpm_relinquish_locality (i); + /* Setup of SLR table. */ + grub_slaunch_init_slrt_storage (GRUB_SLR_INTEL_TXT); + txt_heap = grub_txt_get_heap (); + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + setup_txt_slrt_entry (slparams, os_mle_data); - grub_dprintf ("slaunch", "TPM localities relinquished\n"); + grub_tpm_relinquish_locality (0); + grub_dprintf ("slaunch", "Relinquished TPM locality 0\n"); err = set_mtrrs_for_acmod (sinit_base); if (err) @@ -1053,3 +1093,20 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) return GRUB_ERR_NONE; } + +void +grub_txt_add_slrt_policy_entries (void) +{ + struct grub_txt_os_mle_data *os_mle_data; + grub_uint8_t *txt_heap; + + txt_heap = grub_txt_get_heap (); + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_TXT_OS2MLE, + /*flags=*/0, + (grub_addr_t) os_mle_data, + sizeof(*os_mle_data), + "Measured TXT OS-MLE data"); +} diff --git a/include/grub/i386/slaunch.h b/include/grub/i386/slaunch.h index 93a491e4f..a694260cb 100644 --- a/include/grub/i386/slaunch.h +++ b/include/grub/i386/slaunch.h @@ -31,6 +31,19 @@ #define GRUB_SLAUNCH_TPM_EVT_LOG_SIZE (8 * GRUB_PAGE_SIZE) +/* + * Special value for slr_table_base of struct grub_slaunch_params that indicates + * that the table should be stored near OS2MLE data (right after it). + * + * In this case: + * 1. Platform-specific code (e.g., TXT-code) is responsible for setting + * slr_table_base to its final value + * 2. SLRT should be copied from slr_table_mem to slr_table_base after invoking + * grub_slaunch_finish_slr_table () by the code which used this special + * value. + */ +#define GRUB_SLAUNCH_STORE_IN_OS2MLE ((grub_uint64_t) 0xFFFFFFFFFFFFFFFF) + #ifndef ASM_FILE #include @@ -38,11 +51,16 @@ struct grub_slaunch_params { grub_uint32_t boot_params_addr; + grub_uint64_t slr_table_base; + /* This is size of SLRT buffer, so maximum size of the table. */ + grub_uint32_t slr_table_size; + void *slr_table_mem; grub_uint32_t mle_start; grub_uint32_t mle_size; - void *mle_ptab_mem; + void *mle_mem; grub_uint64_t mle_ptab_target; grub_uint32_t mle_ptab_size; + void *mle_ptab_mem; grub_uint32_t mle_header_offset; grub_uint32_t ap_wake_block; grub_uint32_t ap_wake_block_size; @@ -56,6 +74,16 @@ extern grub_uint32_t grub_slaunch_platform_type (void); extern void *grub_slaunch_module (void); extern struct grub_slaunch_params *grub_slaunch_params (void); +extern void grub_slaunch_init_slrt_storage (int arch); +extern void grub_slaunch_add_slrt_policy_entries (void); +extern void grub_slaunch_add_slrt_policy_entry (grub_uint16_t pcr, + grub_uint16_t entity_type, + grub_uint16_t flags, + grub_uint64_t entity, + grub_uint64_t size, + const char *evt_info); +extern void grub_slaunch_finish_slr_table (void); + #endif /* ASM_FILE */ #endif /* GRUB_I386_SLAUNCH_H */ diff --git a/include/grub/i386/txt.h b/include/grub/i386/txt.h index 155ebab0f..c3dde57ea 100644 --- a/include/grub/i386/txt.h +++ b/include/grub/i386/txt.h @@ -353,29 +353,14 @@ struct grub_txt_bios_data /* GRUB SLAUNCH specific definitions OS-MLE data */ #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 boot_params_addr; - grub_uint64_t saved_misc_enable_msr; - struct grub_slaunch_mtrr_state saved_bsp_mtrrs; + grub_uint32_t slrt; + grub_uint32_t txt_info; grub_uint32_t ap_wake_block; grub_uint32_t ap_wake_block_size; - grub_uint64_t evtlog_addr; - grub_uint32_t evtlog_size; grub_uint8_t mle_scratch[64]; } GRUB_PACKED; @@ -729,6 +714,13 @@ extern void grub_txt_setup_mle_ptab (struct grub_slaunch_params *slparams); extern grub_err_t grub_txt_init (void); extern void grub_txt_shutdown (void); extern void grub_txt_state_show (void); +/* + * This function doesn't finish building of SLRT. It's caller's responsibility + * to call grub_slaunch_finish_slr_table() after making any necessary + * grub_slr_add_entry() and grub_slaunch_add_slrt_policy_entry() calls including + * grub_txt_add_slrt_polic_entries() and grub_slaunch_add_slrt_policy_entries(). + */ extern grub_err_t grub_txt_boot_prepare (struct grub_slaunch_params *slparams); +extern void grub_txt_add_slrt_policy_entries (void); #endif From 1ee2f715f28859fb3cfa437468155c18ff44c2de Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 15:01:00 -0400 Subject: [PATCH 18/31] i386/slaunch: Add secure launch framework and commands Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Krystian Hebel --- grub-core/Makefile.am | 6 + grub-core/Makefile.core.def | 15 + grub-core/lib/i386/relocator32.S | 8 + grub-core/loader/i386/bsd.c | 4 + grub-core/loader/i386/coreboot/chainloader.c | 2 + grub-core/loader/i386/linux.c | 320 ++++++++++++++++++- grub-core/loader/i386/pc/plan9.c | 3 +- grub-core/loader/i386/slaunch.c | 304 ++++++++++++++++++ grub-core/loader/i386/xnu.c | 3 + grub-core/loader/multiboot.c | 5 + include/grub/file.h | 3 + include/grub/i386/linux.h | 14 +- 12 files changed, 670 insertions(+), 17 deletions(-) create mode 100644 grub-core/loader/i386/slaunch.c diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index ee88e44e9..44ba9ac40 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -103,6 +103,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h endif if COND_i386_xen_pvh @@ -122,6 +124,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h @@ -183,6 +187,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index f482cc01e..7ebacaa3c 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1816,6 +1816,15 @@ module = { enable = noemu; }; +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; @@ -2489,6 +2498,12 @@ module = { common = commands/testspeed.c; }; +module = { + name = tpm; + x86 = commands/i386/tpm.c; + enable = x86; +}; + module = { name = tpm_verifier; common = commands/tpm_verifier.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 + .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 5f3290ce1..d9832328d 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -792,6 +793,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; @@ -907,6 +909,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; @@ -1229,6 +1232,7 @@ grub_netbsd_boot (void) return err; #endif + state.edi = SLP_NONE; state.eip = entry; state.esp = stack_target; state.ebp = stack_target; diff --git a/grub-core/loader/i386/coreboot/chainloader.c b/grub-core/loader/i386/coreboot/chainloader.c index 0a19ebb9c..dccd86b07 100644 --- a/grub-core/loader/i386/coreboot/chainloader.c +++ b/grub-core/loader/i386/coreboot/chainloader.c @@ -33,6 +33,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -47,6 +48,7 @@ grub_chain_boot (void) grub_video_set_mode ("text", 0, 0); state.eip = entry; + state.edi = SLP_NONE; return grub_relocator32_boot (relocator, state, 0); } diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 9f74a96b1..bf3878ab9 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -34,9 +34,12 @@ #include #include #include +#include +#include #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -63,18 +66,36 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define ACCEPTS_PURE_TEXT 1 #endif +/* See kernel_info in Documentation/arch/x86/boot.rst in the kernel tree */ +#define KERNEL_INFO_HEADER "LToP" +#define KERNEL_INFO_MIN_SIZE_TOTAL 12 + +struct linux_params_efi_info +{ + grub_uint32_t efi_signature; + grub_uint32_t efi_system_table; + grub_uint32_t efi_mem_desc_size; + grub_uint32_t efi_mem_desc_version; + grub_uint32_t efi_mmap; + grub_uint32_t efi_mmap_size; + grub_uint32_t efi_system_table_hi; + grub_uint32_t efi_mmap_hi; +}; + static grub_dl_t my_mod; static grub_size_t linux_mem_size; static int loaded; static void *prot_mode_mem; static grub_addr_t prot_mode_target; +static grub_size_t prot_file_size; static void *initrd_mem; static grub_addr_t initrd_mem_target; static grub_size_t prot_init_space; static struct grub_relocator *relocator = NULL; static void *efi_mmap_buf; static grub_size_t maximal_cmdline_size; +static struct linux_kernel_info *linux_info; static struct linux_kernel_params linux_params; static char *linux_cmdline; #ifdef GRUB_MACHINE_EFI @@ -98,6 +119,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) { @@ -150,11 +173,37 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, grub_uint64_t preferred_address) { grub_err_t err; + grub_size_t total_size; + struct grub_slaunch_params *slparams = grub_slaunch_params(); if (prot_size == 0) prot_size = 1; - prot_size = page_align (prot_size); + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + if (prot_size > GRUB_TXT_MLE_MAX_SIZE) + { + err = GRUB_ERR_OUT_OF_RANGE; + goto fail; + } + + /* Check performed above makes sure that this doesn't overflow. */ + prot_size = ALIGN_UP (prot_size, GRUB_TXT_PMR_ALIGN); + + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (prot_size); + slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); + /* Do not go below GRUB_TXT_PMR_ALIGN. */ + preferred_address = (preferred_address > slparams->mle_ptab_size) ? + (preferred_address - slparams->mle_ptab_size) : GRUB_TXT_PMR_ALIGN; + preferred_address = ALIGN_UP (preferred_address, GRUB_TXT_PMR_ALIGN); + } + else + { + prot_size = page_align (prot_size); + slparams->mle_ptab_size = 0; + } + + total_size = prot_size + slparams->mle_ptab_size; /* Initialize the memory pointers with NULL for convenience. */ free_pages (); @@ -176,7 +225,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)--) @@ -194,11 +243,65 @@ 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) + { + /* Zero out memory to get stable MLE measurements. */ + grub_memset (prot_mode_mem, 0, total_size); + + slparams->mle_ptab_mem = prot_mode_mem; + slparams->mle_ptab_target = prot_mode_target; + + prot_mode_mem = (char *)prot_mode_mem + slparams->mle_ptab_size; + prot_mode_target += slparams->mle_ptab_size; + + slparams->mle_start = prot_mode_target; + slparams->mle_size = prot_size; + slparams->mle_mem = prot_mode_mem; + + grub_dprintf ("linux", "mle_ptab_mem = %p, mle_ptab_target = %lx, mle_ptab_size = %x\n", + slparams->mle_ptab_mem, (unsigned long) slparams->mle_ptab_target, + (unsigned) slparams->mle_ptab_size); + + err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_PAGE_SIZE, + GRUB_PAGE_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + goto fail; + + slparams->slr_table_base = get_physical_target_address (ch); + slparams->slr_table_size = GRUB_PAGE_SIZE; + slparams->slr_table_mem = get_virtual_current_address (ch); + + grub_memset (slparams->slr_table_mem, 0, slparams->slr_table_size); + + grub_dprintf ("linux", "slr_table_base = %lx, slr_table_size = %x\n", + (unsigned long) slparams->slr_table_base, + (unsigned) slparams->slr_table_size); + + err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, + GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + goto fail; + + slparams->tpm_evt_log_base = get_physical_target_address (ch); + slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; + + grub_txt_init_tpm_event_log (get_virtual_current_address (ch), + slparams->tpm_evt_log_size); + + grub_dprintf ("linux", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", + (unsigned long) slparams->tpm_evt_log_base, + (unsigned) slparams->tpm_evt_log_size); + } } grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n", @@ -286,7 +389,7 @@ grub_linux_setup_video (struct linux_kernel_params *params) params->lfb_size >>= 16; params->have_vga = GRUB_VIDEO_LINUX_TYPE_VESA; break; - + case GRUB_VIDEO_DRIVER_EFI_UGA: case GRUB_VIDEO_DRIVER_EFI_GOP: params->have_vga = GRUB_VIDEO_LINUX_TYPE_EFIFB; @@ -398,6 +501,63 @@ grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size, return 0; } +static void +grub_linux_setup_slr_table (struct grub_slaunch_params *slparams) +{ + struct linux_kernel_params *boot_params = (void *) (grub_addr_t) slparams->boot_params_addr; + struct linux_params_efi_info *efi_info; + + /* A bit of work to extract the v2.08 EFI info from the linux params */ + efi_info = (void *)((grub_uint8_t *)&boot_params->v0208 + 2*sizeof(grub_uint32_t)); + + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_BOOT_PARAMS, + /*flags=*/0, + (grub_addr_t) boot_params, + GRUB_PAGE_SIZE, + "Measured boot parameters"); + + if (boot_params->setup_data) + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_SETUP_DATA, + GRUB_SLR_POLICY_IMPLICIT_SIZE, + boot_params->setup_data, + /*size=*/0, + "Measured Kernel setup_data"); + + /* The cmdline ptr can have hi bits but GRUB puts it always < 4G */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_CMDLINE, + /*flags=*/0, + boot_params->cmd_line_ptr, + boot_params->cmdline_size, + "Measured Kernel command line"); + + if (!grub_memcmp(&efi_info->efi_signature, "EL64", sizeof(grub_uint32_t))) + { + grub_uint64_t mmap_addr = + ((grub_uint64_t) efi_info->efi_mmap_hi << 32) | efi_info->efi_mmap; + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_UEFI_MEMMAP, + /*flags=*/0, + mmap_addr, + efi_info->efi_mmap_size, + "Measured EFI memory map"); + } + + if (boot_params->ramdisk_image) + /* + * The initrd image and size can have hi bits but in GRUB it is always + * < 4G, see GRUB_LINUX_INITRD_MAX_ADDRESS in grub_cmd_initrd(). + */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_CODE_PCR, + GRUB_SLR_ET_RAMDISK, + /*flags=*/0, + boot_params->ramdisk_image, + boot_params->ramdisk_size, + "Measured Kernel initrd"); +} + static grub_err_t grub_linux_boot (void) { @@ -411,6 +571,8 @@ grub_linux_boot (void) }; grub_size_t mmap_size; grub_size_t cl_offset; + grub_size_t ap_wake_block_size = 0; + struct grub_slaunch_params *slparams = grub_slaunch_params(); #ifdef GRUB_MACHINE_IEEE1275 { @@ -543,6 +705,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"); @@ -550,7 +715,8 @@ grub_linux_boot (void) grub_relocator_chunk_t ch; grub_size_t sz; - if (grub_add (ctx.real_size, efi_mmap_size, &sz)) + if (grub_add (efi_mmap_size, ap_wake_block_size, &sz) || + grub_add (ctx.real_size, sz, &sz)) return GRUB_ERR_OUT_OF_RANGE; err = grub_relocator_alloc_chunk_addr (relocator, &ch, @@ -561,6 +727,20 @@ grub_linux_boot (void) } 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; + slparams->ap_wake_block_size = ap_wake_block_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); + + /* Grab the real mode target address, this is the boot params page. */ + slparams->boot_params_addr = ctx.real_mode_target; + } + grub_dprintf ("linux", "real_mode_mem = %p\n", real_mode_mem); @@ -587,13 +767,15 @@ grub_linux_boot (void) ctx.params->secure_boot = grub_efi_get_secureboot (); + grub_dprintf ("linux", "EFI exit boot services\n"); + err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL, &efi_desc_size, &efi_desc_version); if (err) return err; - + /* Note that no boot services are available from here. */ - efi_mmap_target = ctx.real_mode_target + efi_mmap_target = ctx.real_mode_target + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem); /* Pass EFI parameters. */ if (grub_le_to_cpu16 (ctx.params->version) >= 0x0208) @@ -624,12 +806,36 @@ 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) + { + err = grub_txt_boot_prepare (slparams); + + if (err != GRUB_ERR_NONE) + return err; + + grub_slaunch_add_slrt_policy_entries (); + grub_txt_add_slrt_policy_entries (); + grub_linux_setup_slr_table (slparams); + grub_slaunch_finish_slr_table (); + + /* Configure relocator GETSEC[SENTER] call. */ + state.eax = GRUB_SMX_LEAF_SENTER; + state.ebx = slparams->dce_base; + state.ecx = slparams->dce_size; + state.edx = 0; + } + else + { + /* 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); } @@ -650,12 +856,13 @@ 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; int relocatable; grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR; + struct grub_slaunch_params *slparams = grub_slaunch_params(); grub_dl_ref (my_mod); @@ -743,7 +950,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), align = 0; relocatable = 0; } - + if (grub_le_to_cpu16 (lh.version) >= 0x020a) { min_align = lh.min_alignment; @@ -760,6 +967,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)) @@ -767,6 +981,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 (grub_slaunch_params ()); + /* * The Linux 32-bit boot protocol defines the setup header end * to be at 0x202 + the byte value at 0x201. @@ -793,6 +1010,79 @@ 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_off_t) grub_le_to_cpu32 (lh.kernel_info_offset) + + real_size + GRUB_DISK_SECTOR_SIZE) == ((grub_off_t) -1)) + goto fail; + + linux_info = grub_malloc (KERNEL_INFO_MIN_SIZE_TOTAL); + + if (!linux_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate memory for kernel_info")); + goto fail; + } + + /* Load minimal kernel_info struct. */ + if (grub_file_read (file, linux_info, + KERNEL_INFO_MIN_SIZE_TOTAL) != KERNEL_INFO_MIN_SIZE_TOTAL) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + if (grub_memcmp (&linux_info->header, KERNEL_INFO_HEADER, sizeof (linux_info->header))) + { + grub_error (GRUB_ERR_BAD_OS, N_("incorrect kernel_info header")); + goto fail; + } + + if (linux_info->size_total < KERNEL_INFO_MIN_SIZE_TOTAL) + { + grub_error (GRUB_ERR_BAD_OS, N_("incorrect kernel_info size")); + goto fail; + } + + linux_info = grub_realloc (linux_info, linux_info->size_total); + + if (!linux_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot reallocate memory for kernel_info")); + goto fail; + } + + /* Load the rest of kernel_info struct. */ + if (grub_file_read (file, &linux_info->setup_type_max, + linux_info->size_total - KERNEL_INFO_MIN_SIZE_TOTAL) != + (grub_ssize_t)(linux_info->size_total - KERNEL_INFO_MIN_SIZE_TOTAL)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + if (grub_slaunch_platform_type () != SLP_NONE) + { + if (OFFSET_OF (mle_header_offset, linux_info) >= + grub_le_to_cpu32 (linux_info->size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: lack of mle_header_offset")); + goto fail; + } + + slparams->mle_header_offset = grub_le_to_cpu32 (linux_info->mle_header_offset); + } + } + else if (grub_slaunch_platform_type () != SLP_NONE) + { + grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: boot protocol too old")); + goto fail; + } + linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; linux_params.kernel_alignment = (1 << align); linux_params.ps_mouse = linux_params.padding11 = 0; diff --git a/grub-core/loader/i386/pc/plan9.c b/grub-core/loader/i386/pc/plan9.c index 37550155d..cd8213a05 100644 --- a/grub-core/loader/i386/pc/plan9.c +++ b/grub-core/loader/i386/pc/plan9.c @@ -34,6 +34,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -84,7 +85,7 @@ grub_plan9_boot (void) .ebx = 0, .ecx = 0, .edx = 0, - .edi = 0, + .edi = SLP_NONE, .esp = 0, .ebp = 0, .esi = 0 diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c new file mode 100644 index 000000000..b15194c78 --- /dev/null +++ b/grub-core/loader/i386/slaunch.c @@ -0,0 +1,304 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint32_t slp = SLP_NONE; + +static void *slaunch_module = NULL; + +static struct grub_slaunch_params slparams; + +/* Area to collect and build SLR Table information. */ +static struct grub_slr_entry_dl_info slr_dl_info_staging; +static struct grub_slr_entry_log_info slr_log_info_staging; +static grub_uint8_t slr_policy_buf[GRUB_PAGE_SIZE]; +static struct grub_slr_entry_policy *slr_policy_staging = + (struct grub_slr_entry_policy *)slr_policy_buf; + +grub_uint32_t +grub_slaunch_platform_type (void) +{ + return slp; +} + +void * +grub_slaunch_module (void) +{ + return slaunch_module; +} + +struct grub_slaunch_params * +grub_slaunch_params (void) +{ + return &slparams; +} + +void +grub_slaunch_init_slrt_storage (int arch) +{ + struct grub_txt_mle_header *mle_header = + (void *) ((grub_uint8_t *) slparams.mle_mem + slparams.mle_header_offset); + + /* Setup the generic bits of the SLRT. */ + grub_slr_init_table(slparams.slr_table_mem, arch, slparams.slr_table_size); + + /* Setup DCE and DLME information. */ + slr_dl_info_staging.hdr.tag = GRUB_SLR_ENTRY_DL_INFO; + slr_dl_info_staging.hdr.size = sizeof(struct grub_slr_entry_dl_info); + slr_dl_info_staging.dce_base = slparams.dce_base; + slr_dl_info_staging.dce_size = slparams.dce_size; + slr_dl_info_staging.dlme_entry = mle_header->entry_point; + + slr_log_info_staging.hdr.tag = GRUB_SLR_ENTRY_LOG_INFO; + slr_log_info_staging.hdr.size = sizeof(struct grub_slr_entry_log_info); + slr_log_info_staging.addr = slparams.tpm_evt_log_base; + slr_log_info_staging.size = slparams.tpm_evt_log_size; + slr_log_info_staging.format = + (grub_get_tpm_ver () == GRUB_TPM_20) ? + GRUB_SLR_DRTM_TPM20_LOG : GRUB_SLR_DRTM_TPM12_LOG; + + slr_policy_staging->hdr.tag = GRUB_SLR_ENTRY_DRTM_POLICY; + slr_policy_staging->hdr.size = sizeof(struct grub_slr_entry_policy); + slr_policy_staging->revision = GRUB_SLR_TABLE_REVISION; + slr_policy_staging->nr_entries = 0; +} + +void grub_slaunch_add_slrt_policy_entries (void) +{ + /* The SLR table should be measured too, at least parts of it. */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_SLRT, + GRUB_SLR_POLICY_IMPLICIT_SIZE, + slparams.slr_table_base, + /*size=*/0, + "Measured SLR Table"); +} + +void +grub_slaunch_add_slrt_policy_entry (grub_uint16_t pcr, + grub_uint16_t entity_type, + grub_uint16_t flags, + grub_uint64_t entity, + grub_uint64_t size, + const char *evt_info) +{ + struct grub_slr_policy_entry *entry = + (void *)((grub_uint8_t *)slr_policy_staging + + sizeof(struct grub_slr_entry_policy) + + slr_policy_staging->nr_entries*sizeof(*entry)); + + if (slr_policy_staging->hdr.size > sizeof(slr_policy_buf) - sizeof(*entry)) + grub_fatal("Not enough space for adding policy entry: %s! The buffer is full.", + evt_info); + + entry->pcr = pcr; + entry->entity_type = entity_type; + entry->flags = flags; + entry->entity = entity; + entry->size = size; + + grub_strncpy(entry->evt_info, evt_info, sizeof(entry->evt_info) - 1); + entry->evt_info[sizeof(entry->evt_info) - 1] = '\0'; + + slr_policy_staging->hdr.size += sizeof(*entry); + ++slr_policy_staging->nr_entries; +} + +void +grub_slaunch_finish_slr_table (void) +{ + struct grub_slr_table *slr_table = slparams.slr_table_mem; + + grub_slr_add_entry (slr_table, &slr_dl_info_staging.hdr); + grub_slr_add_entry (slr_table, &slr_log_info_staging.hdr); + grub_slr_add_entry (slr_table, &slr_policy_staging->hdr); +} + +static grub_err_t +grub_cmd_slaunch (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_uint32_t manufacturer[3]; + grub_uint32_t eax; + 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 != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected: filename")); + + if (slp == SLP_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("secure launch not enabled")); + + if (slp != SLP_INTEL_TXT) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unknown secure launch platform type: %d"), slp); + + grub_errno = GRUB_ERR_NONE; + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_SLAUNCH_MODULE); + + if (file == NULL) + return grub_errno; + + size = grub_file_size (file); + + if (!size) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("file size is zero")); + goto fail; + } + + 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 (); + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Unknown secure launcher platform type: %d\n"), slp); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_slaunch, cmd_slaunch_module, cmd_slaunch_state; + +GRUB_MOD_INIT (slaunch) +{ + cmd_slaunch = grub_register_command ("slaunch", grub_cmd_slaunch, + NULL, N_("Enable secure launcher")); + cmd_slaunch_module = grub_register_command ("slaunch_module", grub_cmd_slaunch_module, + NULL, N_("Load secure launcher module from file")); + cmd_slaunch_state = grub_register_command ("slaunch_state", grub_cmd_slaunch_state, + NULL, N_("Display secure launcher state")); +} + +GRUB_MOD_FINI (slaunch) +{ + if (cmd_slaunch_state) + grub_unregister_command (cmd_slaunch_state); + + if (cmd_slaunch_module) + grub_unregister_command (cmd_slaunch_module); + + if (cmd_slaunch) + grub_unregister_command (cmd_slaunch); + + if (slp == SLP_INTEL_TXT) + grub_txt_shutdown (); +} diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c index a70093607..4989227bd 100644 --- a/grub-core/loader/i386/xnu.c +++ b/grub-core/loader/i386/xnu.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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 facb13f3d..3ab0c828c 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -50,6 +50,9 @@ #include #include #include +#if defined (__i386__) || defined (__x86_64__) +#include +#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 eddf9251d..18c860e6b 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -148,6 +148,17 @@ struct linux_i386_kernel_header grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; + grub_uint32_t kernel_info_offset; +} GRUB_PACKED; + +struct linux_kernel_info +{ + grub_uint32_t header; + grub_uint32_t size; /* In bytes, excluding var_len_data[] */ + grub_uint32_t size_total; /* In bytes, including var_len_data[] */ + grub_uint32_t setup_type_max; + grub_uint32_t mle_header_offset; + grub_uint8_t var_len_data[]; } GRUB_PACKED; /* Boot parameters for Linux based on 2.6.12. This is used by the setup @@ -325,9 +336,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; From e360ef611f3315530349d6791fba11c0ba1c5fad Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Wed, 22 Nov 2023 21:08:35 +0200 Subject: [PATCH 19/31] multiboot: make GRUB_MULTIBOOT(make_mbi) return MBI's size GRUB_MULTIBOOT(get_mbi_size) doesn't look like an accurate source of the final size, more like a minimal memory buffer size. Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/multiboot_mbi.c | 4 +++- grub-core/loader/multiboot.c | 3 ++- grub-core/loader/multiboot_mbi2.c | 6 ++++-- include/grub/multiboot.h | 2 +- include/grub/multiboot2.h | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index a67d9d0a8..29dd9cbb7 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -450,7 +450,7 @@ retrieve_video_parameters (struct multiboot_info *mbi, } grub_err_t -grub_multiboot_make_mbi (grub_uint32_t *target) +grub_multiboot_make_mbi (grub_uint32_t *target, grub_uint32_t *size) { struct multiboot_info *mbi; struct multiboot_mod_list *modlist; @@ -618,6 +618,8 @@ grub_multiboot_make_mbi (grub_uint32_t *target) return err; #endif + *size = (char *) ptrorig - (char *) mbi; + return GRUB_ERR_NONE; } diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 3ab0c828c..07173f60e 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -180,6 +180,7 @@ static grub_err_t grub_multiboot_boot (void) { grub_err_t err; + grub_uint32_t mbi_size; #ifdef GRUB_USE_MULTIBOOT2 struct grub_relocator32_state state = MULTIBOOT2_INITIAL_STATE; @@ -188,7 +189,7 @@ grub_multiboot_boot (void) #endif state.MULTIBOOT_ENTRY_REGISTER = GRUB_MULTIBOOT (payload_eip); - err = GRUB_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER); + err = GRUB_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER, &mbi_size); if (err) return err; diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 9a943d7bd..197afd1b1 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -701,7 +701,7 @@ retrieve_video_parameters (grub_properly_aligned_t **ptrorig) } grub_err_t -grub_multiboot2_make_mbi (grub_uint32_t *target) +grub_multiboot2_make_mbi (grub_uint32_t *target, grub_uint32_t *size) { grub_properly_aligned_t *ptrorig; grub_properly_aligned_t *mbistart; @@ -1002,7 +1002,9 @@ grub_multiboot2_make_mbi (grub_uint32_t *target) / sizeof (grub_properly_aligned_t); } - ((grub_uint32_t *) mbistart)[0] = (char *) ptrorig - (char *) mbistart; + *size = (char *) ptrorig - (char *) mbistart; + + ((grub_uint32_t *) mbistart)[0] = *size; ((grub_uint32_t *) mbistart)[1] = 0; return GRUB_ERR_NONE; diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h index bd0a9873e..f9fa74471 100644 --- a/include/grub/multiboot.h +++ b/include/grub/multiboot.h @@ -41,7 +41,7 @@ void grub_multiboot (int argc, char *argv[]); void grub_module (int argc, char *argv[]); void grub_multiboot_set_accepts_video (int val); -grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target); +grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target, grub_uint32_t *size); void grub_multiboot_free_mbi (void); grub_err_t grub_multiboot_init_mbi (int argc, char *argv[]); grub_err_t grub_multiboot_add_module (grub_addr_t start, grub_size_t size, diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h index 502d34ef1..d1e3b3a8b 100644 --- a/include/grub/multiboot2.h +++ b/include/grub/multiboot2.h @@ -33,7 +33,8 @@ void grub_multiboot2 (int argc, char *argv[]); void grub_module2 (int argc, char *argv[]); void grub_multiboot2_set_accepts_video (int val); -grub_err_t grub_multiboot2_make_mbi (grub_uint32_t *target); +grub_err_t grub_multiboot2_make_mbi (grub_uint32_t *target, + grub_uint32_t *size); void grub_multiboot2_free_mbi (void); grub_err_t grub_multiboot2_init_mbi (int argc, char *argv[]); grub_err_t grub_multiboot2_add_module (grub_addr_t start, grub_size_t size, From 594fe4760a33158e7cd500825ee26a0c9aef5f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 1 Sep 2022 17:58:53 +0200 Subject: [PATCH 20/31] multiboot2: Implement TXT slaunch support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code makes sure that MBI entry goes first in DRTM, so the payload can measure it first on launch. SLRT table is allocated on the heap first, size for it is reserved inside TXT heap by TXT code and data is later copied into its final place. Signed-off-by: Sergii Dmytruk Signed-off-by: Michał Żygowski Signed-off-by: Tomasz Żyjewski Signed-off-by: Krystian Hebel --- grub-core/loader/multiboot.c | 23 ++++++- grub-core/loader/multiboot_elfxx.c | 104 ++++++++++++++++++++++++++--- grub-core/loader/multiboot_mbi2.c | 99 +++++++++++++++++++++++++++ include/grub/multiboot2.h | 2 + 4 files changed, 219 insertions(+), 9 deletions(-) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 07173f60e..17a922be2 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -52,6 +52,7 @@ #include #if defined (__i386__) || defined (__x86_64__) #include +#include #endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -164,7 +165,17 @@ 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; + struct grub_slaunch_params *slparams = grub_slaunch_params(); + state.edi = grub_slaunch_platform_type (); + + if (state.edi == SLP_INTEL_TXT) + { + /* Configure relocator GETSEC[SENTER] call. */ + state.eax = GRUB_SMX_LEAF_SENTER; + state.ebx = slparams->dce_base; + state.ecx = slparams->dce_size; + state.edx = 0; + } grub_relocator32_boot (rel, state, 0); } @@ -194,6 +205,16 @@ grub_multiboot_boot (void) if (err) return err; +#ifdef GRUB_USE_MULTIBOOT2 + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + err = grub_multiboot2_prepare_slaunch_txt (state.MULTIBOOT_MBI_REGISTER, + mbi_size); + if (err) + return err; + } +#endif + if (grub_efi_is_finished) normal_boot (GRUB_MULTIBOOT (relocator), state); else diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index f2318e0d1..438cedc2c 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -34,6 +34,8 @@ #error "I'm confused" #endif +#include +#include #include #define CONCAT(a,b) CONCAT_(a, b) @@ -60,6 +62,9 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) grub_uint32_t load_offset = 0, load_size; int i; void *source = NULL; + struct grub_slaunch_params *slparams = grub_slaunch_params(); + grub_size_t total_size; + grub_uint32_t mle_hdr_offset; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 @@ -106,25 +111,87 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) (long) mld->align, mld->preference, load_size, mld->avoid_efi_boot_services); - if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size) + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { +#ifndef GRUB_USE_MULTIBOOT2 + return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); +#else + /* + * We allocate the the binary together with page tables to make one + * contiguous block for MLE. We have to align up to PMR (2MB). + */ + total_size = ALIGN_UP(load_size, GRUB_TXT_PMR_ALIGN); + + slparams->mle_size = total_size; + + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (total_size); + slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); + + /* Do not go below GRUB_TXT_PMR_ALIGN. */ + if (mld->align < GRUB_TXT_PMR_ALIGN) + mld->align = GRUB_TXT_PMR_ALIGN; +#endif + } + else + { + total_size = load_size; + slparams->mle_ptab_size = 0; + } + + if (total_size > mld->max_addr || mld->min_addr > mld->max_addr - total_size) return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size"); err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch, - mld->min_addr, mld->max_addr, - load_size, mld->align ? mld->align : 1, + mld->min_addr, mld->max_addr - total_size, + total_size, mld->align ? mld->align : 1, mld->preference, mld->avoid_efi_boot_services); if (err) - { - grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n"); - return err; - } + { + grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n"); + return err; + } mld->load_base_addr = get_physical_target_address (ch); source = get_virtual_current_address (ch); + grub_memset (get_virtual_current_address (ch), 0, total_size); + grub_dprintf ("multiboot_loader", "load_base_addr=0x%lx, source=0x%lx\n", + (long) mld->load_base_addr, (long) source); + + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { +#ifndef GRUB_USE_MULTIBOOT2 + return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); +#else + slparams->mle_start = mld->load_base_addr; + slparams->mle_mem = source; + + err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch, + GRUB_MEMORY_MACHINE_UPPER_START, + mld->load_base_addr - slparams->mle_ptab_size, + slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + { + grub_dprintf ("multiboot_loader", "Cannot allocate memory for MLE page tables\n"); + return err; + } + + slparams->mle_ptab_mem = get_virtual_current_address (ch); + slparams->mle_ptab_target = (grub_uint64_t) get_physical_target_address (ch); + grub_dprintf ("multiboot_loader", "mle_ptab_mem = %p, mle_ptab_target = %lx, mle_ptab_size = %x\n", + slparams->mle_ptab_mem, (unsigned long) slparams->mle_ptab_target, + (unsigned) slparams->mle_ptab_size); +#endif + } } else - mld->load_base_addr = mld->link_base_addr; + { + mld->load_base_addr = mld->link_base_addr; + /* TODO: support non-relocatable */ + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + return grub_error (GRUB_ERR_BAD_OS, "Non-relocatable ELF not supported with slaunch"); + } grub_dprintf ("multiboot_loader", "relocatable=%d, link_base_addr=0x%x, " "load_base_addr=0x%x\n", mld->relocatable, @@ -180,6 +247,27 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) } } + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + slparams->mle_header_offset = 0xffffffff; + + /* TODO: decide on universal way of conveying location of MLE header */ + for (mle_hdr_offset = 0; mle_hdr_offset < 0x1000; mle_hdr_offset += 16) + { + if ( !grub_memcmp ((void *)((grub_addr_t) source + mle_hdr_offset), GRUB_TXT_MLE_UUID, 16) ) + { + slparams->mle_header_offset = mle_hdr_offset; + break; + } + } + + if (slparams->mle_header_offset == 0xffffffff) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "MLE header not found"); + + grub_dprintf ("slaunch", "slparams->mle_header_offset: 0x%08x\n", + slparams->mle_header_offset); + } + for (i = 0; i < ehdr->e_phnum; i++) if (phdr(i)->p_vaddr <= ehdr->e_entry && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry) diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 197afd1b1..fd93bc0e4 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -36,6 +36,10 @@ #include #include #include +#include +#include +#include +#include #if defined (GRUB_MACHINE_EFI) #include @@ -277,6 +281,9 @@ grub_multiboot2_load (grub_file_t file, const char *filename) if (addr_tag) { + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + return grub_error (GRUB_ERR_BAD_OS, "Slaunch not supported with multiboot addr tag"); + grub_uint64_t load_addr = (addr_tag->load_addr + 1) ? addr_tag->load_addr : (addr_tag->header_addr - ((char *) header - (char *) mld.buffer)); @@ -390,6 +397,34 @@ grub_multiboot2_load (grub_file_t file, const char *filename) err = grub_multiboot2_set_console (GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT, accepted_consoles, 0, 0, 0, console_required); + + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + grub_relocator_chunk_t ch; + struct grub_slaunch_params *slparams = grub_slaunch_params(); + + if (grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch, 0x1000000, + UP_TO_TOP32 (GRUB_SLAUNCH_TPM_EVT_LOG_SIZE), + GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_HIGH, 1)) + { + grub_free (mld.buffer); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not allocate TPM event log area"); + } + + slparams->tpm_evt_log_base = get_physical_target_address (ch); + slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; + + grub_txt_init_tpm_event_log(get_virtual_current_address (ch), + slparams->tpm_evt_log_size); + + grub_dprintf ("multiboot_loader", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", + (unsigned long) slparams->tpm_evt_log_base, + (unsigned) slparams->tpm_evt_log_size); + + grub_txt_setup_mle_ptab (slparams); + } + return err; } @@ -722,7 +757,12 @@ grub_multiboot2_make_mbi (grub_uint32_t *target, grub_uint32_t *size) ptrorig = get_virtual_current_address (ch); #if defined (__i386__) || defined (__x86_64__) + struct grub_slaunch_params *slparams = grub_slaunch_params(); + *target = get_physical_target_address (ch); + /* Save MBI pointer in the TXT heap area */ + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + slparams->boot_params_addr = *target; #elif defined (__mips) *target = get_physical_target_address (ch) | 0x80000000; #else @@ -1128,3 +1168,62 @@ grub_multiboot2_set_bootdev (void) bootdev_set = 1; } + +static void +add_multiboot2_slrt_policy_entries (void) +{ + unsigned i; + struct module *cur; + + for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next) + { + grub_slaunch_add_slrt_policy_entry (17, + GRUB_SLR_ET_MULTIBOOT2_MODULE, + /*flags=*/0, + cur->start, + cur->size, + "Measured MB2 module"); + } +} + +grub_err_t +grub_multiboot2_prepare_slaunch_txt (grub_uint32_t mbi_target, + grub_uint32_t mbi_size) +{ + grub_err_t err; + struct grub_slaunch_params *slparams = grub_slaunch_params (); + + slparams->slr_table_base = GRUB_SLAUNCH_STORE_IN_OS2MLE; + slparams->slr_table_size = GRUB_PAGE_SIZE; + + slparams->slr_table_mem = grub_zalloc (slparams->slr_table_size); + if (slparams->slr_table_mem == NULL) + return GRUB_ERR_OUT_OF_MEMORY; + + err = grub_txt_boot_prepare (slparams); + if (err != GRUB_ERR_NONE) + { + grub_printf ("TXT boot preparation failed"); + return err; + } + + grub_slaunch_add_slrt_policy_entry (18, + GRUB_SLR_ET_MULTIBOOT2_INFO, + /*flags=*/0, + mbi_target, + mbi_size, + "Measured MB2 information"); + grub_slaunch_add_slrt_policy_entries (); + grub_txt_add_slrt_policy_entries (); + add_multiboot2_slrt_policy_entries (); + grub_slaunch_finish_slr_table (); + + grub_dprintf ("multiboot_loader", "slr_table_base = %lx, slr_table_size = %x\n", + (unsigned long) slparams->slr_table_base, + (unsigned) slparams->slr_table_size); + grub_memcpy ((void *)(grub_addr_t) slparams->slr_table_base, + slparams->slr_table_mem, + slparams->slr_table_size); + + return GRUB_ERR_NONE; +} diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h index d1e3b3a8b..0ca577e73 100644 --- a/include/grub/multiboot2.h +++ b/include/grub/multiboot2.h @@ -43,6 +43,8 @@ void grub_multiboot2_set_bootdev (void); void grub_multiboot2_add_elfsyms (grub_size_t num, grub_size_t entsize, unsigned shndx, void *data); +grub_err_t grub_multiboot2_prepare_slaunch_txt (grub_uint32_t mbi_target, + grub_uint32_t mbi_size); grub_uint32_t grub_multiboot2_get_mmap_count (void); grub_err_t grub_multiboot2_set_video_mode (void); From c6bd2eeaee2619f41d0633d6cc553eb98074d517 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 29 Jul 2023 20:35:30 +0300 Subject: [PATCH 21/31] Add GitHub actions for building QubesOS packages Signed-off-by: Sergii Dmytruk --- .github/workflows/build.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..93484b98e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,18 @@ +name: Test build and package QubesOS RPMs + +on: + push: + branches: + - 'intel-txt-aem*' + pull_request_target: + branches: # this is to avoid running the workflow twice in a PR + - 'intel-txt-aem*' + +jobs: + qubes-dom0-package: + uses: TrenchBoot/.github/.github/workflows/qubes-dom0-package.yml@master + with: + base-commit: 'ae94b97be2b81b625d6af6654d3ed79078b50ff6' + patch-start: 1100 + qubes-component: 'grub2' + spec-pattern: '/^Patch1001:/' From 60d94af47529409e21f3a16b80c4779f390744af Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Wed, 4 Oct 2023 14:42:13 +0300 Subject: [PATCH 22/31] .github/workflows/build.yml: run when a tag is pushed Signed-off-by: Sergii Dmytruk --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93484b98e..3f138df1f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,8 @@ on: push: branches: - 'intel-txt-aem*' + tags: + - '*' pull_request_target: branches: # this is to avoid running the workflow twice in a PR - 'intel-txt-aem*' From 8be0756ca809a8e011d3a00f90aba68e548bdf3e Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Wed, 4 Oct 2023 14:53:09 +0300 Subject: [PATCH 23/31] .github/workflows/build.yml: drop pull_request_target This still runs CI twice in a PR. Signed-off-by: Sergii Dmytruk --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f138df1f..73186b7ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,9 +6,6 @@ on: - 'intel-txt-aem*' tags: - '*' - pull_request_target: - branches: # this is to avoid running the workflow twice in a PR - - 'intel-txt-aem*' jobs: qubes-dom0-package: From a4851208092fc235a826a5ddd53af0a7a6d40e86 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Fri, 26 Apr 2024 15:48:05 +0200 Subject: [PATCH 24/31] .github/workflows/build.yml: bump base-commit After https://github.com/QubesOS/qubes-grub2/pull/13 got merged, some of the commits were duplicated, causing build system to fail. Skip those commits and add the rest starting at the next available patch-start number. Signed-off-by: Krystian Hebel --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73186b7ed..0255ecf45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: qubes-dom0-package: uses: TrenchBoot/.github/.github/workflows/qubes-dom0-package.yml@master with: - base-commit: 'ae94b97be2b81b625d6af6654d3ed79078b50ff6' - patch-start: 1100 + base-commit: '594fe4760a33158e7cd500825ee26a0c9aef5f6b' + patch-start: 1120 qubes-component: 'grub2' - spec-pattern: '/^Patch1001:/' + spec-pattern: '/^Patch1119:/' From 61e31700c09f3d28b456f2cd240d51c286192099 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Tue, 10 Nov 2020 13:40:09 +0100 Subject: [PATCH 25/31] i386/skinit: Add AMD SKINIT implementation Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- grub-core/Makefile.core.def | 1 + grub-core/loader/i386/skinit.c | 99 ++++++++++++++++++++++++++++++++++ include/grub/i386/skinit.h | 38 +++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 grub-core/loader/i386/skinit.c create mode 100644 include/grub/i386/skinit.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 7ebacaa3c..087707454 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1822,6 +1822,7 @@ module = { x86 = loader/i386/txt/txt.c; x86 = loader/i386/txt/acmod.c; x86 = loader/i386/txt/verify.c; + x86 = loader/i386/skinit.c; enable = x86; }; diff --git a/grub-core/loader/i386/skinit.c b/grub-core/loader/i386/skinit.c new file mode 100644 index 000000000..6afdffc47 --- /dev/null +++ b/grub-core/loader/i386/skinit.c @@ -0,0 +1,99 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SLRT_SIZE GRUB_PAGE_SIZE + +/* Offset to entry point. */ +#define SLB_ENTRY(slb) ((const grub_uint16_t *) (slb))[0] +/* Amount of data actually measured for DRTM. */ +#define SLB_MEASURED(slb) ((const grub_uint16_t *) (slb))[1] +/* Offset to structure with extra info. */ +#define SLB_INFO(slb) ((const grub_uint16_t *) (slb))[2] +/* Offset to area for passing data to SKL. */ +#define SLB_PARAM(slb) ((const grub_uint16_t *) (slb))[3] + +grub_err_t +grub_skinit_boot_prepare (struct grub_relocator *rel, + struct grub_slaunch_params *slparams) +{ + grub_uint32_t *apic = (grub_uint32_t *)0xfee00300ULL; + const void *slb = grub_slaunch_module (); + grub_relocator_chunk_t ch; + grub_err_t err; + void *dce_mem; + + if (slb == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "SLB module is missing"); + + err = grub_relocator_alloc_chunk_align (rel, &ch, 0x1000000, + 0xffffffff - GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, + GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "cannot alloc memory for TPM event log"); + + 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 ("slaunch", "tpm_evt_log_base = %lx, tpm_evt_log_size = %x\n", + (unsigned long) slparams->tpm_evt_log_base, + (unsigned) slparams->tpm_evt_log_size); + + /* Contrary to the TXT, on AMD we do not have vendor-provided blobs in + * reserved memory, we are using normal RAM */ + err = grub_relocator_alloc_chunk_align (rel, &ch, 0, + 0xffffffff - GRUB_SKINIT_SLB_SIZE, + GRUB_SKINIT_SLB_SIZE, + GRUB_SKINIT_SLB_ALIGN, + GRUB_RELOCATOR_PREFERENCE_LOW, 1); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "cannot alloc memory for SLB"); + + slparams->dce_base = get_physical_target_address (ch); + slparams->dce_size = SLB_MEASURED (slb); + + dce_mem = get_virtual_current_address (ch); + grub_memcpy (dce_mem, slb, slparams->dce_size); + + slparams->slr_table_base = slparams->dce_base + SLB_PARAM (slb); + slparams->slr_table_size = SLRT_SIZE; + slparams->slr_table_mem = (grub_uint8_t *) dce_mem + SLB_PARAM (slb); + grub_memset (slparams->slr_table_mem, 0, SLRT_SIZE); + + grub_slaunch_init_slrt_storage (GRUB_SLR_AMD_SKINIT); + + grub_dprintf ("slaunch", "broadcasting INIT\r\n"); + *apic = 0x000c0500; // INIT, all excluding self + grub_dprintf ("slaunch", "grub_tpm_relinquish_locality\r\n"); + grub_tpm_relinquish_locality (0); + + grub_dprintf ("slaunch", "Invoke SKINIT\r\n"); + + return GRUB_ERR_NONE; +} diff --git a/include/grub/i386/skinit.h b/include/grub/i386/skinit.h new file mode 100644 index 000000000..cfeff2843 --- /dev/null +++ b/include/grub/i386/skinit.h @@ -0,0 +1,38 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Main secure launch definitions header file. + */ + +#ifndef GRUB_I386_SKINIT_H +#define GRUB_I386_SKINIT_H 1 + +// SLB is 64k, 64k-aligned +#define GRUB_SKINIT_SLB_SIZE 0x10000 +#define GRUB_SKINIT_SLB_ALIGN 0x10000 + +#ifndef ASM_FILE + +#include +#include + +grub_err_t grub_skinit_boot_prepare (struct grub_relocator *rel, + struct grub_slaunch_params *slparams); + +#endif /* ASM_FILE */ + +#endif /* GRUB_I386_SKINIT_H */ From c9a17ff9cf2b3b3a8de9ea432410159f866d81bb Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Tue, 10 Nov 2020 13:44:17 +0100 Subject: [PATCH 26/31] i386/slaunch: Add support for AMD SKINIT This updates `struct grub_slr_entry_dl_info`: * by adding `dlme_base` and `dlme_size` which weren't necessary for Intel TXT because the same information is passed via OS2SINIT data * by changing `dlme_entry` from `grub_uint64_t` to `grub_uint32_t` because that should be enough for an offset Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- grub-core/lib/i386/relocator32.S | 6 ++++ grub-core/loader/i386/skinit.c | 58 ++++++++++++++++++++++++++++++++ grub-core/loader/i386/slaunch.c | 35 +++++++++++++++++-- include/grub/i386/skinit.h | 2 ++ include/grub/i386/slaunch.h | 1 + include/grub/slr_table.h | 4 ++- 6 files changed, 103 insertions(+), 3 deletions(-) diff --git a/grub-core/lib/i386/relocator32.S b/grub-core/lib/i386/relocator32.S index a2b377197..2bdc07018 100644 --- a/grub-core/lib/i386/relocator32.S +++ b/grub-core/lib/i386/relocator32.S @@ -115,6 +115,9 @@ VARIABLE(grub_relocator32_edx) cmpl $SLP_INTEL_TXT, %edi je LOCAL(intel_txt) + cmpl $SLP_AMD_SKINIT, %edi + je LOCAL(amd_skinit) + .byte 0xea VARIABLE(grub_relocator32_eip) .long 0 @@ -123,6 +126,9 @@ VARIABLE(grub_relocator32_eip) LOCAL(intel_txt): getsec +LOCAL(amd_skinit): + skinit + /* GDT. Copied from loader/i386/linux.c. */ .p2align 4 LOCAL(gdt): diff --git a/grub-core/loader/i386/skinit.c b/grub-core/loader/i386/skinit.c index 6afdffc47..ef9d6fa93 100644 --- a/grub-core/loader/i386/skinit.c +++ b/grub-core/loader/i386/skinit.c @@ -35,6 +35,64 @@ /* Offset to area for passing data to SKL. */ #define SLB_PARAM(slb) ((const grub_uint16_t *) (slb))[3] +int +grub_skinit_is_slb (const void *slb_base, grub_uint32_t slb_size) +{ + const grub_uint8_t skl_uuid[16] = { + 0x78, 0xf1, 0x26, 0x8e, 0x04, 0x92, 0x11, 0xe9, + 0x83, 0x2a, 0xc8, 0x5b, 0x76, 0xc4, 0xcc, 0x02, + }; + /* We need space after SLB to pass SLRT to it. */ + const grub_ssize_t max_size = GRUB_SKINIT_SLB_SIZE - SLRT_SIZE; + + const grub_uint8_t *uuid; + + if (slb_size > max_size) + { + grub_dprintf ("slaunch", "SLB is too large: %d > %d\n", + slb_size, max_size); + return 0; + } + + if (SLB_MEASURED (slb_base) > slb_size) + { + grub_dprintf ("slaunch", "SLB measured size is too large: %d > %d\n", + SLB_MEASURED (slb_base), slb_size); + return 0; + } + + if (SLB_ENTRY (slb_base) >= SLB_MEASURED (slb_base)) + { + grub_dprintf ("slaunch", "SLB entry is not measured: %d >= %d\n", + SLB_ENTRY (slb_base), SLB_MEASURED (slb_base)); + return 0; + } + + if (SLB_INFO (slb_base) > SLB_MEASURED (slb_base) - sizeof(skl_uuid)) + { + grub_dprintf ("slaunch", "SLB info is not measured: %d > %d\n", + SLB_INFO (slb_base), + SLB_MEASURED (slb_base) - sizeof(skl_uuid)); + return 0; + } + + if (SLB_PARAM (slb_base) > max_size) + { + grub_dprintf ("slaunch", "SLB bootloader data offset is too large: %d > %d\n", + SLB_PARAM (slb_base), max_size); + return 0; + } + + uuid = (const grub_uint8_t *) slb_base + SLB_INFO (slb_base); + if (grub_memcmp (uuid, skl_uuid, sizeof(skl_uuid)) != 0) + { + grub_dprintf ("slaunch", "SLB has unexpected UUID\n"); + return 0; + } + + return 1; +} + grub_err_t grub_skinit_boot_prepare (struct grub_relocator *rel, struct grub_slaunch_params *slparams) diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c index b15194c78..5c3f76941 100644 --- a/grub-core/loader/i386/slaunch.c +++ b/grub-core/loader/i386/slaunch.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -77,8 +78,12 @@ grub_slaunch_init_slrt_storage (int arch) /* Setup DCE and DLME information. */ slr_dl_info_staging.hdr.tag = GRUB_SLR_ENTRY_DL_INFO; slr_dl_info_staging.hdr.size = sizeof(struct grub_slr_entry_dl_info); + slr_dl_info_staging.bl_context.bootloader = GRUB_SLR_BOOTLOADER_GRUB; + slr_dl_info_staging.bl_context.context = slparams.boot_params_addr; slr_dl_info_staging.dce_base = slparams.dce_base; slr_dl_info_staging.dce_size = slparams.dce_size; + slr_dl_info_staging.dlme_base = slparams.mle_start; + slr_dl_info_staging.dlme_size = slparams.mle_size; slr_dl_info_staging.dlme_entry = mle_header->entry_point; slr_log_info_staging.hdr.tag = GRUB_SLR_ENTRY_LOG_INFO; @@ -152,7 +157,8 @@ grub_cmd_slaunch (grub_command_t cmd __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { grub_uint32_t manufacturer[3]; - grub_uint32_t eax; + grub_uint32_t eax, ebx, ecx, edx; + grub_uint64_t msr_value; grub_err_t err; if (!grub_cpu_is_cpuid_supported ()) @@ -174,6 +180,19 @@ grub_cmd_slaunch (grub_command_t cmd __attribute__ ((unused)), slp = SLP_INTEL_TXT; } + else if (!grub_memcmp (manufacturer, "AuthenticAMD", 12)) + { + grub_cpuid (GRUB_AMD_CPUID_FEATURES, eax, ebx, ecx, edx); + if (! (ecx & GRUB_AMD_CPUID_FEATURES_ECX_SVM) ) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPU does not support AMD SVM")); + + /* Check whether SVM feature is disabled in BIOS */ + msr_value = grub_rdmsr (GRUB_MSR_AMD64_VM_CR); + if (msr_value & GRUB_MSR_SVM_VM_CR_SVM_DISABLE) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("BIOS has AMD SVM disabled")); + + slp = SLP_AMD_SKINIT; + } else return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("CPU is unsupported")); @@ -193,7 +212,7 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), if (slp == SLP_NONE) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("secure launch not enabled")); - if (slp != SLP_INTEL_TXT) + if (slp > SLP_AMD_SKINIT) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown secure launch platform type: %d"), slp); @@ -239,6 +258,14 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), goto fail; } } + else if (slp == SLP_AMD_SKINIT) + { + if (!grub_skinit_is_slb (slaunch_module, size)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SLB")); + goto fail; + } + } grub_file_close (file); @@ -269,6 +296,10 @@ grub_cmd_slaunch_state (grub_command_t cmd __attribute__ ((unused)), grub_printf ("Secure launcher: Intel TXT\n"); grub_txt_state_show (); } + else if (slp == SLP_AMD_SKINIT) + { + grub_printf ("Secure launcher: AMD SKINIT\n"); + } else return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown secure launcher platform type: %d\n"), slp); diff --git a/include/grub/i386/skinit.h b/include/grub/i386/skinit.h index cfeff2843..aade96138 100644 --- a/include/grub/i386/skinit.h +++ b/include/grub/i386/skinit.h @@ -30,6 +30,8 @@ #include #include +int grub_skinit_is_slb (const void *slb_base, grub_uint32_t slb_size); + grub_err_t grub_skinit_boot_prepare (struct grub_relocator *rel, struct grub_slaunch_params *slparams); diff --git a/include/grub/i386/slaunch.h b/include/grub/i386/slaunch.h index a694260cb..9b4932756 100644 --- a/include/grub/i386/slaunch.h +++ b/include/grub/i386/slaunch.h @@ -24,6 +24,7 @@ /* Secure launch platform types. */ #define SLP_NONE 0 #define SLP_INTEL_TXT 1 +#define SLP_AMD_SKINIT 2 /* PCRs used by Secure launch. */ #define GRUB_SLAUNCH_CODE_PCR 17 diff --git a/include/grub/slr_table.h b/include/grub/slr_table.h index b6bbb0d54..44eb1273e 100644 --- a/include/grub/slr_table.h +++ b/include/grub/slr_table.h @@ -121,7 +121,9 @@ struct grub_slr_entry_dl_info grub_uint64_t dl_handler; grub_uint64_t dce_base; grub_uint32_t dce_size; - grub_uint64_t dlme_entry; + grub_uint64_t dlme_base; + grub_uint32_t dlme_size; + grub_uint32_t dlme_entry; } GRUB_PACKED; /* From 8dc7c6748b43ae7f4b5c278283a352585ac3fa42 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Wed, 30 Dec 2020 10:15:16 +0100 Subject: [PATCH 27/31] multiboot2: support AMD SKINIT Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- grub-core/loader/multiboot.c | 12 ++++++-- grub-core/loader/multiboot_elfxx.c | 8 +++--- grub-core/loader/multiboot_mbi2.c | 46 ++++++++++++++++++++---------- include/grub/multiboot2.h | 4 +-- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 17a922be2..e8a0fe207 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -51,6 +51,7 @@ #include #include #if defined (__i386__) || defined (__x86_64__) +#include #include #include #endif @@ -176,6 +177,11 @@ normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state) state.ecx = slparams->dce_size; state.edx = 0; } + else if (state.edi == SLP_AMD_SKINIT) + { + state.eax = slparams->dce_base; + state.edi = SLP_AMD_SKINIT; + } grub_relocator32_boot (rel, state, 0); } @@ -206,10 +212,10 @@ grub_multiboot_boot (void) return err; #ifdef GRUB_USE_MULTIBOOT2 - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) { - err = grub_multiboot2_prepare_slaunch_txt (state.MULTIBOOT_MBI_REGISTER, - mbi_size); + err = grub_multiboot2_prepare_slaunch (state.MULTIBOOT_MBI_REGISTER, + mbi_size); if (err) return err; } diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index 438cedc2c..5e2bcb063 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -111,7 +111,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) (long) mld->align, mld->preference, load_size, mld->avoid_efi_boot_services); - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) { #ifndef GRUB_USE_MULTIBOOT2 return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); @@ -158,7 +158,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) grub_dprintf ("multiboot_loader", "load_base_addr=0x%lx, source=0x%lx\n", (long) mld->load_base_addr, (long) source); - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) { #ifndef GRUB_USE_MULTIBOOT2 return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); @@ -189,7 +189,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) { mld->load_base_addr = mld->link_base_addr; /* TODO: support non-relocatable */ - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) return grub_error (GRUB_ERR_BAD_OS, "Non-relocatable ELF not supported with slaunch"); } @@ -247,7 +247,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) } } - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) { slparams->mle_header_offset = 0xffffffff; diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index fd93bc0e4..4e27f4766 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1187,25 +1188,37 @@ add_multiboot2_slrt_policy_entries (void) } grub_err_t -grub_multiboot2_prepare_slaunch_txt (grub_uint32_t mbi_target, - grub_uint32_t mbi_size) +grub_multiboot2_prepare_slaunch (grub_uint32_t mbi_target, + grub_uint32_t mbi_size) { grub_err_t err; struct grub_slaunch_params *slparams = grub_slaunch_params (); + grub_uint32_t slp = grub_slaunch_platform_type (); - slparams->slr_table_base = GRUB_SLAUNCH_STORE_IN_OS2MLE; - slparams->slr_table_size = GRUB_PAGE_SIZE; + slparams->boot_params_addr = mbi_target; - slparams->slr_table_mem = grub_zalloc (slparams->slr_table_size); - if (slparams->slr_table_mem == NULL) - return GRUB_ERR_OUT_OF_MEMORY; + if (slp == SLP_INTEL_TXT) + { + slparams->slr_table_base = GRUB_SLAUNCH_STORE_IN_OS2MLE; + slparams->slr_table_size = GRUB_PAGE_SIZE; + + slparams->slr_table_mem = grub_zalloc (slparams->slr_table_size); + if (slparams->slr_table_mem == NULL) + return GRUB_ERR_OUT_OF_MEMORY; - err = grub_txt_boot_prepare (slparams); - if (err != GRUB_ERR_NONE) + err = grub_txt_boot_prepare (slparams); + if (err != GRUB_ERR_NONE) + return grub_error (err, "TXT boot preparation failed"); + } + else if (slp == SLP_AMD_SKINIT) { - grub_printf ("TXT boot preparation failed"); - return err; + err = grub_skinit_boot_prepare (grub_multiboot2_relocator, slparams); + if (err != GRUB_ERR_NONE) + return grub_error (err, "SKINIT preparations have failed"); } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Unknown secure launcher platform type: %d\n"), slp); grub_slaunch_add_slrt_policy_entry (18, GRUB_SLR_ET_MULTIBOOT2_INFO, @@ -1214,16 +1227,19 @@ grub_multiboot2_prepare_slaunch_txt (grub_uint32_t mbi_target, mbi_size, "Measured MB2 information"); grub_slaunch_add_slrt_policy_entries (); - grub_txt_add_slrt_policy_entries (); + if (slp == SLP_INTEL_TXT) + grub_txt_add_slrt_policy_entries (); add_multiboot2_slrt_policy_entries (); grub_slaunch_finish_slr_table (); grub_dprintf ("multiboot_loader", "slr_table_base = %lx, slr_table_size = %x\n", (unsigned long) slparams->slr_table_base, (unsigned) slparams->slr_table_size); - grub_memcpy ((void *)(grub_addr_t) slparams->slr_table_base, - slparams->slr_table_mem, - slparams->slr_table_size); + + if (slp == SLP_INTEL_TXT) + grub_memcpy ((void *)(grub_addr_t) slparams->slr_table_base, + slparams->slr_table_mem, + slparams->slr_table_size); return GRUB_ERR_NONE; } diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h index 0ca577e73..61a7fc904 100644 --- a/include/grub/multiboot2.h +++ b/include/grub/multiboot2.h @@ -43,8 +43,8 @@ void grub_multiboot2_set_bootdev (void); void grub_multiboot2_add_elfsyms (grub_size_t num, grub_size_t entsize, unsigned shndx, void *data); -grub_err_t grub_multiboot2_prepare_slaunch_txt (grub_uint32_t mbi_target, - grub_uint32_t mbi_size); +grub_err_t grub_multiboot2_prepare_slaunch (grub_uint32_t mbi_target, + grub_uint32_t mbi_size); grub_uint32_t grub_multiboot2_get_mmap_count (void); grub_err_t grub_multiboot2_set_video_mode (void); From b1083c1077b64222f74612b8c32b66f0b2b66c9a Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Wed, 21 Feb 2024 01:10:53 +0200 Subject: [PATCH 28/31] i386/slaunch: improve error recovery of "slaunch_module" command Update `slaunch_module` global only if new module passed all checks. Closes #11. Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/slaunch.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c index 5c3f76941..3dc633f8d 100644 --- a/grub-core/loader/i386/slaunch.c +++ b/grub-core/loader/i386/slaunch.c @@ -205,6 +205,7 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), { grub_file_t file; grub_ssize_t size; + void *new_module = NULL; if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected: filename")); @@ -231,12 +232,12 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), goto fail; } - slaunch_module = grub_malloc (size); + new_module = grub_malloc (size); - if (slaunch_module == NULL) + if (new_module == NULL) goto fail; - if (grub_file_read (file, slaunch_module, size) != size) + if (grub_file_read (file, new_module, size) != size) { if (grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file: %s"), @@ -246,13 +247,13 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), if (slp == SLP_INTEL_TXT) { - if (!grub_txt_is_sinit_acmod (slaunch_module, size)) + if (!grub_txt_is_sinit_acmod (new_module, size)) { grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SINIT ACM")); goto fail; } - if (!grub_txt_acmod_match_platform (slaunch_module)) + if (!grub_txt_acmod_match_platform (new_module)) { grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("SINIT ACM does not match platform")); goto fail; @@ -260,7 +261,7 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), } else if (slp == SLP_AMD_SKINIT) { - if (!grub_skinit_is_slb (slaunch_module, size)) + if (!grub_skinit_is_slb (new_module, size)) { grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SLB")); goto fail; @@ -269,16 +270,17 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), grub_file_close (file); + grub_free (slaunch_module); + slaunch_module = new_module; + return GRUB_ERR_NONE; fail: grub_error_push (); - grub_free (slaunch_module); + grub_free (new_module); grub_file_close (file); - slaunch_module = NULL; - grub_error_pop (); return grub_errno; From 99b6c86e582e7577e0f1e356e4e8740b5af9225f Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Wed, 28 Feb 2024 15:42:42 +0200 Subject: [PATCH 29/31] i386/txt: do not update MLE header It's measured and we better measure the binary as is without changing it on the fly. Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/txt/txt.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/grub-core/loader/i386/txt/txt.c b/grub-core/loader/i386/txt/txt.c index cc4db716d..ec144b5eb 100644 --- a/grub-core/loader/i386/txt/txt.c +++ b/grub-core/loader/i386/txt/txt.c @@ -1042,7 +1042,6 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) grub_err_t err; grub_uint8_t *txt_heap; struct grub_txt_os_mle_data *os_mle_data; - struct grub_txt_mle_header *mle_header; struct grub_txt_acm_header *sinit_base; sinit_base = grub_txt_sinit_select (grub_slaunch_module ()); @@ -1058,12 +1057,6 @@ grub_txt_boot_prepare (struct grub_slaunch_params *slparams) grub_dprintf ("slaunch", "TXT heap successfully prepared\n"); - /* Update the MLE header. */ - mle_header = - (struct grub_txt_mle_header *) ((grub_uint8_t *) slparams->mle_mem + slparams->mle_header_offset); - mle_header->first_valid_page = 0; - mle_header->mle_end = slparams->mle_size; - slparams->dce_base = (grub_uint32_t)(grub_addr_t) sinit_base; slparams->dce_size = sinit_base->size * 4; From f6dfae51de0fb810bd441889c499db0602934db5 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sun, 31 Mar 2024 00:33:26 +0200 Subject: [PATCH 30/31] i386/skinit: don't allocate TPM event log low Do allocation in the same way as it's done for TXT (preferring high addresses), otherwise Xen loads Dom0 kernel over the TPM even log and Linux ends up panicking when it detects conflict with e820 map in Xen-specific code (why wouldn't Xen check it? who knows). Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/skinit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/loader/i386/skinit.c b/grub-core/loader/i386/skinit.c index ef9d6fa93..b588bb26f 100644 --- a/grub-core/loader/i386/skinit.c +++ b/grub-core/loader/i386/skinit.c @@ -106,10 +106,10 @@ grub_skinit_boot_prepare (struct grub_relocator *rel, if (slb == NULL) return grub_error (GRUB_ERR_BAD_ARGUMENT, "SLB module is missing"); - err = grub_relocator_alloc_chunk_align (rel, &ch, 0x1000000, - 0xffffffff - GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, - GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, - GRUB_RELOCATOR_PREFERENCE_NONE, 1); + err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000000, + UP_TO_TOP32(GRUB_SLAUNCH_TPM_EVT_LOG_SIZE), + GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_HIGH, 1); if (err != GRUB_ERR_NONE) return grub_error (err, "cannot alloc memory for TPM event log"); From 85ccfdc700a1640c148909cbbad777ba6d7d124b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Fri, 24 May 2024 00:35:19 +0200 Subject: [PATCH 31/31] grub-core/loader/multiboot_elfxx.c: Fix calculating MLE size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid mismatch between OS SINIT data MLE size and MLE size from MLE header, take the mle_size from MLE header and do not align it to PMR size, which is 2MB. Signed-off-by: Michał Żygowski --- grub-core/loader/multiboot_elfxx.c | 34 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index 5e2bcb063..4889f8fe5 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -63,8 +63,8 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) int i; void *source = NULL; struct grub_slaunch_params *slparams = grub_slaunch_params(); - grub_size_t total_size; grub_uint32_t mle_hdr_offset; + struct grub_txt_mle_header *mle_hdr; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 @@ -106,11 +106,6 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) { load_size = highest_load - mld->link_base_addr; - grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, " - "load_size=0x%x, avoid_efi_boot_services=%d\n", - (long) mld->align, mld->preference, load_size, - mld->avoid_efi_boot_services); - if (grub_slaunch_platform_type () != SLP_NONE) { #ifndef GRUB_USE_MULTIBOOT2 @@ -118,13 +113,9 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) #else /* * We allocate the the binary together with page tables to make one - * contiguous block for MLE. We have to align up to PMR (2MB). + * contiguous block for MLE. */ - total_size = ALIGN_UP(load_size, GRUB_TXT_PMR_ALIGN); - - slparams->mle_size = total_size; - - slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (total_size); + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (load_size); slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); /* Do not go below GRUB_TXT_PMR_ALIGN. */ @@ -134,16 +125,20 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) } else { - total_size = load_size; slparams->mle_ptab_size = 0; } - if (total_size > mld->max_addr || mld->min_addr > mld->max_addr - total_size) + grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, " + "load_size=0x%x, avoid_efi_boot_services=%d\n", + (long) mld->align, mld->preference, load_size, + mld->avoid_efi_boot_services); + + if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size) return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size"); err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch, - mld->min_addr, mld->max_addr - total_size, - total_size, mld->align ? mld->align : 1, + mld->min_addr, mld->max_addr, + load_size, mld->align ? mld->align : 1, mld->preference, mld->avoid_efi_boot_services); if (err) @@ -154,7 +149,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) mld->load_base_addr = get_physical_target_address (ch); source = get_virtual_current_address (ch); - grub_memset (get_virtual_current_address (ch), 0, total_size); + grub_memset (get_virtual_current_address (ch), 0, load_size); grub_dprintf ("multiboot_loader", "load_base_addr=0x%lx, source=0x%lx\n", (long) mld->load_base_addr, (long) source); @@ -254,7 +249,8 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) /* TODO: decide on universal way of conveying location of MLE header */ for (mle_hdr_offset = 0; mle_hdr_offset < 0x1000; mle_hdr_offset += 16) { - if ( !grub_memcmp ((void *)((grub_addr_t) source + mle_hdr_offset), GRUB_TXT_MLE_UUID, 16) ) + mle_hdr = (struct grub_txt_mle_header *)((grub_addr_t)source + mle_hdr_offset); + if ( !grub_memcmp ((void *)mle_hdr->uuid, GRUB_TXT_MLE_UUID, 16) ) { slparams->mle_header_offset = mle_hdr_offset; break; @@ -266,6 +262,8 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) grub_dprintf ("slaunch", "slparams->mle_header_offset: 0x%08x\n", slparams->mle_header_offset); + + slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; } for (i = 0; i < ehdr->e_phnum; i++)