diff --git a/grub-core/loader/slaunch/slrt.c b/grub-core/loader/slaunch/slrt.c index de5c32cf1..80f62fe0d 100644 --- a/grub-core/loader/slaunch/slrt.c +++ b/grub-core/loader/slaunch/slrt.c @@ -249,6 +249,12 @@ grub_update_slrt_policy (struct grub_slaunch_params *slparams) grub_uint64_t hi_val; int i, next = 0; + if (boot_params == NULL) + { + /* Nothing to update if Linux boot params aren't supplied */ + return; + } + policy = grub_slr_next_entry_by_tag ((struct grub_slr_table *)(grub_addr_t)slparams->slr_table_base, NULL, GRUB_SLR_ENTRY_ENTRY_POLICY); diff --git a/grub-core/loader/slaunch/txt.c b/grub-core/loader/slaunch/txt.c index 5336f72ad..3f9cb5411 100644 --- a/grub-core/loader/slaunch/txt.c +++ b/grub-core/loader/slaunch/txt.c @@ -302,8 +302,6 @@ set_all_mtrrs (int 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 @@ -351,21 +349,25 @@ fls (int mask) } /* - * set the memory type for specified range (base to base+size) - * to mem_type and everything else to UC + * Set the memory type for ACM's memory range 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) +set_mtrr_mem_type (struct grub_txt_acm_header *sinit, 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; + grub_uint32_t max_phy_addr_bits; + grub_uint64_t mtrr_shifted_mask; unsigned long ndx, base_v; int i = 0, j, num_pages, mtrr_s; + const grub_uint8_t *base = (const grub_uint8_t *)sinit; + grub_uint32_t size = sinit->size*4; + /* 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); @@ -390,7 +392,7 @@ set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, base, size, num_pages); /* Each VAR MTRR base must be a multiple of that MTRR's Size */ - base_v = (unsigned long)base; + base_v = (grub_addr_t)base; /* MTRR size in pages */ mtrr_s = 1; @@ -407,16 +409,22 @@ set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, ndx = 0; + if (grub_txt_get_sinit_capabilities (sinit) & GRUB_TXT_CAPS_MAXPHYSADDR_SUPPORT) + max_phy_addr_bits = grub_get_max_phy_addr_bits (); + else + max_phy_addr_bits = 36; + mtrr_shifted_mask = (1ULL << (max_phy_addr_bits - GRUB_PAGE_SHIFT)) - 1; + while ( num_pages >= mtrr_s ) { mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2); - mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & - SINIT_MTRR_MASK; + mtrr_physbase.base = ((grub_addr_t)base >> GRUB_PAGE_SHIFT) & + mtrr_shifted_mask; mtrr_physbase.type = mem_type; grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2, mtrr_physbase.raw); mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2); - mtrr_physmask.mask = ~(mtrr_s - 1) & SINIT_MTRR_MASK; + mtrr_physmask.mask = ~(mtrr_s - 1) & mtrr_shifted_mask; mtrr_physmask.v = 1; grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2, mtrr_physmask.raw); @@ -432,8 +440,8 @@ set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, { /* Set the base of the current MTRR */ mtrr_physbase.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2); - mtrr_physbase.base = ((unsigned long)base >> GRUB_PAGE_SHIFT) & - SINIT_MTRR_MASK; + mtrr_physbase.base = ((grub_addr_t)base >> GRUB_PAGE_SHIFT) & + mtrr_shifted_mask; mtrr_physbase.type = mem_type; grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSBASE0 + ndx*2, mtrr_physbase.raw); @@ -445,7 +453,7 @@ set_mtrr_mem_type (const grub_uint8_t *base, grub_uint32_t size, pages_in_range = 1 << (fls (num_pages) - 1); mtrr_physmask.raw = grub_rdmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2); - mtrr_physmask.mask = ~(pages_in_range - 1) & SINIT_MTRR_MASK; + mtrr_physmask.mask = ~(pages_in_range - 1) & mtrr_shifted_mask; mtrr_physmask.v = 1; grub_wrmsr (GRUB_MSR_X86_MTRR_PHYSMASK0 + ndx*2, mtrr_physmask.raw); @@ -505,8 +513,7 @@ grub_set_mtrrs_for_acmod (struct grub_txt_acm_header *hdr) 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); + err = set_mtrr_mem_type (hdr, GRUB_MTRR_MEMORY_TYPE_WB); /* Undo some of earlier changes and enable our new settings */ diff --git a/grub-core/loader/slaunch/x86_efi_linux.c b/grub-core/loader/slaunch/x86_efi_linux.c index c88ec42f6..f1c2c1d3f 100644 --- a/grub-core/loader/slaunch/x86_efi_linux.c +++ b/grub-core/loader/slaunch/x86_efi_linux.c @@ -58,15 +58,53 @@ sl_efi_install_slr_table (struct grub_slaunch_params *slparams) return GRUB_ERR_NONE; } +/* Traverses sections of a loaded EFI image to map an offset within a file to + * an offset within the image. Returns appropriately casted -1 on error. */ +static grub_efi_uint64_t +foffset_to_voffset(grub_efi_loaded_image_t *image, grub_uint32_t foffset) +{ + struct grub_msdos_image_header *header; + struct grub_pe_image_header *pe_image_header; + struct grub_pe32_coff_header *coff_header; + struct grub_pe32_section_table *sections; + struct grub_pe32_section_table *section; + grub_uint16_t i; + grub_uint32_t loaded_section; + + header = image->image_base; + pe_image_header = (struct grub_pe_image_header *) + ((char *) header + header->pe_image_header_offset); + coff_header = &pe_image_header->coff_header; + sections = (struct grub_pe32_section_table *) + ((char *) coff_header + sizeof (*coff_header) + + coff_header->optional_header_size); + + loaded_section = GRUB_PE32_SCN_CNT_CODE | GRUB_PE32_SCN_CNT_INITIALIZED_DATA; + for (i = 0, section = sections; i < coff_header->num_sections; i++, section++) + { + grub_uint32_t fstart = section->raw_data_offset; + grub_uint32_t fend = fstart + section->raw_data_size; + + if ((section->characteristics & loaded_section) != 0 && + foffset >= fstart && foffset < fend) + { + return section->virtual_address + (foffset - fstart); + } + } + + return ~(grub_efi_uint64_t)0; +} + static grub_err_t sl_efi_load_mle_data (struct grub_slaunch_params *slparams, void *kernel_addr, grub_ssize_t kernel_start, + grub_efi_loaded_image_t *loaded_image, bool is_linux) { struct linux_kernel_params *lh = (struct linux_kernel_params *)kernel_addr; struct linux_kernel_info kernel_info; struct grub_txt_mle_header *mle_hdr; - grub_uint32_t mle_hdr_offset; + grub_uint64_t mle_hdr_offset; if (is_linux) { @@ -76,10 +114,20 @@ sl_efi_load_mle_data (struct grub_slaunch_params *slparams, sizeof (struct linux_kernel_info)); if (OFFSET_OF (mle_header_offset, &kernel_info) >= grub_le_to_cpu32 (kernel_info.size)) - return grub_error (GRUB_ERR_BAD_OS, N_("not an slaunch kernel: lack of mle_header_offset")); + { + grub_dprintf ("slaunch", "not an slaunch kernel: lack of mle_header_offset\n"); + return GRUB_ERR_BAD_OS; + } mle_hdr_offset = grub_le_to_cpu32 (kernel_info.mle_header_offset); mle_hdr = (struct grub_txt_mle_header *)((grub_addr_t)kernel_addr + slparams->mle_header_offset); + + if (!grub_memcmp (mle_hdr->uuid, GRUB_TXT_MLE_UUID, 16)) + { + grub_dprintf ("slaunch", "Not an MLE header at %llu\n", + (unsigned long long)((grub_addr_t)kernel_addr + mle_hdr_offset)); + return GRUB_ERR_BAD_OS; + } } else { @@ -92,22 +140,31 @@ sl_efi_load_mle_data (struct grub_slaunch_params *slparams, if (mle_hdr_offset >= 0x1000) { - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("not an slaunch kernel: no MLE header found")); + grub_dprintf ("slaunch", "not an slaunch kernel: no MLE header found\n"); + return GRUB_ERR_BAD_ARGUMENT; + } + + mle_hdr_offset = foffset_to_voffset (loaded_image, mle_hdr_offset); + if (mle_hdr_offset >= (1ULL << 32)) + { + grub_dprintf ("slaunch", "failed to map MLE header\n"); + return GRUB_ERR_BAD_ARGUMENT; } } slparams->mle_header_offset = mle_hdr_offset; slparams->mle_entry = mle_hdr->entry_point; - if (!is_linux) { - /* - * The previous value of the field is covering the whole EFI image which - * can include a lot of useless padding. Limit the size used for measuring - * MLE to that reported by the header. Don't change the behaviour for - * Linux. - */ - slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; - } + if (!is_linux) + { + /* + * The current value of the field covers the whole EFI image which can + * include a lot of useless padding. Limit the size used for measuring + * MLE to that reported by the header. Don't change the behaviour for + * Linux in case it causes trouble (needs testing). + */ + slparams->mle_size = mle_hdr->mle_end - mle_hdr->mle_start; + } return GRUB_ERR_NONE; } @@ -284,10 +341,10 @@ grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, goto fail; } - err = sl_efi_load_mle_data (slparams, kernel_addr, start, is_linux); + err = sl_efi_load_mle_data (slparams, kernel_addr, start, loaded_image, is_linux); if (err != GRUB_ERR_NONE) { - grub_dprintf ("slaunch", N_("failed to load MLE data")); + grub_dprintf ("slaunch", N_("failed to load MLE data\n")); goto fail; } @@ -295,14 +352,14 @@ grub_sl_efi_txt_setup (struct grub_slaunch_params *slparams, void *kernel_addr, err = grub_txt_boot_prepare (slparams); if (err != GRUB_ERR_NONE) { - grub_dprintf ("slaunch", N_("failed to prepare TXT")); + grub_dprintf ("slaunch", N_("failed to prepare TXT\n")); goto fail; } err = sl_efi_install_slr_table (slparams); if (err != GRUB_ERR_NONE) { - grub_dprintf ("slaunch", N_("failed to register SLRT with UEFI")); + grub_dprintf ("slaunch", N_("failed to register SLRT with UEFI\n")); goto fail; } @@ -364,9 +421,12 @@ grub_sl_efi_skinit_setup (struct grub_slaunch_params *slparams, void *kernel_add /* It's OK to call this for AMD SKINIT because SKL erases the log before use. */ grub_txt_init_tpm_event_log (logmem, slparams->tpm_evt_log_size); - err = sl_efi_load_mle_data (slparams, kernel_addr, start, is_linux); + err = sl_efi_load_mle_data (slparams, kernel_addr, start, loaded_image, is_linux); if (err != GRUB_ERR_NONE) - goto fail; + { + grub_dprintf ("slaunch", N_("failed to load MLE data\n")); + goto fail; + } /* * AMD SKL final setup may relocate the SKL module. It is also what sets the SLRT and DCE diff --git a/grub-core/video/i386/pc/vbe.c b/grub-core/video/i386/pc/vbe.c index a0bb9af09..70952009f 100644 --- a/grub-core/video/i386/pc/vbe.c +++ b/grub-core/video/i386/pc/vbe.c @@ -93,7 +93,6 @@ grub_vbe_enable_mtrr (grub_uint8_t *base, grub_size_t size) grub_uint32_t features; grub_uint32_t mtrrcap; int var_mtrrs; - grub_uint32_t max_extended_cpuid; grub_uint32_t maxphyaddr; grub_uint64_t fb_base, fb_size; grub_uint64_t size_bits, fb_mask; @@ -142,15 +141,7 @@ grub_vbe_enable_mtrr (grub_uint8_t *base, grub_size_t size) return; var_mtrrs = (mtrrcap & 0xFF); - grub_cpuid (0x80000000, eax, ebx, ecx, edx); - max_extended_cpuid = eax; - if (max_extended_cpuid >= 0x80000008) - { - grub_cpuid (0x80000008, eax, ebx, ecx, edx); - maxphyaddr = (eax & 0xFF); - } - else - maxphyaddr = 36; + maxphyaddr = grub_get_max_phy_addr_bits (); bits_lo = 0xFFFFF000; /* assume maxphyaddr >= 36 */ bits_hi = (1 << (maxphyaddr - 32)) - 1; bits = bits_lo | ((grub_uint64_t) bits_hi << 32); diff --git a/include/grub/i386/cpuid.h b/include/grub/i386/cpuid.h index 36e4ee05e..023ab9431 100644 --- a/include/grub/i386/cpuid.h +++ b/include/grub/i386/cpuid.h @@ -96,4 +96,18 @@ grub_cpu_is_cpuid_supported (void) : "0" (num)) #endif +static inline grub_uint8_t grub_get_max_phy_addr_bits (void) +{ + grub_uint32_t eax, ebx, ecx, edx; + grub_uint32_t max_extended_cpuid; + + grub_cpuid (0x80000000, eax, ebx, ecx, edx); + max_extended_cpuid = eax; + if (max_extended_cpuid < 0x80000008) + return 36; + + grub_cpuid (0x80000008, eax, ebx, ecx, edx); + return eax & 0xFF; +} + #endif