From 6d90da4b3a77982d89836cbf54178261db3c7ab0 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 17 Dec 2019 17:51:29 +0100 Subject: [PATCH 01/24] 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 f73757df8abd9ee1d010807065b1d219d52d36a9 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 17 Dec 2019 20:02:37 +0100 Subject: [PATCH 02/24] 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 0ce866478c3e7c6a990350a217ad6a16abdefdf0 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sat, 25 Apr 2020 16:43:52 +0200 Subject: [PATCH 03/24] 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 a79a24e1f980bb70e114b2db8104e2e4e9650e1e Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 20 Nov 2019 12:40:42 +0100 Subject: [PATCH 04/24] 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 3b856e842..520367732 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, @@ -818,7 +818,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 81b40ff2be3f1c1962a9d32f35285fb897ba7169 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 20 Nov 2019 12:52:16 +0100 Subject: [PATCH 05/24] 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 520367732..dcdf005df 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -92,8 +92,7 @@ static struct xen_loader_state xen_state; static grub_dl_t my_mod; -#define PAGE_SIZE (1UL << 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 - @@ -828,7 +827,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base), (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: grub_initrd_close (&initrd_ctx); @@ -882,7 +881,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), { xen_state.xen_inf.unmapped_initrd = 0; xen_state.n_modules = 0; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); xen_state.modules_target_start = xen_state.max_addr; xen_state.next_start.mod_start = xen_state.max_addr + xen_state.xen_inf.virt_base; @@ -902,7 +901,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), MAX_MODULES * sizeof (xen_state.module_info_page[0]); } - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)); @@ -925,7 +924,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.module_info_page[xen_state.n_modules].cmdline = xen_state.max_addr - xen_state.modules_target_start; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, GRUB_PAGE_SIZE); if (size) { @@ -952,7 +951,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.n_modules++; grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", (unsigned) xen_state.max_addr, (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: diff --git a/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 7f1e10758b1dcfed10a4782add3c4531b4a7fe7d Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Mon, 22 Jan 2024 13:14:39 +0100 Subject: [PATCH 06/24] 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 e0bc92a5c309fa0c4fe221b22a4a9abbb17eb89c Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 4 May 2020 22:34:59 +0200 Subject: [PATCH 07/24] 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 | 83 +++++++++++++++++++++++++++++++++++++++++++ include/grub/memory.h | 3 ++ 2 files changed, 86 insertions(+) diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c index c8c8312c5..80d6c60b8 100644 --- a/grub-core/mmap/mmap.c +++ b/grub-core/mmap/mmap.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -343,6 +344,88 @@ grub_mmap_unregister (int handle) #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */ +typedef struct +{ + grub_uint64_t addr; + grub_uint64_t limit; +} addr_limit_t; + +/* Helper for grub_mmap_get_lowest(). */ +static int +lowest_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + addr_limit_t *al = data; + grub_uint64_t end; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_add (addr, size, &end)) + return 0; + + if (addr >= al->limit) + al->addr = grub_min (al->addr, addr); + + if ((addr < al->limit) && (end > al->limit)) + al->addr = al->limit; + + return 0; +} + +/* + * This function calculates lowest available RAM address that is at or above + * the passed limit. If no RAM exists above the limit, ~0 is returned. + */ +grub_uint64_t +grub_mmap_get_lowest (grub_uint64_t limit) +{ + addr_limit_t al = {~0, limit}; + + grub_mmap_iterate (lowest_hook, &al); + + return al.addr; +} + +/* Helper for grub_mmap_get_highest(). */ +static int +highest_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + addr_limit_t *al = data; + grub_uint64_t end; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_add (addr, size, &end)) + return 0; + + if (end < al->limit) + al->addr = grub_max (al->addr, end); + + if ((addr < al->limit) && (end >= al->limit)) + al->addr = al->limit; + + return 0; +} + +/* + * This function calculates highest available RAM address that is below the + * passed limit. Returned address is either one byte after last byte of RAM or + * equal to limit, whichever is lower. If no RAM exists below limit, 0 is + * returned. + */ +grub_uint64_t +grub_mmap_get_highest (grub_uint64_t limit) +{ + addr_limit_t al = {0, limit}; + + grub_mmap_iterate (highest_hook, &al); + + return al.addr; +} + #define CHUNK_SIZE 0x400 struct badram_entry { diff --git a/include/grub/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 7266492eefa5db7d2c013b9647606694d1603065 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 22 Apr 2020 21:41:24 +0200 Subject: [PATCH 08/24] 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} (97%) diff --git a/docs/grub.texi b/docs/grub.texi index 096a3cde0..99552af66 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6775,10 +6775,10 @@ grub-mkimage -O x86_64-efi -o grubx64.efi -p '(tftp)/grub' --sbat sbat.csv efine @node Measured Boot @section Measuring boot components -If the tpm module is loaded and the platform has a Trusted Platform Module -installed, GRUB will log each command executed and each file loaded into the -TPM event log and extend the PCR values in the TPM correspondingly. All events -will be logged into the PCR described below with a type of EV_IPL and an +If the tpm_verifier module is loaded and the platform has a Trusted Platform +Module installed, GRUB will log each command executed and each file loaded into +the TPM event log and extend the PCR values in the TPM correspondingly. All +events will be logged into the PCR described below with a type of EV_IPL and an event description as described below. @multitable @columnfractions 0.3 0.1 0.6 @@ -6803,9 +6803,10 @@ corresponding to the filename. GRUB will not measure its own @file{core.img} - it is expected that firmware will carry this out. GRUB will also not perform any measurements until the -tpm module is loaded. As such it is recommended that the tpm module be built -into @file{core.img} in order to avoid a potential gap in measurement between -@file{core.img} being loaded and the tpm module being loaded. +tpm_verifier module is loaded. As such it is recommended that the tpm_verifier +module be built into @file{core.img} in order to avoid a potential gap in +measurement between @file{core.img} being loaded and the tpm_verifier module +being loaded. Measured boot is currently only supported on EFI and IBM IEEE1275 PowerPC platforms. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 6ff483534..fc4712cdf 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2620,8 +2620,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 97% rename from grub-core/commands/tpm.c rename to grub-core/commands/tpm_verifier.c index 324423ef8..51419518e 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm_verifier.c @@ -88,13 +88,13 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type) } struct grub_file_verifier grub_tpm_verifier = { - .name = "tpm", + .name = "tpm_verifier", .init = grub_tpm_verify_init, .write = grub_tpm_verify_write, .verify_string = grub_tpm_verify_string, }; -GRUB_MOD_INIT (tpm) +GRUB_MOD_INIT (tpm_verifier) { /* * Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(), @@ -107,7 +107,7 @@ GRUB_MOD_INIT (tpm) grub_verifier_register (&grub_tpm_verifier); } -GRUB_MOD_FINI (tpm) +GRUB_MOD_FINI (tpm_verifier) { if (!grub_tpm_present()) return; From e0f005e7250173e705745badd63a74ec81b11e16 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 4 May 2020 22:30:58 +0200 Subject: [PATCH 09/24] 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 | 151 ++++++++++++++++++++++++++++++++++ include/grub/i386/tpm.h | 35 ++++++++ 2 files changed, 186 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..598c88680 --- /dev/null +++ b/grub-core/commands/i386/tpm.c @@ -0,0 +1,151 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * TPM TIS and CRB driver. + * + * Note: It is suggested to not use this driver together with UEFI TPM driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + * Code based on TCG PC Client Platform TPM Profile Specification for TPM 2.0, + * Version 1.05 Revision 14 released September 4, 2020. + */ + +#define TPM_MMIO_BASE 0xfed40000 + +/* 6.3.2 Register Space Addresses */ +/* TIS registers. */ +#define TPM_ACCESS 0x0000 +#define TPM_INTF_CAPABILITY 0x0014 +#define INTF_CAP_INTERFACE_VERSION_SHIFT 28 +#define INTF_CAP_INTERFACE_VERSION_MASK 7 +#define TPM_INTERFACE_ID 0x0030 +#define INTERFACE_ID_INTERFACE_TYPE_SHIFT 0 +#define INTERFACE_ID_INTERFACE_TYPE_MASK 0xF + +/* CRB registers. */ +#define TPM_LOC_CTRL 0x0008 + + +#define TIS_RELINQUISH_LCL 0x20 +#define CRB_RELINQUISH_LCL 0x0002 + +/* 6.4.2 Interface Identifier Register */ +#define TPM_CRB_INTF_ACTIVE 0x1 + +/* 6.5.2.7 Interface Capability */ +#define TPM_12_TIS_INTF_12 0x0 +#define TPM_12_TIS_INTF_13 0x2 +#define TPM_20_TIS_INTF_13 0x3 + +typedef enum + { + TPM_INTF_NONE = 0, + TPM_INTF_TIS, + TPM_INTF_CRB + } +tpm_intf_t; + +static grub_tpm_ver_t tpm_ver = GRUB_TPM_NONE; +static tpm_intf_t tpm_intf = TPM_INTF_NONE; + +grub_tpm_ver_t +grub_get_tpm_ver (void) +{ + return tpm_ver; +} + +/* Localities 0-4 are supported only. */ +void +grub_tpm_relinquish_locality (grub_uint8_t lcl) +{ + grub_addr_t addr = TPM_MMIO_BASE + lcl * GRUB_PAGE_SIZE; + + if (tpm_intf == TPM_INTF_TIS) + grub_write8 (TIS_RELINQUISH_LCL, addr + TPM_ACCESS); + else if (tpm_intf == TPM_INTF_CRB) + grub_write32 (CRB_RELINQUISH_LCL, addr + TPM_LOC_CTRL); +} + +static grub_err_t +grub_cmd_tpm_type (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + const char *tpm_ver_s = "NONE"; + const char *tpm_intf_s = "NONE"; + + if (tpm_ver == GRUB_TPM_12) + tpm_ver_s = "1.2"; + else if (tpm_ver == GRUB_TPM_20) + tpm_ver_s = "2.0"; + + if (tpm_intf == TPM_INTF_TIS) + tpm_intf_s = "TIS"; + else if (tpm_intf == TPM_INTF_CRB) + tpm_intf_s = "CRB"; + + grub_printf ("TPM family: %s\nTPM interface: %s\n", tpm_ver_s, tpm_intf_s); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_tpm_type; + +GRUB_MOD_INIT (tpm) +{ + grub_uint32_t intf_id; + grub_uint32_t intf_cap; + + cmd_tpm_type = grub_register_command ("tpm_type", grub_cmd_tpm_type, + NULL, N_("Show TPM version and interface type.")); + + tpm_ver = GRUB_TPM_20; + + intf_id = grub_read32 (TPM_MMIO_BASE + TPM_INTERFACE_ID); + intf_id >>= INTERFACE_ID_INTERFACE_TYPE_SHIFT; + intf_id &= INTERFACE_ID_INTERFACE_TYPE_MASK; + + tpm_intf = (intf_id == TPM_CRB_INTF_ACTIVE) ? TPM_INTF_CRB : TPM_INTF_TIS; + + /* CRB exists only in TPM 2.0 */ + if (tpm_intf == TPM_INTF_CRB) + return; + + intf_cap = grub_read32 (TPM_MMIO_BASE + TPM_INTF_CAPABILITY); + intf_cap >>= INTF_CAP_INTERFACE_VERSION_SHIFT; + intf_cap &= INTF_CAP_INTERFACE_VERSION_MASK; + + if (intf_cap == TPM_12_TIS_INTF_12 || intf_cap == TPM_12_TIS_INTF_13) + tpm_ver = GRUB_TPM_12; +} + +GRUB_MOD_FINI (tpm) +{ + grub_unregister_command (cmd_tpm_type); +} diff --git a/include/grub/i386/tpm.h b/include/grub/i386/tpm.h new file mode 100644 index 000000000..ded6d9af3 --- /dev/null +++ b/include/grub/i386/tpm.h @@ -0,0 +1,35 @@ +/* + * 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 ee6d3180fcf7a9fa42971fa08f9fc0d98a5f057d Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Mon, 9 Jan 2023 12:55:42 -0500 Subject: [PATCH 10/24] include/grub: Introduce 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 Signed-off-by: Krystian Hebel --- include/grub/slr_table.h | 328 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 328 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..514ec5feb --- /dev/null +++ b/include/grub/slr_table.h @@ -0,0 +1,328 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * Secure Launch Resource Table definitions + */ + +#ifndef GRUB_SLR_TABLE_H +#define GRUB_SLR_TABLE_H 1 + +#include + +#define GRUB_UEFI_SLR_TABLE_GUID \ + { 0x877a9b2a, 0x0385, 0x45d1, { 0xa0, 0x34, 0x9d, 0xac, 0x9c, 0x9e, 0x56, 0x5f }} + +/* SLR table header values */ +#define GRUB_SLR_TABLE_MAGIC 0x4452544d +#define GRUB_SLR_TABLE_REVISION 1 + +/* Current revisions for the policy and UEFI config */ +#define GRUB_SLR_POLICY_REVISION 1 +#define GRUB_SLR_UEFI_CONFIG_REVISION 1 + +/* SLR defined architectures */ +#define GRUB_SLR_INTEL_TXT 1 +#define GRUB_SLR_AMD_SKINIT 2 + +/* SLR defined bootloaders */ +#define GRUB_SLR_BOOTLOADER_INVALID 0 +#define GRUB_SLR_BOOTLOADER_GRUB 1 + +/* Log formats */ +#define GRUB_SLR_DRTM_TPM12_LOG 1 +#define GRUB_SLR_DRTM_TPM20_LOG 2 + +/* DRTM Policy Entry Flags */ +#define GRUB_SLR_POLICY_FLAG_MEASURED 0x1 +#define GRUB_SLR_POLICY_IMPLICIT_SIZE 0x2 + +/* Array Lengths */ +#define GRUB_TPM_EVENT_INFO_LENGTH 32 +#define GRUB_TXT_VARIABLE_MTRRS_LENGTH 32 + +/* Tags */ +#define GRUB_SLR_ENTRY_INVALID 0x0000 +#define GRUB_SLR_ENTRY_DL_INFO 0x0001 +#define GRUB_SLR_ENTRY_LOG_INFO 0x0002 +#define GRUB_SLR_ENTRY_DRTM_POLICY 0x0003 +#define GRUB_SLR_ENTRY_INTEL_INFO 0x0004 +#define GRUB_SLR_ENTRY_AMD_INFO 0x0005 +#define GRUB_SLR_ENTRY_ARM_INFO 0x0006 +#define GRUB_SLR_ENTRY_UEFI_INFO 0x0007 +#define GRUB_SLR_ENTRY_UEFI_CONFIG 0x0008 +#define GRUB_SLR_ENTRY_END 0xffff + +/* Entity Types */ +#define GRUB_SLR_ET_UNSPECIFIED 0x0000 +#define GRUB_SLR_ET_SLRT 0x0001 +#define GRUB_SLR_ET_BOOT_PARAMS 0x0002 +#define GRUB_SLR_ET_SETUP_DATA 0x0003 +#define GRUB_SLR_ET_CMDLINE 0x0004 +#define GRUB_SLR_ET_UEFI_MEMMAP 0x0005 +#define GRUB_SLR_ET_RAMDISK 0x0006 +#define GRUB_SLR_ET_MULTIBOOT2_INFO 0x0007 +#define GRUB_SLR_ET_MULTIBOOT2_MODULE 0x0008 +#define GRUB_SLR_ET_TXT_OS2MLE 0x0010 +#define GRUB_SLR_ET_UNUSED 0xffff + +/* + * Primary Secure Launch Resource Table Header + */ +struct grub_slr_table +{ + grub_uint32_t magic; + grub_uint16_t revision; + grub_uint16_t architecture; + grub_uint32_t size; + grub_uint32_t max_size; + /* table entries */ +} GRUB_PACKED; + +/* + * Common SLRT Table Header + */ +struct grub_slr_entry_hdr +{ + grub_uint32_t tag; + grub_uint32_t size; +} GRUB_PACKED; + +/* + * Boot loader context + */ +struct grub_slr_bl_context +{ + grub_uint16_t bootloader; + grub_uint16_t reserved[3]; + grub_uint64_t context; +} GRUB_PACKED; + +/* + * Dynamic Launch Callback Function type + */ +typedef void (*grub_dl_handler_func)(struct grub_slr_bl_context *bl_context); + +/* + * DRTM Dynamic Launch Configuration + */ +struct grub_slr_entry_dl_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint64_t dce_size; + grub_uint64_t dce_base; + grub_uint64_t dlme_size; + grub_uint64_t dlme_base; + grub_uint64_t dlme_entry; + struct grub_slr_bl_context bl_context; + grub_uint64_t dl_handler; +} GRUB_PACKED; + +/* + * TPM Log Information + */ +struct grub_slr_entry_log_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t format; + grub_uint16_t reserved; + grub_uint32_t size; + grub_uint64_t addr; +} GRUB_PACKED; + +/* + * DRTM Measurement Entry + */ +struct grub_slr_policy_entry +{ + grub_uint16_t pcr; + grub_uint16_t entity_type; + grub_uint16_t flags; + grub_uint16_t reserved; + grub_uint64_t size; + grub_uint64_t entity; + char evt_info[GRUB_TPM_EVENT_INFO_LENGTH]; +} GRUB_PACKED; + +/* + * DRTM Measurement Policy + */ +struct grub_slr_entry_policy +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t reserved[2]; + grub_uint16_t revision; + grub_uint16_t nr_entries; + struct grub_slr_policy_entry policy_entries[]; +} GRUB_PACKED; + +/* + * Secure Launch defined MTRR saving structures + */ +struct grub_slr_txt_mtrr_pair +{ + grub_uint64_t mtrr_physbase; + grub_uint64_t mtrr_physmask; +} GRUB_PACKED; + +struct grub_slr_txt_mtrr_state +{ + grub_uint64_t default_mem_type; + grub_uint64_t mtrr_vcnt; + struct grub_slr_txt_mtrr_pair mtrr_pair[GRUB_TXT_VARIABLE_MTRRS_LENGTH]; +} GRUB_PACKED; + +/* + * Intel TXT Info table + */ +struct grub_slr_entry_intel_info +{ + struct grub_slr_entry_hdr hdr; + grub_uint64_t txt_heap; + grub_uint64_t saved_misc_enable_msr; + struct grub_slr_txt_mtrr_state saved_bsp_mtrrs; +} GRUB_PACKED; + +/* + * UEFI config measurement entry + */ +struct grub_slr_uefi_cfg_entry +{ + grub_uint16_t pcr; + grub_uint16_t reserved; + grub_uint32_t size; + grub_uint64_t cfg; /* address or value */ + char evt_info[GRUB_TPM_EVENT_INFO_LENGTH]; +} GRUB_PACKED; + +/* + * UEFI config measurements + */ +struct grub_slr_entry_uefi_config +{ + struct grub_slr_entry_hdr hdr; + grub_uint16_t reserved[2]; + grub_uint16_t revision; + grub_uint16_t nr_entries; + struct grub_slr_uefi_cfg_entry uefi_cfg_entries[]; +} GRUB_PACKED; + +static inline grub_addr_t +grub_slr_end_of_entries (struct grub_slr_table *table) +{ + return (grub_addr_t) table + table->size; +} + +static inline struct grub_slr_entry_hdr * +grub_slr_next_entry (struct grub_slr_table *table, + struct grub_slr_entry_hdr *curr) +{ + grub_addr_t addr; + struct grub_slr_entry_hdr *next; + + /* Can read the size field of current entry? */ + if (grub_add ((grub_addr_t) curr, sizeof(*curr), &addr)) + return NULL; + + /* Does current size overflow? */ + if (grub_add ((grub_addr_t) curr, curr->size, &addr)) + return NULL; + + /* Can read the size field of next entry? */ + if (grub_add (addr, sizeof(*next), &addr)) + return NULL; + + /* Does next element's header fit within the table? */ + if (addr > grub_slr_end_of_entries (table)) + return NULL; + + next = (struct grub_slr_entry_hdr *) (addr - sizeof(*next)); + + /* Does next element fit within the table? */ + if (grub_slr_end_of_entries (table) - (addr - sizeof(*next)) < next->size) + return NULL; + + /* Is this the last element? */ + if (next->tag == GRUB_SLR_ENTRY_END) + return NULL; + + return next; +} + +static inline struct grub_slr_entry_hdr * +grub_slr_next_entry_by_tag (struct grub_slr_table *table, + struct grub_slr_entry_hdr *entry, + grub_uint16_t tag) +{ + if (!entry) /* Start from the beginning */ + entry = (struct grub_slr_entry_hdr *)((grub_uint8_t *) table + sizeof(*table)); + + for ( ; ; ) + { + if (entry->tag == tag) + return entry; + + entry = grub_slr_next_entry (table, entry); + if (!entry) + return NULL; + } + + return NULL; +} + +static inline int +grub_slr_add_entry (struct grub_slr_table *table, + struct grub_slr_entry_hdr *entry) +{ + struct grub_slr_entry_hdr *end; + grub_uint32_t new_size; + + if (entry->size < sizeof(*end) || + grub_add (table->size, entry->size, &new_size) || + new_size > table->max_size) + return -1; + + grub_memcpy ((grub_uint8_t *) table + table->size - sizeof(*end), entry, entry->size); + table->size += entry->size; + + end = (struct grub_slr_entry_hdr *)((grub_uint8_t *) table + table->size - sizeof(*end)); + end->tag = GRUB_SLR_ENTRY_END; + end->size = sizeof(*end); + + return 0; +} + +static inline void +grub_slr_init_table(struct grub_slr_table *slrt, grub_uint16_t architecture, + grub_uint32_t max_size) +{ + struct grub_slr_entry_hdr *end; + + if (max_size < sizeof(*slrt) + sizeof(*end)) + grub_fatal ("Requested SLRT max size (%" PRIuGRUB_UINT32_T " B) is too small\n", + max_size); + + slrt->magic = GRUB_SLR_TABLE_MAGIC; + slrt->revision = GRUB_SLR_TABLE_REVISION; + slrt->architecture = architecture; + slrt->size = sizeof(*slrt) + sizeof(*end); + slrt->max_size = max_size; + end = (struct grub_slr_entry_hdr *)((grub_uint8_t *) slrt + sizeof(*slrt)); + end->tag = GRUB_SLR_ENTRY_END; + end->size = sizeof(*end); +} + +#endif /* GRUB_SLR_TABLE_H */ From 0b1682c7b08289088a951ae31328163c6cc89aa4 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:50:14 -0400 Subject: [PATCH 11/24] 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 Signed-off-by: Sergii Dmytruk --- 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 | 77 ++++++++++++++++++++++ 5 files changed, 351 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..881a4dad4 --- /dev/null +++ b/include/grub/i386/crfr.h @@ -0,0 +1,127 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_CRFR_H +#define GRUB_CRFR_H 1 + +#include + +/* Routines for R/W of control and flags registers */ + +#define GRUB_CR0_X86_PE 0x00000001 /* Enable Protected Mode */ +#define GRUB_CR0_X86_MP 0x00000002 /* "Math" (FPU) Present */ +#define GRUB_CR0_X86_EM 0x00000004 /* EMulate FPU */ +#define GRUB_CR0_X86_TS 0x00000008 /* Task Switched */ +#define GRUB_CR0_X86_PG 0x80000000 /* Enable PaGing */ + +#define GRUB_CR0_X86_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */ +#define GRUB_CR0_X86_WP 0x00010000 /* Write Protect */ +#define GRUB_CR0_X86_AM 0x00040000 /* Alignment Mask */ +#define GRUB_CR0_X86_NW 0x20000000 /* Not Write-through */ +#define GRUB_CR0_X86_CD 0x40000000 /* Cache Disable */ + +#define GRUB_CR4_X86_VME 0x00000001 /* Virtual 8086 mode extensions */ +#define GRUB_CR4_X86_PVI 0x00000002 /* Protected-mode virtual interrupts */ +#define GRUB_CR4_X86_TSD 0x00000004 /* Time stamp disable */ +#define GRUB_CR4_X86_DE 0x00000008 /* Debugging extensions */ +#define GRUB_CR4_X86_PSE 0x00000010 /* Page size extensions */ +#define GRUB_CR4_X86_PAE 0x00000020 /* Physical address extension */ +#define GRUB_CR4_X86_MCE 0x00000040 /* Enable Machine check enable */ +#define GRUB_CR4_X86_PGE 0x00000080 /* Enable Page global */ +#define GRUB_CR4_X86_PCE 0x00000100 /* Enable Performance monitoring counter */ +#define GRUB_CR4_X86_FXSR 0x00000200 /* Fast FPU save/restore */ +#define GRUB_CR4_X86_XMM 0x00000400 /* Generate #XM instead of #UD for SIMD */ +#define GRUB_CR4_X86_VMXE 0x00002000 /* Enable VMX */ +#define GRUB_CR4_X86_SMXE 0x00004000 /* Enable SMX */ +#define GRUB_CR4_X86_PCIDE 0x00020000 /* Enable PCID */ + +static inline unsigned long +grub_read_cr0 (void) +{ + unsigned long val; + + asm volatile ("mov %%cr0, %0" : "=r" (val) : : "memory"); + + return val; +} + +static inline void +grub_write_cr0 (unsigned long val) +{ + asm volatile ("mov %0, %%cr0" : : "r" (val) : "memory"); +} + +static inline unsigned long +grub_read_cr4 (void) +{ + unsigned long val; + + asm volatile ("mov %%cr4, %0" : "=r" (val) : : "memory"); + + return val; +} + +static inline void +grub_write_cr4 (unsigned long val) +{ + asm volatile ("mov %0, %%cr4" : : "r" (val) : "memory"); +} + +#define GRUB_EFLAGS_X86_CF 0x00000001 /* Carry Flag */ +#define GRUB_EFLAGS_X86_PF 0x00000004 /* Parity Flag */ +#define GRUB_EFLAGS_X86_AF 0x00000010 /* Auxillary carry Flag */ +#define GRUB_EFLAGS_X86_ZF 0x00000040 /* Zero Flag */ +#define GRUB_EFLAGS_X86_SF 0x00000080 /* Sign Flag */ +#define GRUB_EFLAGS_X86_TF 0x00000100 /* Trap Flag */ +#define GRUB_EFLAGS_X86_IF 0x00000200 /* Interrupt Flag */ +#define GRUB_EFLAGS_X86_DF 0x00000400 /* Direction Flag */ +#define GRUB_EFLAGS_X86_OF 0x00000800 /* Overflow Flag */ +#define GRUB_EFLAGS_X86_IOPL 0x00003000 /* IOPL mask */ +#define GRUB_EFLAGS_X86_NT 0x00004000 /* Nested Task */ +#define GRUB_EFLAGS_X86_RF 0x00010000 /* Resume Flag */ +#define GRUB_EFLAGS_X86_VM 0x00020000 /* Virtual Mode */ +#define GRUB_EFLAGS_X86_AC 0x00040000 /* Alignment Check */ +#define GRUB_EFLAGS_X86_VIF 0x00080000 /* Virtual Interrupt Flag */ +#define GRUB_EFLAGS_X86_VIP 0x00100000 /* Virtual Interrupt Pending */ +#define GRUB_EFLAGS_X86_ID 0x00200000 /* CPUID detection flag */ + +static inline unsigned long +grub_read_flags_register (void) +{ + unsigned long flags; + +#ifdef __x86_64__ + asm volatile ("pushfq; popq %0" : "=r" (flags)); +#else + asm volatile ("pushfl; popl %0" : "=r" (flags)); +#endif + + return flags; +} + +static inline void +grub_write_flags_register (unsigned long flags) +{ +#ifdef __x86_64__ + asm volatile ("pushq %0; popfq" : : "r" (flags)); +#else + asm volatile ("pushl %0; popfl" : : "r" (flags)); +#endif +} + +#endif diff --git a/include/grub/i386/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..1574b3a1c --- /dev/null +++ b/include/grub/i386/slaunch.h @@ -0,0 +1,77 @@ +/* + * 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_uint64_t slr_table_base; + /* This is size of SLRT buffer, so maximum size of the table. */ + grub_uint32_t slr_table_size; + void *slr_table_mem; + grub_uint32_t mle_start; + grub_uint32_t mle_size; + void *mle_mem; + grub_uint64_t mle_ptab_target; + grub_uint32_t mle_ptab_size; + void *mle_ptab_mem; + grub_uint32_t mle_header_offset; + grub_uint32_t ap_wake_block; + grub_uint32_t ap_wake_block_size; + grub_uint32_t dce_base; + grub_uint32_t dce_size; + grub_uint64_t tpm_evt_log_base; + grub_uint32_t tpm_evt_log_size; +}; + +extern grub_uint32_t grub_slaunch_platform_type (void); +extern void *grub_slaunch_module (void); +extern struct grub_slaunch_params *grub_slaunch_params (void); + +extern void grub_slaunch_init_slrt_storage (int arch); +extern void grub_slaunch_add_slrt_policy_entries (void); +extern void grub_slaunch_add_slrt_policy_entry (grub_uint16_t pcr, + grub_uint16_t entity_type, + grub_uint16_t flags, + grub_uint64_t entity, + grub_uint64_t size, + const char *evt_info); +extern void grub_slaunch_finish_slr_table (void); + +#endif /* ASM_FILE */ + +#endif /* GRUB_I386_SLAUNCH_H */ From f47656a392adef2f900213aeb32612419f243e7b Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:53:06 -0400 Subject: [PATCH 12/24] i386/txt: Add Intel TXT definitions header file Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk --- include/grub/i386/txt.h | 709 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 709 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..53e4b788c --- /dev/null +++ b/include/grub/i386/txt.h @@ -0,0 +1,709 @@ +/* + * 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 (20 * GRUB_PAGE_SIZE) + +struct grub_txt_acm_header +{ + grub_uint16_t module_type; + grub_uint16_t module_sub_type; + grub_uint32_t header_len; + grub_uint32_t header_version; + grub_uint16_t chipset_id; + grub_uint16_t flags; + grub_uint32_t module_vendor; + grub_uint32_t date; /* e.g 20131231H == December 31, 2013 */ + grub_uint32_t size; /* multiples of 4 bytes */ + grub_uint16_t txt_svn; + grub_uint16_t se_svn; + grub_uint32_t code_control; + grub_uint32_t error_entry_point; + grub_uint32_t gdt_limit; + grub_uint32_t gdt_base; + grub_uint32_t seg_sel; + grub_uint32_t entry_point; + grub_uint8_t reserved2[64]; + grub_uint32_t key_size; + grub_uint32_t scratch_size; + /* RSA Pub Key and Signature */ +} GRUB_PACKED; + +/* A.1.2 ACM Information Table */ +#define GRUB_TXT_ACM_UUID "\xaa\x3a\xc0\x7f\xa7\x46\xdb\x18\x2e\xac\x69\x8f\x8d\x41\x7f\x5a" + +#define GRUB_TXT_ACM_CHIPSET_TYPE_BIOS 0 +#define GRUB_TXT_ACM_CHIPSET_TYPE_SINIT 1 +/* Revocation ACMs */ +#define GRUB_TXT_ACM_CHIPSET_TYPE_BIOS_RACM 8 +#define GRUB_TXT_ACM_CHIPSET_TYPE_SINIT_RACM 9 + +struct grub_txt_acm_info_table +{ + grub_uint8_t uuid[16]; + grub_uint8_t chipset_acm_type; + grub_uint8_t version; + grub_uint16_t length; + grub_uint32_t chipset_id_list; + grub_uint32_t os_sinit_data_ver; + grub_uint32_t min_mle_header_ver; + grub_uint32_t capabilities; + grub_uint32_t acm_version_revision; + grub_uint32_t processor_id_list; + /* Version >= 5 */ + grub_uint32_t tpm_info_list; +} GRUB_PACKED; + +struct grub_txt_acm_chipset_id_list +{ + grub_uint32_t count; + /* Array of chipset ID structs */ +} GRUB_PACKED; + +#define GRUB_TXT_ACM_REVISION_ID_MASK (1<<0) + +struct grub_txt_acm_chipset_id +{ + grub_uint32_t flags; + grub_uint16_t vendor_id; + grub_uint16_t device_id; + grub_uint16_t revision_id; + grub_uint16_t reserved; + grub_uint32_t extended_id; +} GRUB_PACKED; + +struct grub_txt_acm_processor_id_list +{ + grub_uint32_t count; + /* Array of processor ID structs */ +} GRUB_PACKED; + +struct grub_txt_acm_processor_id +{ + grub_uint32_t fms; + grub_uint32_t fms_mask; + grub_uint64_t platform_id; + grub_uint64_t platform_mask; +} GRUB_PACKED; + +struct grub_txt_acm_tpm_info +{ + grub_uint32_t capabilities; + grub_uint16_t count; + /* List of supported hash algorithm per TPM2 spec */ +} GRUB_PACKED; + +/* + * Appendix B SMX Interaction with Platform + * B.1 Intel Trusted Execution Technology Configuration Registers + */ + +#ifdef __x86_64__ +#define GRUB_TXT_CFG_REGS_PUB 0xfed30000ULL +#else +#define GRUB_TXT_CFG_REGS_PUB 0xfed30000 +#endif + +#define GRUB_TXT_STS 0x0000 +#define GRUB_TXT_ESTS 0x0008 +#define GRUB_TXT_ERRORCODE 0x0030 +#define GRUB_TXT_CMD_RESET 0x0038 +#define GRUB_TXT_CMD_CLOSE_PRIVATE 0x0048 +/* VER_FSBIF is considered deprecated, but some CPUs still use it */ +#define GRUB_TXT_VER_FSBIF 0x0100 +#define GRUB_TXT_DIDVID 0x0110 +#define GRUB_TXT_VER_QPIIF 0x0200 +#define GRUB_TXT_CMD_UNLOCK_MEM_CONFIG 0x0218 +#define GRUB_TXT_SINIT_BASE 0x0270 +#define GRUB_TXT_SINIT_SIZE 0x0278 +#define GRUB_TXT_MLE_JOIN 0x0290 +#define GRUB_TXT_HEAP_BASE 0x0300 +#define GRUB_TXT_HEAP_SIZE 0x0308 +/* DPR is considered deprecated, but some CPUs still use it */ +#define GRUB_TXT_DPR 0x0330 +#define GRUB_TXT_CMD_OPEN_LOCALITY1 0x0380 +#define GRUB_TXT_CMD_CLOSE_LOCALITY1 0x0388 +#define GRUB_TXT_CMD_OPEN_LOCALITY2 0x0390 +#define GRUB_TXT_CMD_CLOSE_LOCALITY2 0x0398 +#define GRUB_TXT_PUBLIC_KEY 0x0400 +#define GRUB_TXT_CMD_SECRETS 0x08e0 +#define GRUB_TXT_CMD_NO_SECRETS 0x08e8 +#define GRUB_TXT_E2STS 0x08f0 + +#define GRUB_TXT_STS_SENTER_DONE (1 << 0) +#define GRUB_TXT_STS_SEXIT_DONE (1 << 1) +#define GRUB_TXT_STS_MEM_CONFIG_LOCK (1 << 6) +#define GRUB_TXT_STS_PRIVATE_OPEN (1 << 7) +#define GRUB_TXT_STS_LOCALITY1_OPEN (1 << 15) +#define GRUB_TXT_STS_LOCALITY2_OPEN (1 << 16) + +#define GRUB_TXT_ESTS_TXT_RESET (1 << 0) + +#define GRUB_TXT_VER_FSBIF_DEBUG_FUSE (1 << 31) + +#define GRUB_TXT_VER_QPIIF_DEBUG_FUSE (1 << 31) + +#define GRUB_TXT_E2STS_SECRETS (1 << 1) + +union grub_txt_didvid +{ + grub_uint64_t value; + struct + { + grub_uint16_t vid; + grub_uint16_t did; + grub_uint16_t rid; + grub_uint16_t id_ext; + }; +} GRUB_PACKED; + +#define GRUB_TXT_VERSION_PROD_FUSED (1<<31) + +/* Appendix C Intel TXT Heap Memory */ + +/* Ext Data Structs */ + +struct grub_txt_heap_uuid +{ + grub_uint32_t data1; + grub_uint16_t data2; + grub_uint16_t data3; + grub_uint16_t data4; + grub_uint8_t data5[6]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_END 0 + +struct grub_txt_heap_end_element +{ + /* Empty, just the common header with type and size */ +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER 1 + +struct grub_txt_heap_bios_spec_ver_element +{ + grub_uint16_t spec_ver_major; + grub_uint16_t spec_ver_minor; + grub_uint16_t spec_ver_revision; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_ACM 2 + +struct grub_txt_heap_acm_element +{ + grub_uint32_t num_acms; + /* Array of num_acms grub_uint64_t addresses */ + grub_uint64_t addr[]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_STM 3 + +struct grub_txt_heap_stm_element +{ + /* STM specific BIOS properties */ + /* + * GNU extension used to counteract "error: flexible array member in a struct + * with no named members". + */ + grub_uint8_t data[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_CUSTOM 4 + +struct grub_txt_heap_custom_element +{ + struct grub_txt_heap_uuid uuid; + /* Vendor Data */ + grub_uint8_t data[]; +} GRUB_PACKED; + +/* Deprecated, but still used for TPM 1.2 */ +#define GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR 5 + +struct grub_txt_heap_tpm_event_log_element +{ + grub_uint64_t event_log_phys_addr; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_MADT 6 + +struct grub_txt_heap_madt_element +{ + /* Copy of ACPI MADT table */ + /* + * GNU extension used to counteract "error: flexible array member in a struct + * with no named members". + */ + grub_uint8_t madt[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 8 + +struct grub_txt_heap_event_log_pointer2_1_element +{ + grub_uint64_t phys_addr; + grub_uint32_t allocated_event_container_size; + grub_uint32_t first_record_offset; + grub_uint32_t next_record_offset; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG 9 + +struct grub_txt_heap_mcfg_element +{ + /* Copy of ACPI MCFG table */ + /* + * GNU extension used to counteract "error: flexible array member in a struct + * with no named members". + */ + grub_uint8_t data[0]; +} GRUB_PACKED; + +#define GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE (2 * sizeof(grub_uint32_t)) + +struct grub_txt_heap_ext_data_element +{ + grub_uint32_t type; + grub_uint32_t size; /* Must be at least 8 bytes, includes size of this struct */ + union { + struct grub_txt_heap_end_element end; + struct grub_txt_heap_bios_spec_ver_element bios_spec_ver; + struct grub_txt_heap_acm_element acm; + struct grub_txt_heap_stm_element stm; + struct grub_txt_heap_custom_element custom; + struct grub_txt_heap_tpm_event_log_element tpm_event_log; + struct grub_txt_heap_madt_element madt; + struct grub_txt_heap_event_log_pointer2_1_element event_log_pointer2_1; + struct grub_txt_heap_mcfg_element mcfg; + }; +} GRUB_PACKED; + +/* TXT Heap Tables */ + +struct grub_txt_bios_data +{ + grub_uint32_t version; /* Currently 5 for TPM 1.2 and 6 for TPM 2.0 */ + grub_uint32_t bios_sinit_size; + grub_uint64_t reserved1; + grub_uint64_t reserved2; + grub_uint32_t num_logical_procs; + /* Versions >= 3 */ + grub_uint32_t sinit_flags; + /* Versions >= 5 with updates in version 6 */ + grub_uint32_t mle_flags; + /* Versions >= 4 */ + /* Ext Data Elements */ +} GRUB_PACKED; + +/* GRUB SLAUNCH specific definitions OS-MLE data */ +#define GRUB_SL_OS_MLE_STRUCT_VERSION 1 + +struct grub_txt_os_mle_data +{ + grub_uint32_t version; + grub_uint32_t boot_params_addr; + grub_uint32_t slrt; + grub_uint32_t txt_info; + grub_uint32_t ap_wake_block; + grub_uint32_t ap_wake_block_size; + grub_uint8_t mle_scratch[64]; +} GRUB_PACKED; + +/* Table 29. OS to SINIT Data Table */ +#define GRUB_TXT_PCR_EXT_MAX_AGILITY_POLICY 0 +#define GRUB_TXT_PCR_EXT_MAX_PERF_POLICY 1 + +struct grub_txt_os_sinit_data +{ + grub_uint32_t version; /* Currently 6 for TPM 1.2 and 7 for TPM 2.0 */ + grub_uint32_t flags; /* Version 7+ only, otherwise reserved */ + grub_uint64_t mle_ptab; + grub_uint64_t mle_size; + grub_uint64_t mle_hdr_base; + grub_uint64_t vtd_pmr_lo_base; + grub_uint64_t vtd_pmr_lo_size; + grub_uint64_t vtd_pmr_hi_base; + grub_uint64_t vtd_pmr_hi_size; + grub_uint64_t lcp_po_base; + grub_uint64_t lcp_po_size; + grub_uint32_t capabilities; + /* Versions >= 5 */ + /* Warning: version 5 has pointer to RSDT here, not RSDP */ + grub_uint64_t efi_rsdp_ptr; + /* Versions >= 6 */ + /* Ext Data Elements */ + grub_uint8_t ext_data_elts[]; +} GRUB_PACKED; + +struct grub_txt_sinit_mle_data +{ + grub_uint32_t version; /* Current values are 6 through 9 */ + /* Reserved for versions >= 9, must be 0 */ + grub_uint8_t bios_acm_id[20]; + grub_uint32_t edx_senter_flags; + grub_uint64_t mseg_valid; + grub_uint8_t sinit_hash[20]; + grub_uint8_t mle_hash[20]; + grub_uint8_t stm_hash[20]; + grub_uint8_t lcp_policy_hash[20]; + grub_uint32_t lcp_policy_control; + /* Versions >= 7 */ + grub_uint32_t rlp_wakeup_addr; + grub_uint32_t reserved; + grub_uint32_t num_of_sinit_mdrs; + grub_uint32_t sinit_mdrs_table_offset; + grub_uint32_t sinit_vtd_dmar_table_size; + grub_uint32_t sinit_vtd_dmar_table_offset; + /* Versions >= 8 */ + grub_uint32_t processor_scrtm_status; /* Reserved for version 9 */ + /* Versions >= 9 */ + /* Ext Data Elements */ +} GRUB_PACKED; + +struct grub_txt_sinit_memory_descriptor_records +{ + grub_uint64_t address; + grub_uint64_t length; + grub_uint8_t type; + grub_uint8_t reserved[7]; +} GRUB_PACKED; + +/* + * Section 2 Measured Launch Environment + * 2.1 MLE Architecture Overview + * Table 3. MLE Header structure + */ + +#define GRUB_TXT_MLE_UUID "\x5a\xac\x82\x90\x6f\x47\xa7\x74\x0f\x5c\x55\xa2\xcb\x51\xb6\x42" + +struct grub_txt_mle_header +{ + grub_uint8_t uuid[16]; + grub_uint32_t header_len; + grub_uint32_t version; + grub_uint32_t entry_point; + grub_uint32_t first_valid_page; + grub_uint32_t mle_start; + grub_uint32_t mle_end; + grub_uint32_t capabilities; + grub_uint32_t cmdline_start; + grub_uint32_t cmdline_end; +} GRUB_PACKED; + +struct grub_txt_heap_event_log_ptr_elt +{ + grub_uint64_t event_log_phys_addr; +} GRUB_PACKED; + +struct grub_txt_heap_event_log_ptr_elt2_1 +{ + grub_uint64_t phys_addr; + grub_uint32_t allcoated_event_container_size; + grub_uint32_t first_record_offset; + grub_uint32_t next_record_offset; +} GRUB_PACKED; + +/* TXT register and heap access */ + +static inline grub_uint8_t +grub_txt_reg_pub_read8 (grub_uint16_t reg) +{ + return grub_read8 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint32_t +grub_txt_reg_pub_read32 (grub_uint16_t reg) +{ + return grub_read32 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint64_t +grub_txt_reg_pub_read64 (grub_uint16_t reg) +{ + return grub_read64 (GRUB_TXT_CFG_REGS_PUB + reg); +} + +static inline grub_uint8_t * +grub_txt_get_heap (void) +{ + return (grub_uint8_t *)(grub_addr_t) grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); +} + +static inline grub_uint32_t +grub_txt_get_heap_size (void) +{ + return grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); +} + +/* + * Each block of data on heap begins with 64-bit size field, followed by proper + * data. Specified size includes size field, so the minimal value of that field + * is 8. TXT SDG mentions that all sizes must be multiples of 8 bytes, but even + * BiosData produced by code signed by Intel doesn't follow that requirement. + * This means that we can't just cast pointer to arbitrary location on TXT heap + * with (grub_uint64_t *), because unaligned pointer is UB. + */ +static inline grub_uint64_t +grub_txt_bios_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)heap; +} + +static inline struct grub_txt_bios_data * +grub_txt_bios_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_bios_data*)(heap + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_os_mle_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + grub_txt_bios_data_size (heap)); +} + +static inline struct grub_txt_os_mle_data * +grub_txt_os_mle_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_os_mle_data*)(heap + + grub_txt_bios_data_size (heap) + + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_os_sinit_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap)); +} + +static inline struct grub_txt_os_sinit_data * +grub_txt_os_sinit_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_os_sinit_data *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + sizeof (grub_uint64_t)); +} + +static inline grub_uint64_t +grub_txt_sinit_mle_data_size (grub_uint8_t *heap) +{ + return *(grub_uint64_t *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + grub_txt_os_sinit_data_size (heap)); +} + +static inline struct grub_txt_sinit_mle_data * +grub_txt_sinit_mle_data_start (grub_uint8_t *heap) +{ + return (struct grub_txt_sinit_mle_data *)(heap + + grub_txt_bios_data_size (heap) + + grub_txt_os_mle_data_size (heap) + + grub_txt_os_sinit_data_size (heap) + + sizeof (grub_uint64_t)); +} + +/* + * Intel 64 and IA-32 Architectures Software Developer’s Manual + * Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z + * Order Number: 325383-082US December 2023 + */ + +/* CHAPTER 7 SAFER MODE EXTENSIONS REFERENCE */ + +/* Table 7-2. GETSEC Leaf Functions */ +#define GRUB_SMX_LEAF_CAPABILITIES 0 +#define GRUB_SMX_LEAF_UNDEFINED 1 +#define GRUB_SMX_LEAF_ENTERACCS 2 +#define GRUB_SMX_LEAF_EXITAC 3 +#define GRUB_SMX_LEAF_SENTER 4 +#define GRUB_SMX_LEAF_SEXIT 5 +#define GRUB_SMX_LEAF_PARAMETERS 6 +#define GRUB_SMX_LEAF_SMCTRL 7 +#define GRUB_SMX_LEAF_WAKEUP 8 + +/* Table 7-3. GETSEC Capability Result Encoding */ +#define GRUB_SMX_CAPABILITY_CHIPSET_PRESENT (1<<0) +#define GRUB_SMX_CAPABILITY_UNDEFINED (1<<1) +#define GRUB_SMX_CAPABILITY_ENTERACCS (1<<2) +#define GRUB_SMX_CAPABILITY_EXITAC (1<<3) +#define GRUB_SMX_CAPABILITY_SENTER (1<<4) +#define GRUB_SMX_CAPABILITY_SEXIT (1<<5) +#define GRUB_SMX_CAPABILITY_PARAMETERS (1<<6) +#define GRUB_SMX_CAPABILITY_SMCTRL (1<<7) +#define GRUB_SMX_CAPABILITY_WAKEUP (1<<8) +#define GRUB_SMX_CAPABILITY_EXTENDED_LEAFS (1<<31) + +static inline grub_uint32_t +grub_txt_getsec_capabilities (grub_uint32_t index) +{ + grub_uint32_t caps; + + asm volatile ("getsec" + : "=a" (caps) + : "a" (GRUB_SMX_LEAF_CAPABILITIES), "b" (index)); + return caps; +} + +static inline void +grub_txt_getsec_parameters (grub_uint32_t index, grub_uint32_t *eax_out, + grub_uint32_t *ebx_out, grub_uint32_t *ecx_out) +{ + if (!eax_out || !ebx_out || !ecx_out) + return; + + asm volatile ("getsec" + : "=a" (*eax_out), "=b" (*ebx_out), "=c" (*ecx_out) + : "a" (GRUB_SMX_LEAF_PARAMETERS), "b" (index)); +} + +#define GRUB_SMX_PARAMETER_TYPE_MASK 0x1f +#define GRUB_SMX_PARAMETER_NULL 0 +#define GRUB_SMX_PARAMETER_ACM_VERSIONS 1 +#define GRUB_SMX_PARAMETER_MAX_ACM_SIZE 2 +#define GRUB_SMX_PARAMETER_ACM_MEMORY_TYPES 3 +#define GRUB_SMX_PARAMETER_SENTER_CONTROLS 4 +#define GRUB_SMX_PARAMETER_TXT_EXTENSIONS 5 + +#define GRUB_SMX_PARAMETER_MAX_VERSIONS 0x20 + +#define GRUB_SMX_GET_MAX_ACM_SIZE(v) ((v) & ~(__typeof__(v))GRUB_SMX_PARAMETER_TYPE_MASK) + +#define GRUB_SMX_ACM_MEMORY_TYPE_UC 0x00000100 +#define GRUB_SMX_ACM_MEMORY_TYPE_WC 0x00000200 +#define GRUB_SMX_ACM_MEMORY_TYPE_WT 0x00001000 +#define GRUB_SMX_ACM_MEMORY_TYPE_WP 0x00002000 +#define GRUB_SMX_ACM_MEMORY_TYPE_WB 0x00004000 + +#define GRUB_SMX_GET_ACM_MEMORY_TYPES(v) ((v) & ~(__typeof__(v))GRUB_SMX_PARAMETER_TYPE_MASK) + +#define GRUB_SMX_GET_SENTER_CONTROLS(v) ((v & 0x7f00) >> 8) + +#define GRUB_SMX_PROCESSOR_BASE_SCRTM 0x00000020 +#define GRUB_SMX_MACHINE_CHECK_HANDLING 0x00000040 +#define GRUB_SMX_GET_TXT_EXT_FEATURES(v) (v & (__typeof__(v))(GRUB_SMX_PROCESSOR_BASE_SCRTM|GRUB_SMX_MACHINE_CHECK_HANDLING)) + +#define GRUB_SMX_DEFAULT_VERSION 0x0 +#define GRUB_SMX_DEFAULT_VERSION_MASK 0xffffffff +#define GRUB_SMX_DEFAULT_MAX_ACM_SIZE 0x8000 /* 32K */ +#define GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE GRUB_SMX_ACM_MEMORY_TYPE_UC +#define GRUB_SMX_DEFAULT_SENTER_CONTROLS 0x0 + +/* + * Measured Launch Environment Developer’s Guide, + * Table 29. OS to SINIT Data Table + */ +#define GRUB_TXT_PMR_ALIGN_SHIFT 21 +#define GRUB_TXT_PMR_ALIGN (1 << GRUB_TXT_PMR_ALIGN_SHIFT) + +struct grub_smx_supported_versions +{ + grub_uint32_t mask; + grub_uint32_t version; +} GRUB_PACKED; + +struct grub_smx_parameters +{ + struct grub_smx_supported_versions versions[GRUB_SMX_PARAMETER_MAX_VERSIONS]; + grub_uint32_t version_count; + grub_uint32_t max_acm_size; + grub_uint32_t acm_memory_types; + grub_uint32_t senter_controls; + grub_uint32_t txt_feature_ext_flags; +} GRUB_PACKED; + +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); +/* + * 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 d684f477477406c88eff6328d243292ca03fa9d7 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:57:33 -0400 Subject: [PATCH 13/24] 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 Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/txt/txt.c | 1052 +++++++++++++++++++++++++++++++ include/grub/i386/slaunch.h | 14 +- 2 files changed, 1065 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..ece482ec7 --- /dev/null +++ b/grub-core/loader/i386/txt/txt.c @@ -0,0 +1,1052 @@ +/* + * txt.c: Intel(r) TXT support functions, including initiating measured + * launch, post-launch, AP wakeup, etc. + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OS_SINIT_DATA_TPM_12_VER 6 +#define OS_SINIT_DATA_TPM_20_VER 7 + +#define OS_SINIT_DATA_MIN_VER OS_SINIT_DATA_TPM_12_VER + +static struct grub_slr_entry_intel_info slr_intel_info_staging = {0}; + +static grub_err_t +enable_smx_mode (void) +{ + grub_uint32_t caps; + grub_uint64_t feat_ctrl = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if (!(feat_ctrl & GRUB_MSR_X86_FEATURE_CTRL_LOCK)) + { + grub_dprintf ("slaunch", "Firmware didn't lock FEATURE_CONTROL MSR," + "locking it now\n"); + /* Not setting SENTER_FUNCTIONS and SENTER_ENABLE because they were tested + * in grub_txt_verify_platform() */ + feat_ctrl |= GRUB_MSR_X86_ENABLE_VMX_OUT_SMX | + GRUB_MSR_X86_ENABLE_VMX_IN_SMX | + GRUB_MSR_X86_FEATURE_CTRL_LOCK; + grub_wrmsr (GRUB_MSR_X86_FEATURE_CONTROL, feat_ctrl); + } + + /* Enable SMX mode. */ + grub_write_cr4 (grub_read_cr4 () | GRUB_CR4_X86_SMXE); + + caps = grub_txt_getsec_capabilities (0); + + if (!(caps & GRUB_SMX_CAPABILITY_CHIPSET_PRESENT)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("TXT-capable chipset is not present")); + goto fail; + } + + if (!(caps & GRUB_SMX_CAPABILITY_SENTER)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[SENTER] is not available")); + goto fail; + } + + if (!(caps & GRUB_SMX_CAPABILITY_PARAMETERS)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[PARAMETERS] is not available")); + goto fail; + } + + return GRUB_ERR_NONE; + + fail: + /* Disable SMX mode on failure. */ + grub_write_cr4 (grub_read_cr4 () & ~GRUB_CR4_X86_SMXE); + + return grub_errno; +} + +static grub_err_t +grub_txt_smx_parameters (struct grub_smx_parameters *params) +{ + grub_uint32_t index = 0, eax, ebx, ecx, param_type; + + grub_memset (params, 0, sizeof(*params)); + + params->max_acm_size = GRUB_SMX_DEFAULT_MAX_ACM_SIZE; + params->acm_memory_types = GRUB_SMX_DEFAULT_ACM_MEMORY_TYPE; + params->senter_controls = GRUB_SMX_DEFAULT_SENTER_CONTROLS; + + do + { + grub_txt_getsec_parameters (index, &eax, &ebx, &ecx); + param_type = eax & GRUB_SMX_PARAMETER_TYPE_MASK; + + switch (param_type) + { + case GRUB_SMX_PARAMETER_NULL: + break; /* This means done. */ + + case GRUB_SMX_PARAMETER_ACM_VERSIONS: + if (params->version_count >= GRUB_SMX_PARAMETER_MAX_VERSIONS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Too many ACM versions")); + params->versions[params->version_count].mask = ebx; + params->versions[params->version_count++].version = ecx; + break; + + case GRUB_SMX_PARAMETER_MAX_ACM_SIZE: + params->max_acm_size = GRUB_SMX_GET_MAX_ACM_SIZE (eax); + break; + + case GRUB_SMX_PARAMETER_ACM_MEMORY_TYPES: + params->acm_memory_types = GRUB_SMX_GET_ACM_MEMORY_TYPES (eax); + break; + + case GRUB_SMX_PARAMETER_SENTER_CONTROLS: + params->senter_controls = GRUB_SMX_GET_SENTER_CONTROLS (eax); + break; + + case GRUB_SMX_PARAMETER_TXT_EXTENSIONS: + params->txt_feature_ext_flags = GRUB_SMX_GET_TXT_EXT_FEATURES (eax); + break; + + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SMX parameter")); + } + + ++index; + + } while (param_type != GRUB_SMX_PARAMETER_NULL); + + /* If no ACM versions were found, set the default one. */ + if (!params->version_count) + { + params->versions[0].mask = GRUB_SMX_DEFAULT_VERSION_MASK; + params->versions[0].version = GRUB_SMX_DEFAULT_VERSION; + params->version_count++; + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_txt_prepare_cpu (void) +{ + struct grub_smx_parameters params; + grub_uint32_t i; + grub_uint64_t mcg_cap, mcg_stat; + unsigned long cr0; + grub_err_t err; + + cr0 = grub_read_cr0 (); + + /* Cache must be enabled (CR0.CD = CR0.NW = 0). */ + cr0 &= ~(GRUB_CR0_X86_CD | GRUB_CR0_X86_NW); + + /* Native FPU error reporting must be enabled for proper interaction behavior. */ + cr0 |= GRUB_CR0_X86_NE; + + grub_write_cr0 (cr0); + + /* Disable virtual-8086 mode (EFLAGS.VM = 0). */ + grub_write_flags_register (grub_read_flags_register () & ~GRUB_EFLAGS_X86_VM); + + /* + * Verify all machine check status registers are clear (unless + * support preserving them). + */ + + /* Is machine check in progress? */ + if ( grub_rdmsr (GRUB_MSR_X86_MCG_STATUS) & GRUB_MSR_MCG_STATUS_MCIP ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("machine check in progress during secure launch")); + + err = grub_txt_smx_parameters (¶ms); + if (err != GRUB_ERR_NONE) + return err; + + if (params.txt_feature_ext_flags & GRUB_SMX_PROCESSOR_BASE_SCRTM) + grub_dprintf ("slaunch", "CPU supports processor-based S-CRTM\n"); + + if (params.txt_feature_ext_flags & GRUB_SMX_MACHINE_CHECK_HANDLING) + grub_dprintf ("slaunch", "CPU supports preserving machine check errors\n"); + else + { + grub_dprintf ("slaunch", "CPU does not support preserving machine check errors\n"); + + /* Check if all machine check registers are clear. */ + mcg_cap = grub_rdmsr (GRUB_MSR_X86_MCG_CAP); + for (i = 0; i < (mcg_cap & GRUB_MSR_MCG_BANKCNT_MASK); ++i) + { + mcg_stat = grub_rdmsr (GRUB_MSR_X86_MC0_STATUS + i * 4); + if (mcg_stat & (1ULL << 63)) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("secure launch MCG[%u] = %" PRIxGRUB_UINT64_T " ERROR"), i, + mcg_stat); + } + } + + return GRUB_ERR_NONE; +} + +static void +save_mtrrs (struct grub_slr_txt_mtrr_state *saved_bsp_mtrrs) +{ + grub_uint64_t i; + + saved_bsp_mtrrs->default_mem_type = + grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + + saved_bsp_mtrrs->mtrr_vcnt = + grub_rdmsr (GRUB_MSR_X86_MTRRCAP) & GRUB_MSR_X86_VCNT_MASK; + + if (saved_bsp_mtrrs->mtrr_vcnt > GRUB_TXT_VARIABLE_MTRRS_LENGTH) + { + /* Print warning but continue saving what we can... */ + grub_printf ("WARNING: Actual number of variable MTRRs (%" PRIuGRUB_UINT64_T + ") > GRUB_SL_MAX_VARIABLE_MTRRS (%d)\n", + saved_bsp_mtrrs->mtrr_vcnt, + GRUB_TXT_VARIABLE_MTRRS_LENGTH); + saved_bsp_mtrrs->mtrr_vcnt = GRUB_TXT_VARIABLE_MTRRS_LENGTH; + } + + for (i = 0; i < saved_bsp_mtrrs->mtrr_vcnt; ++i) + { + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = + grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (i)); + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = + grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (i)); + } + /* Zero unused array items. */ + for ( ; i < GRUB_TXT_VARIABLE_MTRRS_LENGTH; ++i) + { + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask = 0; + saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase = 0; + } +} + +static void +set_all_mtrrs (int enable) +{ + grub_uint64_t mtrr_def_type; + + mtrr_def_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + + if ( enable ) + mtrr_def_type |= GRUB_MSR_X86_MTRR_ENABLE; + else + mtrr_def_type &= ~GRUB_MSR_X86_MTRR_ENABLE; + + grub_wrmsr (GRUB_MSR_X86_MTRR_DEF_TYPE, mtrr_def_type); +} + +#define SINIT_MTRR_MASK 0xFFFFFF /* SINIT requires 36b mask */ + +/* + * Note: bitfields in following structures are assumed to work on x86 and + * nothing else. All compilers supported by GRUB agree when it comes to layout + * of bits that is consistent with hardware implementation. It was decided to + * use bitfields for better readability instead of manual shifting and masking. + */ +union mtrr_physbase_t +{ + grub_uint64_t raw; + struct + { + grub_uint64_t type : 8; + grub_uint64_t reserved1 : 4; + grub_uint64_t base : 52; /* Define as max width and mask w/ */ + /* MAXPHYADDR when using */ + }; +} GRUB_PACKED; + +union mtrr_physmask_t +{ + grub_uint64_t raw; + struct + { + grub_uint64_t reserved1 : 11; + grub_uint64_t v : 1; /* valid */ + grub_uint64_t mask : 52; /* define as max width and mask w/ */ + /* MAXPHYADDR when using */ + }; +} GRUB_PACKED; + +static inline grub_uint32_t +bsrl (grub_uint32_t mask) +{ + grub_uint32_t result; + + asm ("bsrl %1,%0" : "=r" (result) : "rm" (mask) : "cc"); + + return result; +} + +static inline int +fls (int mask) +{ + return (mask == 0 ? mask : (int)bsrl ((grub_uint32_t)mask) + 1); +} + +/* + * Set the memory type for specified range (base to base+size) + * to mem_type and everything else to UC + */ +static grub_err_t +set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, + grub_uint32_t mem_type) +{ + grub_uint64_t mtrr_def_type; + grub_uint64_t mtrr_cap; + union mtrr_physbase_t mtrr_physbase; + union mtrr_physmask_t mtrr_physmask; + grub_uint32_t vcnt, pages_in_range; + unsigned long ndx, base_v; + int i = 0, j, num_pages, mtrr_s; + + /* Disable all fixed MTRRs, set default type to UC */ + mtrr_def_type = grub_rdmsr (GRUB_MSR_X86_MTRR_DEF_TYPE); + mtrr_def_type &= ~(GRUB_MSR_X86_MTRR_ENABLE_FIXED | GRUB_MSR_X86_DEF_TYPE_MASK); + mtrr_def_type |= GRUB_MTRR_MEMORY_TYPE_UC; + grub_wrmsr (GRUB_MSR_X86_MTRR_DEF_TYPE, mtrr_def_type); + + /* Initially disable all variable MTRRs (we'll enable the ones we use) */ + mtrr_cap = grub_rdmsr (GRUB_MSR_X86_MTRRCAP); + vcnt = (mtrr_cap & GRUB_MSR_X86_VCNT_MASK); + + for ( ndx = 0; ndx < vcnt; ndx++ ) + { + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.v = 0; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + } + + /* Map all AC module pages as mem_type */ + num_pages = GRUB_PAGE_UP(size) >> GRUB_PAGE_SHIFT; + + grub_dprintf ("slaunch", "setting MTRRs for acmod: base=%p, size=%x, num_pages=%d\n", + base, size, num_pages); + + /* + * Each VAR MTRR base must be a multiple of that MTRR's Size. + * grub_txt_sinit_select() made sure that base is at least 4K-aligned. + */ + base_v = (unsigned long)base; + /* MTRR size in pages */ + mtrr_s = 1; + + while ( (base_v & 0x01) == 0 ) + { + i++; + base_v = base_v >> 1; + } + + for (j = i - 12; j > 0; j--) + mtrr_s = mtrr_s*2; /* mtrr_s = mtrr_s << 1 */ + + grub_dprintf ("slaunch", "The maximum allowed MTRR range size=%d Pages \n", mtrr_s); + + ndx = 0; + + while ( num_pages >= mtrr_s ) + { + mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx)); + mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & + SINIT_MTRR_MASK; + mtrr_physbase.type = mem_type; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx), mtrr_physbase.raw); + + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.mask = ~(mtrr_s - 1) & SINIT_MTRR_MASK; + mtrr_physmask.v = 1; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + + base += (mtrr_s * GRUB_PAGE_SIZE); + num_pages -= mtrr_s; + ndx++; + if ( ndx == vcnt ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("exceeded number of var MTRRs when mapping range")); + } + + while ( num_pages > 0 ) + { + /* Set the base of the current MTRR */ + mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx)); + mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & + SINIT_MTRR_MASK; + mtrr_physbase.type = mem_type; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE (ndx), mtrr_physbase.raw); + + /* + * Calculate MTRR mask + * MTRRs can map pages in power of 2 + * may need to use multiple MTRRS to map all of region + */ + pages_in_range = 1 << (fls (num_pages) - 1); + + mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx)); + mtrr_physmask.mask = ~(pages_in_range - 1) & SINIT_MTRR_MASK; + mtrr_physmask.v = 1; + grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK (ndx), mtrr_physmask.raw); + + /* + * Prepare for the next loop depending on number of pages + * We figure out from the above how many pages could be used in this + * mtrr. Then we decrement the count, increment the base, + * increment the mtrr we are dealing with, and if num_pages is + * still not zero, we do it again. + */ + base += (pages_in_range * GRUB_PAGE_SIZE); + num_pages -= pages_in_range; + ndx++; + if ( ndx == vcnt ) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("exceeded number of var MTRRs when mapping range")); + } + + return GRUB_ERR_NONE; +} + +/* + * This must be done for each processor so that all have the same + * memory types + */ +static grub_err_t +set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) +{ + unsigned long eflags; + unsigned long cr0, cr4; + grub_err_t err; + + /* + * Need to do some things before we start changing MTRRs. + * + * Since this will modify some of the MTRRs, they should be saved first + * so that they can be restored once the AC mod is done. + */ + + /* Disable interrupts */ + eflags = grub_read_flags_register (); + grub_write_flags_register (eflags & ~GRUB_EFLAGS_X86_IF); + + /* Save CR0 then disable cache (CRO.CD=1, CR0.NW=0) */ + cr0 = grub_read_cr0 (); + grub_write_cr0 ( (cr0 & ~GRUB_CR0_X86_NW) | GRUB_CR0_X86_CD ); + + /* Flush caches */ + asm volatile ("wbinvd"); + + /* Save CR4 and disable global pages (CR4.PGE=0) */ + cr4 = grub_read_cr4 (); + grub_write_cr4 (cr4 & ~GRUB_CR4_X86_PGE); + + /* Disable MTRRs */ + set_all_mtrrs (0); + + /* Set MTRRs for AC mod and rest of memory */ + err = set_mtrr_mem_type ((grub_uint8_t*)hdr, hdr->size*4, + GRUB_MTRR_MEMORY_TYPE_WB); + + /* Undo some of earlier changes and enable our new settings */ + + /* Flush caches */ + asm volatile ("wbinvd"); + + /* Enable MTRRs */ + set_all_mtrrs (1); + + /* Restore CR0 (caching) */ + grub_write_cr0 (cr0); + + /* Restore CR4 (global pages) */ + grub_write_cr4 (cr4); + + /* Restore flags */ + grub_write_flags_register (eflags); + + return err; +} + +static void +setup_txt_slrt_entry (struct grub_slaunch_params *slparams, + struct grub_txt_os_mle_data *os_mle_data) +{ + struct grub_slr_table *slr_table = slparams->slr_table_mem; + struct grub_slr_entry_hdr *txt_info; + + grub_slr_add_entry (slr_table, &slr_intel_info_staging.hdr); + + txt_info = grub_slr_next_entry_by_tag (slr_table, NULL, GRUB_SLR_ENTRY_INTEL_INFO); + os_mle_data->txt_info = (grub_addr_t) slparams->slr_table_base + + ((grub_addr_t) txt_info - (grub_addr_t) slparams->slr_table_mem); +} + +/* + * Adds new element to the end. `size` does not include common header. + * Assume that heap was cleared and there is enough space to add the element. + */ +static inline struct grub_txt_heap_ext_data_element * +add_ext_data_elt (struct grub_txt_os_sinit_data *os_sinit_data, + grub_uint32_t type, grub_uint32_t size) +{ + struct grub_txt_heap_ext_data_element *elt = + (struct grub_txt_heap_ext_data_element *) os_sinit_data->ext_data_elts; + + while (elt->type != GRUB_TXT_HEAP_EXTDATA_TYPE_END) + elt = (struct grub_txt_heap_ext_data_element *)((grub_uint8_t *)elt + elt->size); + + elt->type = type; + elt->size = size + GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE; + + return elt; +} + +static grub_err_t +init_txt_heap (struct grub_slaunch_params *slparams, struct grub_txt_acm_header *sinit) +{ + grub_uint8_t *txt_heap; + grub_uint32_t os_sinit_data_ver, sinit_caps; + grub_uint64_t *size; + grub_uint64_t size_total; + struct grub_txt_os_mle_data *os_mle_data; + struct grub_txt_os_sinit_data *os_sinit_data; + struct grub_txt_heap_ext_data_element *elt; +#ifdef GRUB_MACHINE_EFI + struct grub_acpi_rsdp_v20 *rsdp; +#endif + + /* BIOS data already verified in grub_txt_verify_platform(). */ + + txt_heap = grub_txt_get_heap (); + + grub_dprintf ("slaunch", "TXT heap %p\n", txt_heap); + + /* OS/loader to MLE data. */ + + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + grub_dprintf ("slaunch", "OS MLE data: %p\n", os_mle_data); + size = (grub_uint64_t *) ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + *size = sizeof (*os_mle_data) + sizeof (grub_uint64_t); + + if (slparams->slr_table_base == GRUB_SLAUNCH_STORE_IN_OS2MLE) + { + /* SLRT needs to be at least 4-byte aligned per specification. */ + slparams->slr_table_base = + ALIGN_UP ((grub_addr_t) os_mle_data + sizeof (*os_mle_data), 4); + + /* Recompute size including SLRT table in it. */ + *size = (slparams->slr_table_base + slparams->slr_table_size) + - ((grub_addr_t) os_mle_data - sizeof (grub_uint64_t)); + + /* Size of heap sections should be a multiple of 8. */ + *size = ALIGN_UP (*size, 8); + } + + if (grub_add (grub_txt_bios_data_size (txt_heap), *size, &size_total) || + (size_total > grub_txt_get_heap_size ())) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsMleData")); + } + + grub_memset (os_mle_data, 0, sizeof (*os_mle_data)); + + os_mle_data->version = GRUB_SL_OS_MLE_STRUCT_VERSION; + os_mle_data->boot_params_addr = slparams->boot_params_addr; + os_mle_data->slrt = slparams->slr_table_base; + + os_mle_data->ap_wake_block = slparams->ap_wake_block; + os_mle_data->ap_wake_block_size = slparams->ap_wake_block_size; + + /* Setup the TXT specific SLR information */ + slr_intel_info_staging.hdr.tag = GRUB_SLR_ENTRY_INTEL_INFO; + slr_intel_info_staging.hdr.size = sizeof(struct grub_slr_entry_intel_info); + slr_intel_info_staging.saved_misc_enable_msr = + grub_rdmsr (GRUB_MSR_X86_MISC_ENABLE); + + /* Save the BSPs MTRR state so post launch can restore it. */ + grub_dprintf ("slaunch", "Saving MTRRs to OS MLE data\n"); + save_mtrrs (&slr_intel_info_staging.saved_bsp_mtrrs); + + /* OS/loader to SINIT data. */ + grub_dprintf ("slaunch", "Get supported OS SINIT data version\n"); + os_sinit_data_ver = grub_txt_supported_os_sinit_data_ver (sinit); + + if (os_sinit_data_ver < OS_SINIT_DATA_MIN_VER) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("unsupported OS to SINIT data version in SINIT ACM: %d" + " expected >= %d"), os_sinit_data_ver, OS_SINIT_DATA_MIN_VER); + + os_sinit_data = grub_txt_os_sinit_data_start (txt_heap); + grub_dprintf ("slaunch", "OS SINIT data: %p\n", os_sinit_data); + size = (grub_uint64_t *) ((grub_addr_t) os_sinit_data - sizeof (grub_uint64_t)); + + *size = sizeof(grub_uint64_t) + sizeof (struct grub_txt_os_sinit_data) + + GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE /* End element */; + + if (grub_get_tpm_ver () == GRUB_TPM_12) + *size += GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_tpm_event_log_element); + else if (grub_get_tpm_ver () == GRUB_TPM_20) + *size += GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_event_log_pointer2_1_element); + else + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unsupported TPM version")); + + if (grub_add (size_total, *size, &size_total) || + (size_total > grub_txt_get_heap_size ())) + { + *size = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("not enough TXT HEAP space for OsSinitData")); + } + + grub_memset (os_sinit_data, 0, *size); + +#ifdef GRUB_MACHINE_EFI + rsdp = grub_acpi_get_rsdpv2 (); + + if (rsdp == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("ACPI RSDP 2.0 missing\n")); + + os_sinit_data->efi_rsdp_ptr = (grub_uint64_t)(grub_addr_t) rsdp; +#endif + + os_sinit_data->mle_ptab = slparams->mle_ptab_target; + os_sinit_data->mle_size = slparams->mle_size; + + os_sinit_data->mle_hdr_base = slparams->mle_header_offset; + + /* TODO: Check low PMR with RMRR. Look at relevant tboot code too. */ + /* TODO: Kernel should not allocate any memory outside of PMRs regions!!! */ + os_sinit_data->vtd_pmr_lo_base = 0; + os_sinit_data->vtd_pmr_lo_size = ALIGN_DOWN (grub_mmap_get_highest (0x100000000), + GRUB_TXT_PMR_ALIGN); + + os_sinit_data->vtd_pmr_hi_base = ALIGN_UP (grub_mmap_get_lowest (0x100000000), + GRUB_TXT_PMR_ALIGN); + os_sinit_data->vtd_pmr_hi_size = ALIGN_DOWN (grub_mmap_get_highest (0xffffffffffffffff), + GRUB_TXT_PMR_ALIGN); + os_sinit_data->vtd_pmr_hi_size -= os_sinit_data->vtd_pmr_hi_base; + + grub_dprintf ("slaunch", + "vtd_pmr_lo_base: 0x%" PRIxGRUB_UINT64_T " vtd_pmr_lo_size: 0x%" + PRIxGRUB_UINT64_T " vtd_pmr_hi_base: 0x%" PRIxGRUB_UINT64_T + " vtd_pmr_hi_size: 0x%" PRIxGRUB_UINT64_T "\n", + os_sinit_data->vtd_pmr_lo_base, os_sinit_data->vtd_pmr_lo_size, + os_sinit_data->vtd_pmr_hi_base, os_sinit_data->vtd_pmr_hi_size); + + sinit_caps = grub_txt_get_sinit_capabilities (sinit); + + /* + * 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; + grub_uint8_t *txt_heap; + struct grub_txt_os_mle_data *os_mle_data; + struct grub_txt_acm_header *sinit_base; + + sinit_base = grub_txt_sinit_select (grub_slaunch_module ()); + + if (sinit_base == NULL) + return grub_errno; + + grub_dprintf ("slaunch", "Init TXT heap\n"); + err = init_txt_heap (slparams, sinit_base); + + if (err != GRUB_ERR_NONE) + return err; + + grub_dprintf ("slaunch", "TXT heap successfully prepared\n"); + + slparams->dce_base = (grub_uint32_t)(grub_addr_t) sinit_base; + slparams->dce_size = sinit_base->size * 4; + + /* Setup of SLR table. */ + grub_slaunch_init_slrt_storage (GRUB_SLR_INTEL_TXT); + txt_heap = grub_txt_get_heap (); + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + setup_txt_slrt_entry (slparams, os_mle_data); + + grub_tpm_relinquish_locality (0); + grub_dprintf ("slaunch", "Relinquished TPM locality 0\n"); + + err = set_mtrrs_for_acmod (sinit_base); + if (err) + return grub_error (err, N_("secure launch failed to set MTRRs for ACM")); + + grub_dprintf ("slaunch", "MTRRs set for ACMOD\n"); + + err = grub_txt_prepare_cpu (); + if ( err ) + return err; + + grub_dprintf ("slaunch", "CPU prepared for secure launch\n"); + + if (!(grub_rdmsr (GRUB_MSR_X86_APICBASE) & GRUB_MSR_X86_APICBASE_BSP)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("secure launch must run on BSP")); + + return GRUB_ERR_NONE; +} + +void +grub_txt_add_slrt_policy_entries (void) +{ + struct grub_txt_os_mle_data *os_mle_data; + grub_uint8_t *txt_heap; + + txt_heap = grub_txt_get_heap (); + os_mle_data = grub_txt_os_mle_data_start (txt_heap); + + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_TXT_OS2MLE, + /*flags=*/0, + (grub_addr_t) os_mle_data, + sizeof(*os_mle_data), + "Measured TXT OS-MLE data"); +} diff --git a/include/grub/i386/slaunch.h b/include/grub/i386/slaunch.h index 1574b3a1c..a694260cb 100644 --- a/include/grub/i386/slaunch.h +++ b/include/grub/i386/slaunch.h @@ -31,9 +31,21 @@ #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 #include struct grub_slaunch_params From 498f5f40a0f1ed0091a1ce56653298ae79cc46c8 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 13:59:18 -0400 Subject: [PATCH 14/24] 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 | 605 ++++++++++++++++++++++++++++++ 1 file changed, 605 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..55675a2fd --- /dev/null +++ b/grub-core/loader/i386/txt/acmod.c @@ -0,0 +1,605 @@ +/* + * acmod.c: support functions for use of Intel(r) TXT Authenticated + * Code (AC) Modules + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Macro that returns value of it->field if it's inside info table, 0 otherwise. + * Fields at or below 'length' ('uuid', 'chipset_acm_type', 'version') don't + * benefit from this macro, because it requires that 'length' (and by extension + * fields below that) is valid. + */ +#define info_table_get(it, field) \ + (__builtin_offsetof(struct grub_txt_acm_info_table, field) + sizeof(it->field)\ + <= it->length ? it->field : 0) + +/* + * Returns hdr + offset if [offset, offset + count * size) is within the bounds + * of the ACM, NULL otherwise. + */ +static void * +n_fit_in_acm (struct grub_txt_acm_header *hdr, grub_uint32_t offset, + grub_uint32_t size, grub_uint32_t count) +{ + grub_uint32_t total_size, elem_end; + /* ACM size overflow was checked in is_acmod() */ + grub_uint32_t acm_len = hdr->size * 4; + + /* + * `offset` will often come from `info_table_get`, and this is the most + * convenient place to check for the macro returning zero. This is fine, since + * there is no legitimate reason to access the zero offset in this manner. + */ + if (offset == 0) + return NULL; + + if (grub_mul (size, count, &total_size)) + return NULL; + + if (grub_add (offset, total_size, &elem_end)) + return NULL; + + if (elem_end > acm_len) + return NULL; + + /* + * Not checking if (hdr + elem_end) overflows. We know that (hdr + acm_len) + * doesn't, and that elem_end <= acm_len. For the same reason we don't have + * to check if (hdr + offset) overflows. + */ + + return (void *) ((unsigned long) hdr + offset); +} + +static void * +fits_in_acm (struct grub_txt_acm_header *hdr, grub_uint32_t offset, + grub_uint32_t size) +{ + return n_fit_in_acm(hdr, offset, size, 1); +} + +/* + * Returns pointer to ACM information table. If the table is located outside of + * ACM or its reported size is too small to cover at least 'length' field, + * NULL is returned instead. + */ +static struct grub_txt_acm_info_table* +get_acmod_info_table (struct grub_txt_acm_header* hdr) +{ + grub_uint32_t user_area_off, info_table_size; + struct grub_txt_acm_info_table *ptr = NULL; + /* Minimum size required to read full size of table */ + info_table_size = __builtin_offsetof (struct grub_txt_acm_info_table, length) + + sizeof(ptr->length); + + /* Overflow? */ + if (grub_add (hdr->header_len, hdr->scratch_size, &user_area_off)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length plus scratch size overflows")); + return NULL; + } + + if (grub_mul (user_area_off, 4, &user_area_off)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM header length and scratch size in bytes overflows")); + return NULL; + } + + ptr = fits_in_acm(hdr, user_area_off, info_table_size); + + if (ptr != NULL) + { + if (info_table_get (ptr, length) < info_table_size) + return NULL; + + info_table_size = info_table_get (ptr, length); + ptr = fits_in_acm(hdr, user_area_off, info_table_size); + } + + return ptr; +} + +/* + * Function returns pointer to chipset ID list, after checking that + * grub_txt_acm_chipset_id_list and all grub_txt_acm_chipset_id structures are + * within ACM. Otherwise, NULL is returned. + */ +static struct grub_txt_acm_chipset_id_list* +get_acmod_chipset_list (struct grub_txt_acm_header *hdr) +{ + struct grub_txt_acm_info_table *info_table; + grub_uint32_t id_entries_off; + struct grub_txt_acm_chipset_id_list *chipset_id_list; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table (hdr); + if (info_table == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table out of bounds")); + return NULL; + } + + chipset_id_list = fits_in_acm(hdr, info_table_get (info_table, chipset_id_list), + sizeof(struct grub_txt_acm_chipset_id_list)); + if (chipset_id_list == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset ID list out of bounds")); + return NULL; + } + + /* Overflows were checked by fits_in_acm() */ + id_entries_off = info_table->chipset_id_list + sizeof(*chipset_id_list); + + if (n_fit_in_acm (hdr, id_entries_off, sizeof(struct grub_txt_acm_chipset_id), + chipset_id_list->count) == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM chipset ID entries out of bounds")); + return NULL; + } + + return chipset_id_list; +} + +/* + * Function returns pointer to processor ID list, after checking that + * grub_txt_acm_processor_id_list and all grub_txt_acm_processor_id structures + * are within ACM. Otherwise, NULL is returned. + */ +static struct grub_txt_acm_processor_id_list * +get_acmod_processor_list (struct grub_txt_acm_header* hdr) +{ + struct grub_txt_acm_info_table *info_table; + grub_uint32_t id_entries_off; + struct grub_txt_acm_processor_id_list *proc_id_list; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table (hdr); + if (info_table == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM info table out of bounds")); + return NULL; + } + + proc_id_list = fits_in_acm (hdr, info_table_get (info_table, processor_id_list), + sizeof(*proc_id_list)); + if (proc_id_list == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor ID list out of bounds")); + return NULL; + } + + /* Overflows were checked by fits_in_acm() */ + id_entries_off = info_table->processor_id_list + sizeof(*proc_id_list); + + if (n_fit_in_acm (hdr, id_entries_off, sizeof(*proc_id_list), + proc_id_list->count) == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("ACM processor ID entries out of bounds")); + return NULL; + } + + return proc_id_list; +} + +static int +is_acmod (const void *acmod_base, grub_uint32_t acmod_size, + grub_uint8_t *type_out) +{ + struct grub_txt_acm_header *acm_hdr = (struct grub_txt_acm_header *)acmod_base; + struct grub_txt_acm_info_table *info_table; + grub_uint32_t size_from_hdr; + + /* First check size */ + if (acmod_size < sizeof (*acm_hdr)) + return 0; + + /* Then check overflow */ + if (grub_mul (acm_hdr->size, 4, &size_from_hdr)) + return 0; + + /* Then check size equivalency */ + if (acmod_size != size_from_hdr) + return 0; + + /* Then check type, sub-type and vendor */ + if ((acm_hdr->module_type != GRUB_TXT_ACM_MODULE_TYPE) || + (acm_hdr->module_sub_type != GRUB_TXT_ACM_MODULE_SUB_TYPE_TXT_ACM) || + (acm_hdr->module_vendor != GRUB_TXT_ACM_MODULE_VENDOR_INTEL)) + return 0; + + info_table = get_acmod_info_table (acm_hdr); + if (info_table == NULL) + return 0; + + /* Check if ACM UUID is present */ + if (grub_memcmp (&(info_table->uuid), GRUB_TXT_ACM_UUID, 16)) + return 0; + + /* + * TXT specification doesn't give clear mapping of info table size to version, + * so just warn if the size is different than expected but try to use it + * anyway. info_table_get() macro does enough testing to not read outside + * of info table. + */ + if (info_table->length < sizeof(*info_table)) + grub_dprintf ("slaunch", "Info table size (%x) smaller than expected (%" + PRIxGRUB_SIZE ")\n", + info_table->length, sizeof(*info_table)); + + if (type_out) + *type_out = info_table_get (info_table, chipset_acm_type); + + return 1; +} + +static struct grub_txt_acm_header * +get_bios_sinit (void *sinit_region_base) +{ + grub_uint8_t *txt_heap = grub_txt_get_heap (); + struct grub_txt_bios_data *bios_data = grub_txt_bios_data_start (txt_heap); + struct grub_txt_acm_header *bios_sinit; + grub_uint32_t tmp; + + if (sinit_region_base == NULL) + return NULL; + + if (bios_data->bios_sinit_size == 0) + return NULL; + + /* Check if ACM crosses 4G */ + if (grub_add ((unsigned long)sinit_region_base, bios_data->bios_sinit_size, + &tmp)) + return NULL; + + /* BIOS has loaded an SINIT module, so verify that it is valid */ + grub_dprintf ("slaunch", "BIOS has already loaded an SINIT module\n"); + + bios_sinit = (struct grub_txt_acm_header *) sinit_region_base; + + /* Is it a valid SINIT module? */ + if (!grub_txt_is_sinit_acmod (sinit_region_base, bios_data->bios_sinit_size) || + !grub_txt_acmod_match_platform (bios_sinit)) + { + grub_dprintf("slaunch", "BIOS SINIT module did not pass reasonableness checks"); + return NULL; + } + + return bios_sinit; +} + +grub_uint32_t +grub_txt_supported_os_sinit_data_ver (struct grub_txt_acm_header *hdr) +{ + static struct grub_txt_acm_info_table *info_table; + + /* Assumes that it passed grub_txt_is_sinit_acmod() */ + info_table = get_acmod_info_table (hdr); + + if (info_table == NULL) + return 0; + + return info_table_get (info_table, os_sinit_data_ver); +} + +grub_uint32_t +grub_txt_get_sinit_capabilities (struct grub_txt_acm_header *hdr) +{ + static struct grub_txt_acm_info_table *info_table; + + /* Assumes that it passed grub_txt_is_sinit_acmod() */ + info_table = get_acmod_info_table (hdr); + + if (info_table == NULL || info_table->version < 3) + return 0; + + return info_table_get (info_table, capabilities); +} + +int +grub_txt_is_sinit_acmod (const void *acmod_base, grub_uint32_t acmod_size) +{ + grub_uint8_t type; + + if (!is_acmod (acmod_base, acmod_size, &type)) + return 0; + + if (type != GRUB_TXT_ACM_CHIPSET_TYPE_SINIT) + return 0; + + return 1; +} + +static int +didvid_matches(union grub_txt_didvid didvid, + struct grub_txt_acm_chipset_id *chipset_id) +{ + if (didvid.vid != chipset_id->vendor_id) + return 0; + + if (didvid.did != chipset_id->device_id) + return 0; + + /* + * If RevisionIdMask is 0, the RevisionId field must exactly match the + * TXT.DIDVID.RID field. + */ + if ((chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) == 0 && + (didvid.rid == chipset_id->revision_id)) + return 1; + + /* + * If RevisionIdMask is 1, the RevisionId field is a bitwise mask that can be + * used to test for any bits set in the TXT.DIDVID.RID field. If any bits are + * set, the RevisionId is a match. + */ + if ((chipset_id->flags & GRUB_TXT_ACM_REVISION_ID_MASK) != 0 && + (didvid.rid & chipset_id->revision_id) != 0) + return 1; + + return 0; +} + +int +grub_txt_acmod_match_platform (struct grub_txt_acm_header *hdr) +{ + union grub_txt_didvid didvid; + grub_uint32_t fms, ign, i, ver; + grub_uint64_t platform_id; + struct grub_txt_acm_chipset_id_list *chipset_id_list; + struct grub_txt_acm_chipset_id *chipset_id; + struct grub_txt_acm_processor_id_list *proc_id_list; + struct grub_txt_acm_processor_id *proc_id; + struct grub_txt_acm_info_table *info_table; + + /* This fn assumes that the ACM has already passed the is_acmod() checks */ + info_table = get_acmod_info_table (hdr); + if (info_table == NULL) + return 0; + + /* Get chipset fusing, device, and vendor id info */ + didvid.value = grub_txt_reg_pub_read64 (GRUB_TXT_DIDVID); + + ver = grub_txt_reg_pub_read32 (GRUB_TXT_VER_QPIIF); + if (ver == 0xffffffff || ver == 0x00) /* Old CPU, need to use VER.FSBIF */ + ver = grub_txt_reg_pub_read32 (GRUB_TXT_VER_FSBIF); + + grub_dprintf ("slaunch", "chipset production fused: %s, " + "chipset vendor: 0x%x, device: 0x%x, revision: 0x%x\n", + (ver & GRUB_TXT_VERSION_PROD_FUSED) ? "yes" : "no" , didvid.vid, + didvid.did, didvid.rid); + + grub_cpuid (1, fms, ign, ign, ign); + platform_id = grub_rdmsr (GRUB_MSR_X86_PLATFORM_ID); + + grub_dprintf ("slaunch", "processor family/model/stepping: 0x%x, " + "platform id: 0x%" PRIxGRUB_UINT64_T "\n", fms, platform_id); + + /* + * Check if chipset fusing is same. Note the DEBUG.FUSE bit in the version + * is 0 when debug fused so the logic below checking a mismatch is valid. + */ + if (!!(ver & GRUB_TXT_VERSION_PROD_FUSED) == + !!(hdr->flags & GRUB_TXT_ACM_FLAG_DEBUG_SIGNED)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("production/debug mismatch between chipset and ACM")); + return 0; + } + + /* Check if chipset vendor/device/revision IDs match */ + chipset_id_list = get_acmod_chipset_list (hdr); + if (chipset_id_list == NULL) + return 0; + + grub_dprintf ("slaunch", "%d SINIT ACM chipset id entries:\n", chipset_id_list->count); + + chipset_id = (struct grub_txt_acm_chipset_id *) ((grub_addr_t) chipset_id_list + sizeof (chipset_id_list->count)); + for (i = 0; i < chipset_id_list->count; i++, chipset_id++) + { + grub_dprintf ("slaunch", " vendor: 0x%x, device: 0x%x, flags: 0x%x, " + "revision: 0x%x, extended: 0x%x\n", chipset_id->vendor_id, + chipset_id->device_id, chipset_id->flags, + chipset_id->revision_id, chipset_id->extended_id); + + if (didvid_matches (didvid, chipset_id)) + break; + } + + if (i >= chipset_id_list->count) + { + /* + * Version 9 introduces flexible ACM information table format, not yet + * supported by this code. + * + * TXT spec says that 9 will be the final version and further changes will + * be reflected elsewhere, but check for higher values too in case they + * change their mind. + */ + if (info_table->version >= 9) + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("chipset id mismatch, flexible ACM info list may contain" + " matching entry but it isn't yet supported by code")); + else + grub_error (GRUB_ERR_BAD_DEVICE, N_("chipset id mismatch")); + + return 0; + } + + /* + * Unfortunately the spec isn't too clear on what the changes to the info + * table were, across the different versions, but an old version of the entire + * spec document shows that the processor table field didn't exist when the + * latest version of the info table was 3. + */ + if (info_table->version < 4) + return 1; + + /* Check if processor family/model/stepping and platform IDs match */ + proc_id_list = get_acmod_processor_list(hdr); + if (proc_id_list == NULL) + return 0; + + grub_dprintf ("slaunch", "%d SINIT ACM processor id entries:\n", proc_id_list->count); + + proc_id = (struct grub_txt_acm_processor_id *) ((grub_addr_t) proc_id_list + sizeof (proc_id_list->count)); + for (i = 0; i < proc_id_list->count; i++, proc_id++) + { + grub_dprintf ("slaunch", " fms: 0x%x, fms_mask: 0x%x, platform_id: 0x%" PRIxGRUB_UINT64_T + ", platform_mask: 0x%" PRIxGRUB_UINT64_T "\n", proc_id->fms, proc_id->fms_mask, + proc_id->platform_id, proc_id->platform_mask); + + if ((proc_id->fms == (fms & proc_id->fms_mask)) && + (proc_id->platform_id == (platform_id & proc_id->platform_mask))) + break; + } + + if (i >= proc_id_list->count) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("processor id mismatch")); + return 0; + } + + return 1; +} + +/* + * Choose between the BIOS-provided and user-provided SINIT ACMs, and copy the + * chosen module to the SINIT memory. + */ +struct grub_txt_acm_header * +grub_txt_sinit_select (struct grub_txt_acm_header *sinit) +{ + struct grub_txt_acm_header *bios_sinit; + void *sinit_region_base; + grub_uint32_t sinit_size, sinit_region_size; + + sinit_region_base = (void *)(grub_addr_t) grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_BASE); + sinit_region_size = (grub_uint32_t) grub_txt_reg_pub_read32 (GRUB_TXT_SINIT_SIZE); + + grub_dprintf ("slaunch", "TXT.SINIT.BASE: %p\nTXT.SINIT.SIZE: 0x%" + PRIxGRUB_UINT32_T "\n", sinit_region_base, sinit_region_size); + + if (sinit_region_base == NULL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("no SINIT ACM final resting place")); + return NULL; + } + + if (((grub_addr_t) sinit_region_base & ((1 << GRUB_PAGE_SHIFT) - 1)) != 0) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM base not properly aligned")); + return NULL; + } + + if (sinit != NULL) + grub_dprintf ("slaunch", "SINIT ACM date: %" PRIxGRUB_UINT32_T "\n", sinit->date); + + bios_sinit = get_bios_sinit (sinit_region_base); + + /* Does BIOS provide SINIT ACM? */ + if (bios_sinit != NULL) + { + grub_dprintf ("slaunch", "BIOS SINIT ACM date: %" PRIxGRUB_UINT32_T "\n", + bios_sinit->date); + + if (sinit == NULL) + { + grub_dprintf ("slaunch", "no SINIT ACM provided. Using BIOS SINIT ACM\n"); + return bios_sinit; + } + + if (bios_sinit->date >= sinit->date) + { + grub_dprintf ("slaunch", "BIOS provides newer or same SINIT ACM, so, using BIOS one\n"); + return bios_sinit; + } + + grub_dprintf ("slaunch", "BIOS provides older SINIT ACM, so, ignoring BIOS one\n"); + } + + /* Fail if there is no SINIT ACM. */ + if (sinit == NULL) + return NULL; + + /* Our SINIT ACM is newer than BIOS one or BIOS does not have one. */ + + if (grub_mul (sinit->size, 4, &sinit_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("SINIT ACM size in bytes overflows")); + return NULL; + } + + if (sinit_size > sinit_region_size) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("SINIT ACM does not fit into final resting place: 0x%" + PRIxGRUB_UINT32_T "\n"), sinit_size); + return NULL; + } + + grub_memcpy (sinit_region_base, sinit, sinit_size); + + return sinit_region_base; +} From 4ce728ad6aebf5e3dbb1915ee24be8664218faf0 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 14:46:17 -0400 Subject: [PATCH 15/24] 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 | 277 +++++++++++++++++++++++++++++ 1 file changed, 277 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..26e21d7ec --- /dev/null +++ b/grub-core/loader/i386/txt/verify.c @@ -0,0 +1,277 @@ +/* + * verify.c: verify that platform and processor supports Intel(r) TXT + * + * Copyright (c) 2003-2010, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Oracle and/or its affiliates. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Current max that the secure launch can handle */ +#define TXT_MAX_CPUS 512 + +static grub_err_t +verify_bios_spec_ver_elt (struct grub_txt_heap_ext_data_element *elt) +{ + if (elt->size != GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_bios_spec_ver_element)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_BIOS_SPEC_VER element has wrong size (%d)"), + elt->size); + + /* Any values are allowed */ + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_acm_elt (struct grub_txt_heap_ext_data_element *elt) +{ + grub_uint64_t acm_addrs_size; + grub_uint32_t i; + + if (elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_acm_element) || + grub_mul (elt->acm.num_acms, sizeof (grub_uint64_t), &acm_addrs_size) || + elt->size - (GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + sizeof (elt->acm)) != + acm_addrs_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element has wrong size (%d)"), + elt->size); + + /* No addrs is not error, but print warning. */ + if (elt->acm.num_acms == 0) + grub_printf ("WARNING: HEAP_ACM element has no ACM addrs\n"); + + for (i = 0; i < elt->acm.num_acms; i++) + { + if (elt->acm.addr[i] == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element ACM addr (%d) is NULL"), i); + + if (elt->acm.addr[i] >= 0x100000000UL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_ACM element ACM addr (%d) is >4GB"), i); + + /* Not going to check if ACM addrs are valid ACMs */ + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_custom_elt (struct grub_txt_heap_ext_data_element *elt) +{ + if (elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE + + sizeof (struct grub_txt_heap_custom_element)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("HEAP_CUSTOM element has wrong size (%d)"), + elt->size); + + /* Any values are allowed */ + + return GRUB_ERR_NONE; +} + +static grub_err_t +verify_ext_data_elts(struct grub_txt_heap_ext_data_element *elts, + grub_uint64_t elts_size) +{ + struct grub_txt_heap_ext_data_element *elt = elts; + grub_err_t err; + + if (elts_size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("TXT heap ext data elements too small")); + + for ( ; ; ) + { + if (elts_size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE || + elt->size < GRUB_TXT_HEAP_ELEMENT_HEADER_SIZE || + elts_size < elt->size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("TXT heap invalid element size: type: %d, size: %d"), + elt->type, elt->size); + + switch (elt->type) + { + case GRUB_TXT_HEAP_EXTDATA_TYPE_END: + return GRUB_ERR_NONE; + case GRUB_TXT_HEAP_EXTDATA_TYPE_BIOS_SPEC_VER: + err = verify_bios_spec_ver_elt (elt); + if (err != GRUB_ERR_NONE) + return err; + break; + case GRUB_TXT_HEAP_EXTDATA_TYPE_ACM: + err = verify_acm_elt (elt); + if (err != GRUB_ERR_NONE) + return err; + break; + case GRUB_TXT_HEAP_EXTDATA_TYPE_STM: + /* Nothing to check, platform specific */ + break; + case GRUB_TXT_HEAP_EXTDATA_TYPE_CUSTOM: + err = verify_custom_elt (elt); + if (err != GRUB_ERR_NONE) + return err; + break; + /* These shouldn't be present in BIOS data, treat them as errors */ + case GRUB_TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR: + case GRUB_TXT_HEAP_EXTDATA_TYPE_MADT: + case GRUB_TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1: + case GRUB_TXT_HEAP_EXTDATA_TYPE_MCFG: + default: + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("unknown element: type: %u, size: %u\n"), + elt->type, elt->size); + } + + elts_size -= elt->size; + elt = (struct grub_txt_heap_ext_data_element *)((grub_uint8_t *) elt + elt->size); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_txt_verify_platform (void) +{ + grub_uint8_t *txt_heap; + grub_uint32_t eax, ebx, ecx, edx, errorcode, heap_base, heap_size; + grub_uint64_t bios_size, msr; + grub_err_t err = GRUB_ERR_NONE; + struct grub_txt_bios_data *bios_data; + struct grub_txt_heap_ext_data_element *elts; + + grub_cpuid (GRUB_X86_CPUID_FEATURES, eax, ebx, ecx, edx); + + if (!(ecx & GRUB_X86_CPUID_FEATURES_ECX_SMX)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("CPU does not support SMX")); + + msr = grub_rdmsr (GRUB_MSR_X86_FEATURE_CONTROL); + + if ((msr & (GRUB_MSR_X86_SENTER_FUNCTIONS | GRUB_MSR_X86_SENTER_ENABLE)) != + (GRUB_MSR_X86_SENTER_FUNCTIONS | GRUB_MSR_X86_SENTER_ENABLE)) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("GETSEC[SENTER] is not enabled")); + + /* + * TODO + * TXT Specification + * 4.5 SGX Requirement for TXT Platform + * Secure Launch currently does not support interop with SGX since it does + * not have TPM support to write the SE NVRAM index. + * Eventually need the verify_IA32_se_svn_status routine to be called here. + */ + + errorcode = grub_txt_reg_pub_read32 (GRUB_TXT_ERRORCODE); + /* 0 - no previous SENTER, 0xC0000001 - previous SENTER succeeded */ + if (errorcode != 0 && errorcode != 0xC0000001) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT_ERRORCODE reports failure: 0x%08" PRIxGRUB_UINT32_T), + errorcode); + + if (grub_txt_reg_pub_read8 (GRUB_TXT_ESTS) & GRUB_TXT_ESTS_TXT_RESET) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT_RESET.STS is set and GETSEC[SENTER] is disabled")); + + /* + * Verify that the BIOS information in the TXT heap that was setup by the + * BIOS ACM is reasonable. + */ + + txt_heap = grub_txt_get_heap (); + heap_base = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_BASE); + heap_size = grub_txt_reg_pub_read32 (GRUB_TXT_HEAP_SIZE); + + if (txt_heap == NULL || heap_base == 0 || heap_size == 0) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("TXT heap is not configured correctly")); + + bios_size = grub_txt_bios_data_size (txt_heap); + if (bios_size < sizeof (grub_uint64_t) + sizeof (*bios_data) || + bios_size > heap_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("invalid size of the TXT heap BIOS data table")); + + bios_data = grub_txt_bios_data_start (txt_heap); + + /* Check version */ + if (bios_data->version < 3) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("unsupported BIOS data version (%d)"), bios_data->version); + + if (bios_data->num_logical_procs > TXT_MAX_CPUS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("BIOS reports too many CPUs for secure launch (%d)"), + bios_data->num_logical_procs); + + /* + * grub_uint32_t mle_flags is supposed to be added in version 5, however, the + * only ACM in 630744_003 package that is version 4 (Sandy Bridge & Ivy Bridge + * SNB_IVB_SINIT_20190708_PW.bin) seems to also have this field, or at least + * a placeholder for it. + */ + if (bios_data->version >= 4 && bios_size > sizeof (*bios_data) + sizeof (bios_size)) + { + elts = (struct grub_txt_heap_ext_data_element *) ((grub_uint8_t *) bios_data + + sizeof (*bios_data)); + err = verify_ext_data_elts(elts, bios_size - sizeof (*bios_data)); + } + + return err; +} From 442d234d5782bb6583405af14798f58b0cd2ce24 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/24] 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 | 33 ++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/i386/txt/txt.c b/grub-core/loader/i386/txt/txt.c index ece482ec7..6a9bca685 100644 --- a/grub-core/loader/i386/txt/txt.c +++ b/grub-core/loader/i386/txt/txt.c @@ -530,6 +530,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 grub_txt_event_log_container *elog; + + if (buf == NULL || size == 0) + return; + + /* For TPM 2.0 just clear the area, only TPM 1.2 requires initialization. */ + grub_memset (buf, 0, size); + + if (grub_get_tpm_ver () != GRUB_TPM_12) + return; + + elog = (struct grub_txt_event_log_container *) buf; + + grub_memcpy ((void *) elog->signature, EVTLOG_SIGNATURE, sizeof (elog->signature)); + elog->container_ver_major = EVTLOG_CNTNR_MAJOR_VER; + elog->container_ver_minor = EVTLOG_CNTNR_MINOR_VER; + elog->pcr_event_ver_major = EVTLOG_EVT_MAJOR_VER; + elog->pcr_event_ver_minor = EVTLOG_EVT_MINOR_VER; + elog->size = size; + elog->pcr_events_offset = sizeof (*elog); + elog->next_event_offset = sizeof (*elog); +} + static void setup_txt_slrt_entry (struct grub_slaunch_params *slparams, struct grub_txt_os_mle_data *os_mle_data) @@ -702,6 +728,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 @@ -716,6 +744,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. @@ -733,10 +780,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 53e4b788c..ca4ce67ef 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 @@ -678,6 +679,36 @@ 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 grub_tpm12_pcr_event +{ + grub_uint32_t pcr_index; + grub_uint32_t type; + grub_uint8_t digest[SHA1_DIGEST_SIZE]; + grub_uint32_t data_size; + grub_uint8_t data[]; +} GRUB_PACKED; + +#define EVTLOG_SIGNATURE "TXT Event Container" +#define EVTLOG_CNTNR_MAJOR_VER 1 +#define EVTLOG_CNTNR_MINOR_VER 0 +#define EVTLOG_EVT_MAJOR_VER 1 +#define EVTLOG_EVT_MINOR_VER 0 + +struct grub_txt_event_log_container +{ + grub_uint8_t signature[20]; + grub_uint8_t reserved[12]; + grub_uint8_t container_ver_major; + grub_uint8_t container_ver_minor; + grub_uint8_t pcr_event_ver_major; + grub_uint8_t pcr_event_ver_minor; + grub_uint32_t size; + grub_uint32_t pcr_events_offset; + grub_uint32_t next_event_offset; + struct grub_tpm12_pcr_event pcr_events[]; +} GRUB_PACKED; + extern grub_uint32_t grub_txt_supported_os_sinit_data_ver (struct grub_txt_acm_header *hdr); extern grub_uint32_t grub_txt_get_sinit_capabilities (struct grub_txt_acm_header *hdr); @@ -691,6 +722,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 ff727196a6c5298fcdb59bdd586e9fe169f25d9d Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 7 Aug 2019 15:01:00 -0400 Subject: [PATCH 17/24] i386/slaunch: Add secure launch framework and commands Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- 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 | 314 ++++++++++++++++++- grub-core/loader/i386/pc/plan9.c | 3 +- grub-core/loader/i386/slaunch.c | 308 ++++++++++++++++++ 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, 671 insertions(+), 14 deletions(-) create mode 100644 grub-core/loader/i386/slaunch.c diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index aa172391f..fae4d8819 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -105,6 +105,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h endif if COND_i386_xen_pvh @@ -124,6 +126,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h @@ -186,6 +190,8 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/slaunch.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/slr_table.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index fc4712cdf..32b2eb95e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1912,6 +1912,15 @@ module = { common = lib/cmdline.c; }; +module = { + name = slaunch; + x86 = loader/i386/slaunch.c; + x86 = loader/i386/txt/txt.c; + x86 = loader/i386/txt/acmod.c; + x86 = loader/i386/txt/verify.c; + enable = x86; +}; + module = { name = fdt; efi = loader/efi/fdt.c; @@ -2619,6 +2628,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 8075ac9b0..a8f558587 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -793,6 +794,7 @@ grub_freebsd_boot (void) #endif grub_memcpy (&stack[9], &bi, sizeof (bi)); + state.edi = SLP_NONE; state.eip = entry; state.esp = stack_target; state.ebp = stack_target; @@ -908,6 +910,7 @@ grub_openbsd_boot (void) return err; #endif + state.edi = SLP_NONE; state.eip = entry; state.ebp = state.esp = ((grub_uint8_t *) stack - (grub_uint8_t *) buf0) + buf_target; @@ -1230,6 +1233,7 @@ grub_netbsd_boot (void) return err; #endif + state.edi = SLP_NONE; state.eip = entry; state.esp = stack_target; state.ebp = stack_target; diff --git a/grub-core/loader/i386/coreboot/chainloader.c b/grub-core/loader/i386/coreboot/chainloader.c index 4a5179806..215356a0a 100644 --- a/grub-core/loader/i386/coreboot/chainloader.c +++ b/grub-core/loader/i386/coreboot/chainloader.c @@ -33,6 +33,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -47,6 +48,7 @@ grub_chain_boot (void) grub_video_set_mode ("text", 0, 0); state.eip = entry; + state.edi = SLP_NONE; return grub_relocator32_boot (relocator, state, 0); } diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 90121e9bc..787725284 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -34,10 +34,13 @@ #include #include #include +#include +#include #include #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -64,18 +67,36 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define ACCEPTS_PURE_TEXT 1 #endif +/* See kernel_info in Documentation/arch/x86/boot.rst in the kernel tree */ +#define KERNEL_INFO_HEADER "LToP" +#define KERNEL_INFO_MIN_SIZE_TOTAL 12 + +struct linux_params_efi_info +{ + grub_uint32_t efi_signature; + grub_uint32_t efi_system_table; + grub_uint32_t efi_mem_desc_size; + grub_uint32_t efi_mem_desc_version; + grub_uint32_t efi_mmap; + grub_uint32_t efi_mmap_size; + grub_uint32_t efi_system_table_hi; + grub_uint32_t efi_mmap_hi; +}; + static grub_dl_t my_mod; static grub_size_t linux_mem_size; static int loaded; static void *prot_mode_mem; static grub_addr_t prot_mode_target; +static grub_size_t prot_file_size; static void *initrd_mem; static grub_addr_t initrd_mem_target; static grub_size_t prot_init_space; static struct grub_relocator *relocator = NULL; static void *efi_mmap_buf; static grub_size_t maximal_cmdline_size; +static struct linux_kernel_info *linux_info; static struct linux_kernel_params linux_params; static char *linux_cmdline; #ifdef GRUB_MACHINE_EFI @@ -99,6 +120,8 @@ static struct idt_descriptor idt_desc = }; #endif +#define OFFSET_OF(x, y) ((grub_size_t) ((grub_uint8_t *) (&(y)->x) - (grub_uint8_t *) (y))) + static inline grub_size_t page_align (grub_size_t size) { @@ -151,11 +174,38 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, grub_uint64_t preferred_address) { grub_err_t err; + grub_size_t total_size; + struct grub_slaunch_params *slparams = grub_slaunch_params(); if (prot_size == 0) prot_size = 1; - prot_size = page_align (prot_size); + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + if (prot_size > GRUB_TXT_MLE_MAX_SIZE) + { + err = GRUB_ERR_OUT_OF_RANGE; + goto fail; + } + + /* Check performed above makes sure that this doesn't overflow. */ + prot_size = ALIGN_UP (prot_size, GRUB_TXT_PMR_ALIGN); + + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (prot_size); + slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); + /* Do not go below GRUB_TXT_PMR_ALIGN. */ + preferred_address = (preferred_address > slparams->mle_ptab_size) ? + (preferred_address - slparams->mle_ptab_size) : GRUB_TXT_PMR_ALIGN; + preferred_address = ALIGN_UP (preferred_address, GRUB_TXT_PMR_ALIGN); + } + else + { + prot_size = page_align (prot_size); + slparams->mle_ptab_size = 0; + } + + /* Protected mode code immediately follows MLE page table. */ + total_size = slparams->mle_ptab_size + prot_size; /* Initialize the memory pointers with NULL for convenience. */ free_pages (); @@ -177,15 +227,15 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, err = grub_relocator_alloc_chunk_align (relocator, &ch, preferred_address, preferred_address, - prot_size, 1, + total_size, 1, GRUB_RELOCATOR_PREFERENCE_LOW, 1); for (; err && *align + 1 > min_align; (*align)--) { grub_errno = GRUB_ERR_NONE; err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, - UP_TO_TOP32 (prot_size), - prot_size, 1 << *align, + UP_TO_TOP32 (total_size), + total_size, 1 << *align, GRUB_RELOCATOR_PREFERENCE_LOW, 1); } @@ -195,11 +245,80 @@ 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); + + if (grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_MLE_AP_WAKE_BLOCK_SIZE, + GRUB_MLE_AP_WAKE_BLOCK_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1)) + goto fail; + + slparams->ap_wake_block = get_physical_target_address (ch); + slparams->ap_wake_block_size = GRUB_MLE_AP_WAKE_BLOCK_SIZE; + + grub_memset (get_virtual_current_address (ch), 0, slparams->ap_wake_block_size); + + grub_dprintf ("linux", "ap_wake_block = %lx, ap_wake_block_size = %lx\n", + (unsigned long) slparams->ap_wake_block, + (unsigned long) slparams->ap_wake_block_size); + } } grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n", @@ -399,6 +518,63 @@ grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size, return 0; } +static void +grub_linux_setup_slr_table (struct grub_slaunch_params *slparams) +{ + struct linux_kernel_params *boot_params = (void *) (grub_addr_t) slparams->boot_params_addr; + struct linux_params_efi_info *efi_info; + + /* A bit of work to extract the v2.08 EFI info from the linux params */ + efi_info = (void *)((grub_uint8_t *)&boot_params->v0208 + 2*sizeof(grub_uint32_t)); + + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_BOOT_PARAMS, + /*flags=*/0, + (grub_addr_t) boot_params, + GRUB_PAGE_SIZE, + "Measured boot parameters"); + + if (boot_params->setup_data) + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_SETUP_DATA, + GRUB_SLR_POLICY_IMPLICIT_SIZE, + boot_params->setup_data, + /*size=*/0, + "Measured Kernel setup_data"); + + /* The cmdline ptr can have hi bits but GRUB puts it always < 4G */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_CMDLINE, + /*flags=*/0, + boot_params->cmd_line_ptr, + boot_params->cmdline_size, + "Measured Kernel command line"); + + if (!grub_memcmp(&efi_info->efi_signature, "EL64", sizeof(grub_uint32_t))) + { + grub_uint64_t mmap_addr = + ((grub_uint64_t) efi_info->efi_mmap_hi << 32) | efi_info->efi_mmap; + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_UEFI_MEMMAP, + /*flags=*/0, + mmap_addr, + efi_info->efi_mmap_size, + "Measured EFI memory map"); + } + + if (boot_params->ramdisk_image) + /* + * The initrd image and size can have hi bits but in GRUB it is always + * < 4G, see GRUB_LINUX_INITRD_MAX_ADDRESS in grub_cmd_initrd(). + */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_CODE_PCR, + GRUB_SLR_ET_RAMDISK, + /*flags=*/0, + boot_params->ramdisk_image, + boot_params->ramdisk_size, + "Measured Kernel initrd"); +} + static grub_err_t grub_linux_boot (void) { @@ -412,6 +588,7 @@ grub_linux_boot (void) }; grub_size_t mmap_size; grub_size_t cl_offset; + struct grub_slaunch_params *slparams = grub_slaunch_params(); #ifdef GRUB_MACHINE_IEEE1275 { @@ -588,6 +765,8 @@ grub_linux_boot (void) ctx.params->secure_boot = grub_efi_get_secureboot (); + grub_dprintf ("linux", "EFI exit boot services\n"); + err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL, &efi_desc_size, &efi_desc_version); if (err) @@ -637,12 +816,38 @@ grub_linux_boot (void) } #endif - /* FIXME. */ - /* asm volatile ("lidt %0" : : "m" (idt_desc)); */ - state.ebp = state.edi = state.ebx = 0; - state.esi = ctx.real_mode_target; - state.esp = ctx.real_mode_target; - state.eip = ctx.params->code32_start; + state.edi = grub_slaunch_platform_type (); + + if (state.edi == SLP_INTEL_TXT) + { + slparams->boot_params_addr = ctx.real_mode_target; + + err = grub_txt_boot_prepare (slparams); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "TXT boot preparation failed"); + + grub_slaunch_add_slrt_policy_entries (); + grub_txt_add_slrt_policy_entries (); + grub_linux_setup_slr_table (slparams); + grub_slaunch_finish_slr_table (); + + /* Configure relocator GETSEC[SENTER] call. */ + state.eax = GRUB_SMX_LEAF_SENTER; + state.ebx = slparams->dce_base; + state.ecx = slparams->dce_size; + state.edx = 0; + } + else + { + /* FIXME. */ + /* asm volatile ("lidt %0" : : "m" (idt_desc)); */ + state.ebp = state.edi = state.ebx = 0; + state.esi = ctx.real_mode_target; + state.esp = ctx.real_mode_target; + state.eip = ctx.params->code32_start; + } + return grub_relocator32_boot (relocator, state, 0); } @@ -664,13 +869,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), struct linux_i386_kernel_header lh; grub_uint8_t *linux_params_ptr; grub_uint8_t setup_sects; - grub_size_t real_size, prot_size, prot_file_size, kernel_offset; + grub_size_t real_size, prot_size, kernel_offset; grub_ssize_t len; int i; grub_size_t align, min_align; int relocatable; grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR; grub_uint8_t *kernel = NULL; + struct grub_slaunch_params *slparams = grub_slaunch_params(); grub_dl_ref (my_mod); @@ -786,6 +992,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), prot_init_space = page_align (prot_size) * 3; } + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + /* PMRs require GRUB_TXT_PMR_ALIGN_SHIFT aligments. */ + min_align = grub_max (min_align, GRUB_TXT_PMR_ALIGN_SHIFT); + align = grub_max (align, GRUB_TXT_PMR_ALIGN_SHIFT); + } + if (allocate_pages (prot_size, &align, min_align, relocatable, preferred_address)) @@ -793,6 +1006,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memset (&linux_params, 0, sizeof (linux_params)); + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + grub_txt_setup_mle_ptab (slparams); + /* * The Linux 32-bit boot protocol defines the setup header end * to be at 0x202 + the byte value at 0x201. @@ -822,6 +1038,80 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X); + /* Read the kernel_info struct. */ + if (grub_le_to_cpu16 (lh.version) >= 0x020f) + { + if (grub_file_seek (file, (grub_off_t) grub_le_to_cpu32 (lh.kernel_info_offset) + + real_size + GRUB_DISK_SECTOR_SIZE) == ((grub_off_t) -1)) + goto fail; + + linux_info = grub_malloc (KERNEL_INFO_MIN_SIZE_TOTAL); + + if (!linux_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate memory for kernel_info")); + goto fail; + } + + /* Load minimal kernel_info struct. */ + if (grub_file_read (file, linux_info, + KERNEL_INFO_MIN_SIZE_TOTAL) != KERNEL_INFO_MIN_SIZE_TOTAL) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + if (grub_memcmp (&linux_info->header, KERNEL_INFO_HEADER, sizeof (linux_info->header))) + { + grub_error (GRUB_ERR_BAD_OS, N_("incorrect kernel_info header")); + goto fail; + } + + linux_info->size_total = grub_le_to_cpu32 (linux_info->size_total); + if (linux_info->size_total < KERNEL_INFO_MIN_SIZE_TOTAL) + { + grub_error (GRUB_ERR_BAD_OS, N_("incorrect kernel_info size")); + goto fail; + } + + linux_info = grub_realloc (linux_info, linux_info->size_total); + + if (!linux_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot reallocate memory for kernel_info")); + goto fail; + } + + /* Load the rest of kernel_info struct. */ + if (grub_file_read (file, &linux_info->setup_type_max, + linux_info->size_total - KERNEL_INFO_MIN_SIZE_TOTAL) != + (grub_ssize_t)(linux_info->size_total - KERNEL_INFO_MIN_SIZE_TOTAL)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + if (grub_slaunch_platform_type () != SLP_NONE) + { + if (OFFSET_OF (mle_header_offset, linux_info) >= + grub_le_to_cpu32 (linux_info->size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: lack of mle_header_offset")); + goto fail; + } + + slparams->mle_header_offset = grub_le_to_cpu32 (linux_info->mle_header_offset); + } + } + else if (grub_slaunch_platform_type () != SLP_NONE) + { + grub_error (GRUB_ERR_BAD_OS, N_("not slaunch kernel: boot protocol too old")); + goto fail; + } + linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; linux_params.kernel_alignment = (1 << align); linux_params.ps_mouse = linux_params.padding11 = 0; diff --git a/grub-core/loader/i386/pc/plan9.c b/grub-core/loader/i386/pc/plan9.c index 960e866f4..9ec461c75 100644 --- a/grub-core/loader/i386/pc/plan9.c +++ b/grub-core/loader/i386/pc/plan9.c @@ -34,6 +34,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -84,7 +85,7 @@ grub_plan9_boot (void) .ebx = 0, .ecx = 0, .edx = 0, - .edi = 0, + .edi = SLP_NONE, .esp = 0, .ebp = 0, .esi = 0 diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c new file mode 100644 index 000000000..824b5c23b --- /dev/null +++ b/grub-core/loader/i386/slaunch.c @@ -0,0 +1,308 @@ +/* + * 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_base = slparams.mle_start; + slr_dl_info_staging.dlme_size = slparams.mle_size; + slr_dl_info_staging.dlme_entry = mle_header->entry_point; + + slr_log_info_staging.hdr.tag = GRUB_SLR_ENTRY_LOG_INFO; + slr_log_info_staging.hdr.size = sizeof(struct grub_slr_entry_log_info); + slr_log_info_staging.addr = slparams.tpm_evt_log_base; + slr_log_info_staging.size = slparams.tpm_evt_log_size; + slr_log_info_staging.format = + (grub_get_tpm_ver () == GRUB_TPM_20) ? + GRUB_SLR_DRTM_TPM20_LOG : GRUB_SLR_DRTM_TPM12_LOG; + + slr_policy_staging->hdr.tag = GRUB_SLR_ENTRY_DRTM_POLICY; + slr_policy_staging->hdr.size = sizeof(struct grub_slr_entry_policy); + slr_policy_staging->revision = GRUB_SLR_TABLE_REVISION; + slr_policy_staging->nr_entries = 0; +} + +void grub_slaunch_add_slrt_policy_entries (void) +{ + /* The SLR table should be measured too, at least parts of it. */ + grub_slaunch_add_slrt_policy_entry (GRUB_SLAUNCH_DATA_PCR, + GRUB_SLR_ET_SLRT, + GRUB_SLR_POLICY_IMPLICIT_SIZE, + slparams.slr_table_base, + /*size=*/0, + "Measured SLR Table"); +} + +void +grub_slaunch_add_slrt_policy_entry (grub_uint16_t pcr, + grub_uint16_t entity_type, + grub_uint16_t flags, + grub_uint64_t entity, + grub_uint64_t size, + const char *evt_info) +{ + struct grub_slr_policy_entry *entry = + (void *)((grub_uint8_t *) slr_policy_staging + + sizeof(struct grub_slr_entry_policy) + + slr_policy_staging->nr_entries*sizeof(*entry)); + + if (slr_policy_staging->hdr.size > sizeof(slr_policy_buf) - sizeof(*entry)) + grub_fatal ("Not enough space for adding policy entry: %s! The buffer is full.", + evt_info); + + entry->pcr = pcr; + entry->entity_type = entity_type; + entry->flags = flags; + entry->entity = entity; + entry->size = size; + + grub_strncpy (entry->evt_info, evt_info, sizeof(entry->evt_info) - 1); + entry->evt_info[sizeof(entry->evt_info) - 1] = '\0'; + + slr_policy_staging->hdr.size += sizeof(*entry); + ++slr_policy_staging->nr_entries; +} + +void +grub_slaunch_finish_slr_table (void) +{ + struct grub_slr_table *slr_table = slparams.slr_table_mem; + + grub_slr_add_entry (slr_table, &slr_dl_info_staging.hdr); + grub_slr_add_entry (slr_table, &slr_log_info_staging.hdr); + grub_slr_add_entry (slr_table, &slr_policy_staging->hdr); +} + +static grub_err_t +grub_cmd_slaunch (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_uint32_t manufacturer[3]; + grub_uint32_t eax; + 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; + void *new_module = NULL; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected: filename")); + + if (slp == SLP_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("secure launch not enabled")); + + if (slp != SLP_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; + } + + new_module = grub_malloc (size); + + if (new_module == NULL) + goto fail; + + if (grub_file_read (file, new_module, size) != size) + { + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file: %s"), + argv[0]); + goto fail; + } + + if (slp == SLP_INTEL_TXT) + { + if (!grub_txt_is_sinit_acmod (new_module, size)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SINIT ACM")); + goto fail; + } + + if (!grub_txt_acmod_match_platform (new_module)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("SINIT ACM does not match platform")); + goto fail; + } + } + + grub_file_close (file); + + grub_free (slaunch_module); + slaunch_module = new_module; + + return GRUB_ERR_NONE; + +fail: + grub_error_push (); + + grub_free (new_module); + grub_file_close (file); + + grub_error_pop (); + + return grub_errno; +} + +static grub_err_t +grub_cmd_slaunch_state (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + if (slp == SLP_NONE) + grub_printf ("Secure launcher: Disabled\n"); + else if (slp == SLP_INTEL_TXT) + { + grub_printf ("Secure launcher: Intel TXT\n"); + grub_txt_state_show (); + } + else + 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 b91e2f840..ba73d909a 100644 --- a/grub-core/loader/i386/xnu.c +++ b/grub-core/loader/i386/xnu.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -802,6 +803,7 @@ grub_xnu_boot_resume (void) { struct grub_relocator32_state state = {0}; + state.edi = SLP_NONE; state.esp = grub_xnu_stack; state.ebp = grub_xnu_stack; state.eip = grub_xnu_entry_point; @@ -1129,6 +1131,7 @@ grub_xnu_boot (void) grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size, descriptor_version, memory_map); + state.edi = SLP_NONE; state.eip = grub_xnu_entry_point; state.eax = grub_xnu_arg1; state.esp = grub_xnu_stack; diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 94c112a29..367e7273b 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -51,6 +51,9 @@ #include #include #include +#if defined (__i386__) || defined (__x86_64__) +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -162,6 +165,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 d678de063..2bd9e3364 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -134,6 +134,9 @@ enum grub_file_type GRUB_FILE_TYPE_VERIFY_SIGNATURE, + /* Secure Launch module. */ + GRUB_FILE_TYPE_SLAUNCH_MODULE, + GRUB_FILE_TYPE_MASK = 0xffff, /* --skip-sig is specified. */ diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index 461097781..99e9d6cba 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -163,6 +163,17 @@ struct linux_i386_kernel_header grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; + grub_uint32_t kernel_info_offset; +} GRUB_PACKED; + +struct linux_kernel_info +{ + grub_uint32_t header; + grub_uint32_t size; /* In bytes, excluding var_len_data[] */ + grub_uint32_t size_total; /* In bytes, including var_len_data[] */ + grub_uint32_t setup_type_max; + grub_uint32_t mle_header_offset; + grub_uint8_t var_len_data[]; } GRUB_PACKED; /* Boot parameters for Linux based on 2.6.12. This is used by the setup @@ -345,9 +356,10 @@ struct linux_kernel_params grub_uint64_t pref_address; grub_uint32_t init_size; grub_uint32_t handover_offset; + grub_uint32_t kernel_info_offset; /* Linux setup header copy - END. */ - grub_uint8_t _pad7[40]; + grub_uint8_t _pad7[36]; grub_uint32_t edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 290 */ struct grub_e820_mmap e820_map[(0x400 - 0x2d0) / 20]; /* 2d0 */ } GRUB_PACKED; From 73804b06404e8f97414d9e906f07c9d1d8c83667 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sun, 4 Aug 2024 21:29:35 +0300 Subject: [PATCH 18/24] loader/i386/linux.c: fix cleanup if kernel doesn't support 64-bit addressing Simply returning didn't free file resource nor called `grub_dl_ref(my_mod)`. Jump to `fail` label instead. Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/linux.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 787725284..c357fa3c2 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -1145,9 +1145,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), #ifdef GRUB_MACHINE_EFI #ifdef __x86_64__ if (grub_le_to_cpu16 (linux_params.version) < 0x0208 && - ((grub_addr_t) grub_efi_system_table >> 32) != 0) - return grub_error(GRUB_ERR_BAD_OS, - "kernel does not support 64-bit addressing"); + ((grub_addr_t) grub_efi_system_table >> 32) != 0) { + grub_errno = grub_error(GRUB_ERR_BAD_OS, + "kernel does not support 64-bit addressing"); + goto fail; + } #endif if (grub_le_to_cpu16 (linux_params.version) >= 0x0208) From a32a034f73037e449cb692f3cf22beec391f3d2e Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Wed, 22 Nov 2023 21:08:35 +0200 Subject: [PATCH 19/24] 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 11a6e224f..f915386e8 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -450,7 +450,7 @@ retrieve_video_parameters (struct multiboot_info *mbi, } grub_err_t -grub_multiboot_make_mbi (grub_uint32_t *target) +grub_multiboot_make_mbi (grub_uint32_t *target, grub_uint32_t *size) { struct multiboot_info *mbi; struct multiboot_mod_list *modlist; @@ -618,6 +618,8 @@ grub_multiboot_make_mbi (grub_uint32_t *target) return err; #endif + *size = (char *) ptrorig - (char *) mbi; + return GRUB_ERR_NONE; } diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 367e7273b..cb65b2b9b 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -181,6 +181,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; @@ -189,7 +190,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 00a48413c..1945b3ac6 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 d8847f753..020c54847 100644 --- a/include/grub/multiboot.h +++ b/include/grub/multiboot.h @@ -41,7 +41,7 @@ void grub_multiboot (int argc, char *argv[]); void grub_module (int argc, char *argv[]); void grub_multiboot_set_accepts_video (int val); -grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target); +grub_err_t grub_multiboot_make_mbi (grub_uint32_t *target, grub_uint32_t *size); void grub_multiboot_free_mbi (void); grub_err_t grub_multiboot_init_mbi (int argc, char *argv[]); grub_err_t grub_multiboot_add_module (grub_addr_t start, grub_size_t size, diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h index b90aa6989..3417a1447 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 17f85345e6280de9e083fa1230d71230a4b669fe 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/24] 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. To avoid mismatch between OS SINIT data MLE size and MLE size from MLE header, take the mle_size from MLE header as is (without aligning it). Signed-off-by: Michał Żygowski Signed-off-by: Tomasz Żyjewski Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- grub-core/loader/multiboot.c | 23 ++++++- grub-core/loader/multiboot_elfxx.c | 88 ++++++++++++++++++++++++++- grub-core/loader/multiboot_mbi2.c | 96 ++++++++++++++++++++++++++++++ include/grub/multiboot2.h | 2 + 4 files changed, 207 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index cb65b2b9b..8d92f4e0c 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -53,6 +53,7 @@ #include #if defined (__i386__) || defined (__x86_64__) #include +#include #endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -165,7 +166,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); } @@ -195,6 +206,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 24480df50..609b27c4d 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -44,6 +44,8 @@ #error "I'm confused" #endif +#include +#include #include #include @@ -71,6 +73,9 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) grub_off_t phlimit; unsigned int i; void *source = NULL; + struct grub_slaunch_params *slparams = grub_slaunch_params(); + grub_uint32_t mle_hdr_offset; + struct grub_txt_mle_header *mle_hdr; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 @@ -125,6 +130,28 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) { load_size = highest_load - mld->link_base_addr; + 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 binary together with the page tables to make one + * contiguous block for MLE. + */ + slparams->mle_ptab_size = grub_txt_get_mle_ptab_size (load_size); + slparams->mle_ptab_size = ALIGN_UP (slparams->mle_ptab_size, GRUB_TXT_PMR_ALIGN); + + /* Do not go below GRUB_TXT_PMR_ALIGN. */ + if (mld->align < GRUB_TXT_PMR_ALIGN) + mld->align = GRUB_TXT_PMR_ALIGN; +#endif + } + else + { + slparams->mle_ptab_size = 0; + } + grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, " "load_size=0x%x, avoid_efi_boot_services=%d\n", (long) mld->align, mld->preference, load_size, @@ -146,9 +173,44 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) mld->load_base_addr = get_physical_target_address (ch); source = get_virtual_current_address (ch); + grub_memset (get_virtual_current_address (ch), 0, load_size); + grub_dprintf ("multiboot_loader", "load_base_addr=0x%lx, source=0x%lx\n", + (long) mld->load_base_addr, (long) source); + + if (grub_slaunch_platform_type () == SLP_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, @@ -211,6 +273,30 @@ 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) + { + mle_hdr = (struct grub_txt_mle_header *)((grub_addr_t)source + mle_hdr_offset); + if ( !grub_memcmp ((void *)mle_hdr->uuid, GRUB_TXT_MLE_UUID, 16) ) + { + slparams->mle_header_offset = mle_hdr_offset; + break; + } + } + + if (slparams->mle_header_offset == 0xffffffff) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "MLE header not found"); + + grub_dprintf ("slaunch", "slparams->mle_header_offset: 0x%08x\n", + slparams->mle_header_offset); + + slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; + } + for (i = 0; i < phnum; i++) if (phdr(i)->p_vaddr <= ehdr->e_entry && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry) diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 1945b3ac6..40b859fb5 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; } @@ -1128,3 +1163,64 @@ 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->boot_params_addr = mbi_target; + + 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 3417a1447..2e843d24e 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 c3002069e21e1021f31877fe1bc1dccffbf8eaf9 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Tue, 10 Nov 2020 13:40:09 +0100 Subject: [PATCH 21/24] i386/skinit: Add AMD SKINIT implementation This adds code for placing of SLB and initialization of SLRT for AMD SKINIT. Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- grub-core/Makefile.core.def | 1 + grub-core/loader/i386/skinit.c | 98 ++++++++++++++++++++++++++++++++++ include/grub/i386/skinit.h | 34 ++++++++++++ 3 files changed, 133 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 32b2eb95e..5fdd922a7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1918,6 +1918,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..d536630a6 --- /dev/null +++ b/grub-core/loader/i386/skinit.c @@ -0,0 +1,98 @@ +/* + * 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"); + + /* + * Contrary to the TXT, on AMD we do not have vendor-provided blobs in + * reserved memory, we are using normal RAM + */ + err = grub_relocator_alloc_chunk_align (rel, &ch, 0, + 0xffffffff - GRUB_SKINIT_SLB_SIZE, + GRUB_SKINIT_SLB_SIZE, + GRUB_SKINIT_SLB_ALIGN, + GRUB_RELOCATOR_PREFERENCE_LOW, 1); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "cannot alloc memory for SLB"); + + /* + * Send INIT IPI to all APs (CPUs other than this one). + * + * "AMD64 Architecture Programmer’s Manual, Rev. 3.42, Vol. 2" says "Depending + * on processor implementation, a fixed delay of no more than 1000 processor + * cycles may be necessary before executing SKINIT to ensure reliable sensing + * of APIC INIT state by the SKINIT." + * + * However, in tests it wasn't always enough (sometimes up to 7000 cycles + * were necessary), so send the IPIs before grub_memcpy()/grub_memset() to + * increase the delay before SKINIT is executed. + */ + grub_dprintf ("slaunch", "broadcasting INIT\r\n"); + *apic = 0x000c0500; + + slparams->dce_base = get_physical_target_address (ch); + slparams->dce_size = SLB_MEASURED (slb); + + dce_mem = get_virtual_current_address (ch); + grub_memcpy (dce_mem, slb, slparams->dce_size); + + slparams->slr_table_base = slparams->dce_base + SLB_PARAM (slb); + slparams->slr_table_size = SLRT_SIZE; + slparams->slr_table_mem = (grub_uint8_t *) dce_mem + SLB_PARAM (slb); + grub_memset (slparams->slr_table_mem, 0, SLRT_SIZE); + + grub_slaunch_init_slrt_storage (GRUB_SLR_AMD_SKINIT); + + grub_dprintf ("slaunch", "grub_tpm_relinquish_locality\r\n"); + grub_tpm_relinquish_locality (0); + + grub_dprintf ("slaunch", "Invoke SKINIT\r\n"); + + return GRUB_ERR_NONE; +} diff --git a/include/grub/i386/skinit.h b/include/grub/i386/skinit.h new file mode 100644 index 000000000..e2d5e4fbf --- /dev/null +++ b/include/grub/i386/skinit.h @@ -0,0 +1,34 @@ +/* + * 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 + +#include +#include + +grub_err_t grub_skinit_boot_prepare (struct grub_relocator *rel, + struct grub_slaunch_params *slparams); + +#endif /* GRUB_I386_SKINIT_H */ From dd76fdacede9fd91a68759989649ce8b2932ea62 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Tue, 10 Nov 2020 13:44:17 +0100 Subject: [PATCH 22/24] i386/slaunch: Add support for AMD SKINIT Extend slaunch command to work on AMD CPUs. 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 | 33 ++++++++++++++++-- include/grub/i386/skinit.h | 2 ++ include/grub/i386/slaunch.h | 1 + 5 files changed, 98 insertions(+), 2 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 d536630a6..0011f3ece 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 > %zd\n", + slb_size, max_size); + return 0; + } + + if (SLB_MEASURED (slb_base) > slb_size) + { + grub_dprintf ("slaunch", "SLB measured size is too large: %d > %d\n", + SLB_MEASURED (slb_base), slb_size); + return 0; + } + + if (SLB_ENTRY (slb_base) >= SLB_MEASURED (slb_base)) + { + grub_dprintf ("slaunch", "SLB entry is not measured: %d >= %d\n", + SLB_ENTRY (slb_base), SLB_MEASURED (slb_base)); + return 0; + } + + if (SLB_INFO (slb_base) > SLB_MEASURED (slb_base) - sizeof(skl_uuid)) + { + grub_dprintf ("slaunch", "SLB info is not measured: %d > %zd\n", + SLB_INFO (slb_base), + SLB_MEASURED (slb_base) - sizeof(skl_uuid)); + return 0; + } + + if (SLB_PARAM (slb_base) > max_size) + { + grub_dprintf ("slaunch", "SLB bootloader data offset is too large: %d > %zd\n", + SLB_PARAM (slb_base), max_size); + return 0; + } + + uuid = (const grub_uint8_t *) slb_base + SLB_INFO (slb_base); + if (grub_memcmp (uuid, skl_uuid, sizeof(skl_uuid)) != 0) + { + grub_dprintf ("slaunch", "SLB has unexpected UUID\n"); + return 0; + } + + return 1; +} + grub_err_t grub_skinit_boot_prepare (struct grub_relocator *rel, struct grub_slaunch_params *slparams) diff --git a/grub-core/loader/i386/slaunch.c b/grub-core/loader/i386/slaunch.c index 824b5c23b..806dd8c8d 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,6 +78,8 @@ 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; @@ -154,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 ()) @@ -176,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")); @@ -196,7 +213,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); @@ -242,6 +259,14 @@ grub_cmd_slaunch_module (grub_command_t cmd __attribute__ ((unused)), goto fail; } } + else if (slp == SLP_AMD_SKINIT) + { + if (!grub_skinit_is_slb (new_module, size)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("it does not look like SLB")); + goto fail; + } + } grub_file_close (file); @@ -273,6 +298,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 e2d5e4fbf..2901dbbb9 100644 --- a/include/grub/i386/skinit.h +++ b/include/grub/i386/skinit.h @@ -28,6 +28,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 From 48e32e88ab1f2b5c93187fcd8102179cb0a00b40 Mon Sep 17 00:00:00 2001 From: Krystian Hebel Date: Wed, 30 Dec 2020 10:15:16 +0100 Subject: [PATCH 23/24] multiboot2: Support AMD SKINIT Signed-off-by: Krystian Hebel Signed-off-by: Sergii Dmytruk --- grub-core/loader/multiboot.c | 11 +++++-- grub-core/loader/multiboot_elfxx.c | 22 ++++++------- grub-core/loader/multiboot_mbi2.c | 52 +++++++++++++++++++----------- include/grub/multiboot2.h | 4 +-- 4 files changed, 55 insertions(+), 34 deletions(-) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 8d92f4e0c..58a8952be 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -52,6 +52,7 @@ #include #include #if defined (__i386__) || defined (__x86_64__) +#include #include #include #endif @@ -177,6 +178,10 @@ 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; + } grub_relocator32_boot (rel, state, 0); } @@ -207,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 609b27c4d..e7d57a57b 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -128,13 +128,15 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) if (mld->relocatable) { +#ifndef GRUB_USE_MULTIBOOT2 + if (grub_slaunch_platform_type () != SLP_NONE) + return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); +#endif + load_size = highest_load - mld->link_base_addr; if (grub_slaunch_platform_type () == SLP_INTEL_TXT) { -#ifndef GRUB_USE_MULTIBOOT2 - return grub_error (GRUB_ERR_BAD_OS, "Only multiboot2 supported for slaunch"); -#else /* * We allocate the binary together with the page tables to make one * contiguous block for MLE. @@ -145,7 +147,6 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) /* Do not go below GRUB_TXT_PMR_ALIGN. */ if (mld->align < GRUB_TXT_PMR_ALIGN) mld->align = GRUB_TXT_PMR_ALIGN; -#endif } else { @@ -177,14 +178,14 @@ 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"); -#else slparams->mle_start = mld->load_base_addr; slparams->mle_mem = source; + } + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch, GRUB_MEMORY_MACHINE_UPPER_START, mld->load_base_addr - slparams->mle_ptab_size, @@ -201,14 +202,13 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) 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; /* 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"); } @@ -273,7 +273,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 40b859fb5..2ab48b8a6 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 @@ -281,7 +282,7 @@ grub_multiboot2_load (grub_file_t file, const char *filename) if (addr_tag) { - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) return grub_error (GRUB_ERR_BAD_OS, "Slaunch not supported with multiboot addr tag"); grub_uint64_t load_addr = (addr_tag->load_addr + 1) @@ -398,7 +399,7 @@ grub_multiboot2_load (grub_file_t file, const char *filename) accepted_consoles, 0, 0, 0, console_required); - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) { grub_relocator_chunk_t ch; struct grub_slaunch_params *slparams = grub_slaunch_params(); @@ -415,6 +416,7 @@ grub_multiboot2_load (grub_file_t file, const char *filename) slparams->tpm_evt_log_base = get_physical_target_address (ch); slparams->tpm_evt_log_size = GRUB_SLAUNCH_TPM_EVT_LOG_SIZE; + /* It's OK to call this for AMD SKINIT because SKL erases the log before use. */ grub_txt_init_tpm_event_log(get_virtual_current_address (ch), slparams->tpm_evt_log_size); @@ -422,7 +424,8 @@ grub_multiboot2_load (grub_file_t file, const char *filename) (unsigned long) slparams->tpm_evt_log_base, (unsigned) slparams->tpm_evt_log_size); - grub_txt_setup_mle_ptab (slparams); + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + grub_txt_setup_mle_ptab (slparams); } return err; @@ -1182,27 +1185,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->boot_params_addr = mbi_target; - slparams->slr_table_base = GRUB_SLAUNCH_STORE_IN_OS2MLE; - slparams->slr_table_size = GRUB_PAGE_SIZE; + 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; + 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, @@ -1211,16 +1224,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 2e843d24e..60d104fed 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 1bfb72e77d651986926133625c3ae4360d98f715 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 17 Aug 2022 17:47:54 +0200 Subject: [PATCH 24/24] i386/linux: Add support for AMD SKINIT Signed-off-by: Ross Philipson Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk --- grub-core/loader/i386/linux.c | 57 ++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index c357fa3c2..0fdb2d2e7 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -251,7 +252,7 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, prot_mode_mem = get_virtual_current_address (ch); prot_mode_target = get_physical_target_address (ch); - if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + if (grub_slaunch_platform_type () != SLP_NONE) { /* Zero out memory to get stable MLE measurements. */ grub_memset (prot_mode_mem, 0, total_size); @@ -270,22 +271,29 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, 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); + /* + * For AMD SKINIT, SLRT is part of SLB and it will be initialized by + * grub_skinit_boot_prepare(). + */ + if (grub_slaunch_platform_type () == SLP_INTEL_TXT) + { + err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, + 0xffffffff - GRUB_PAGE_SIZE, + GRUB_PAGE_SIZE, GRUB_PAGE_SIZE, + GRUB_RELOCATOR_PREFERENCE_NONE, 1); + if (err) + goto fail; + + slparams->slr_table_base = get_physical_target_address (ch); + slparams->slr_table_size = GRUB_PAGE_SIZE; + slparams->slr_table_mem = get_virtual_current_address (ch); + + grub_memset (slparams->slr_table_mem, 0, slparams->slr_table_size); + + grub_dprintf ("linux", "slr_table_base = %lx, slr_table_size = %x\n", + (unsigned long) slparams->slr_table_base, + (unsigned) slparams->slr_table_size); + } err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000, 0xffffffff - GRUB_SLAUNCH_TPM_EVT_LOG_SIZE, @@ -838,6 +846,21 @@ grub_linux_boot (void) state.ecx = slparams->dce_size; state.edx = 0; } + else if (state.edi == SLP_AMD_SKINIT) + { + slparams->boot_params_addr = ctx.real_mode_target; + + err = grub_skinit_boot_prepare (relocator, slparams); + + if (err != GRUB_ERR_NONE) + return grub_error (err, "SKINIT preparations have failed"); + + grub_slaunch_add_slrt_policy_entries (); + grub_linux_setup_slr_table (slparams); + grub_slaunch_finish_slr_table (); + + state.eax = slparams->dce_base; + } else { /* FIXME. */