Skip to content
6 changes: 6 additions & 0 deletions grub-core/loader/slaunch/slrt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
37 changes: 22 additions & 15 deletions grub-core/loader/slaunch/txt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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 */

Expand Down
96 changes: 78 additions & 18 deletions grub-core/loader/slaunch/x86_efi_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
{
Expand All @@ -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;
}
Expand Down Expand Up @@ -284,25 +341,25 @@ 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;
}

/* Final stage for secure launch, setup TXT and install the SLR table */
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;
}

Expand Down Expand Up @@ -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
Expand Down
11 changes: 1 addition & 10 deletions grub-core/video/i386/pc/vbe.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions include/grub/i386/cpuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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