Blame SOURCES/0056-Make-64-on-32-maybe-work-on-x86_64.patch

4210fa
From 750584c207757688cbab47f51a18a33c3e36fb8b Mon Sep 17 00:00:00 2001
4210fa
From: Peter Jones <pjones@redhat.com>
4210fa
Date: Fri, 19 Sep 2014 11:37:35 -0400
4210fa
Subject: [PATCH 56/74] Make 64-on-32 maybe work on x86_64.
4210fa
4210fa
This is mostly based on a patch (https://github.com/mjg59/shim/issues/30)
4210fa
from https://github.com/TBOpen , which refactors our __LP64__
4210fa
tests to be tests of the header magic instead.  I've simplified things
4210fa
by using what we've pre-loaded into "context" and making some helper
4210fa
functions so the conditionals in most of the code say what they do,
4210fa
instead of how they work.
4210fa
4210fa
Note that we're only allowing that from in_protocol's loader - that is,
4210fa
we'll let 64-bit grub load a 32-bit kernel or 32-bit grub load a 64-bit
4210fa
kernel, but 32-bit shim isn't loading a 64-bit grub.
4210fa
4210fa
Signed-off-by: Peter Jones <pjones@redhat.com>
4210fa
---
4210fa
 shim.c | 220 ++++++++++++++++++++++++++++++++++++++++++++---------------------
4210fa
 1 file changed, 148 insertions(+), 72 deletions(-)
4210fa
4210fa
diff --git a/shim.c b/shim.c
4210fa
index 4b4d31a..c1b5c17 100644
4210fa
--- a/shim.c
4210fa
+++ b/shim.c
4210fa
@@ -118,6 +118,106 @@ static void *ImageAddress (void *image, unsigned int size, unsigned int address)
4210fa
 	return image + address;
4210fa
 }
4210fa
 
4210fa
+/* here's a chart:
4210fa
+ *		i686	x86_64	aarch64
4210fa
+ *  64-on-64:	nyet	yes	yes
4210fa
+ *  64-on-32:	nyet	yes	nyet
4210fa
+ *  32-on-32:	yes	yes	no
4210fa
+ */
4210fa
+static int
4210fa
+allow_64_bit(void)
4210fa
+{
4210fa
+#if defined(__x86_64__) || defined(__aarch64__)
4210fa
+	return 1;
4210fa
+#elif defined(__i386__) || defined(__i686__)
4210fa
+	/* Right now blindly assuming the kernel will correctly detect this
4210fa
+	 * and /halt the system/ if you're not really on a 64-bit cpu */
4210fa
+	if (in_protocol)
4210fa
+		return 1;
4210fa
+	return 0;
4210fa
+#else /* assuming everything else is 32-bit... */
4210fa
+	return 0;
4210fa
+#endif
4210fa
+}
4210fa
+
4210fa
+static int
4210fa
+allow_32_bit(void)
4210fa
+{
4210fa
+#if defined(__x86_64__)
4210fa
+#if defined(ALLOW_32BIT_KERNEL_ON_X64)
4210fa
+	if (in_protocol)
4210fa
+		return 1;
4210fa
+	return 0;
4210fa
+#else
4210fa
+	return 0;
4210fa
+#endif
4210fa
+#elif defined(__i386__) || defined(__i686__)
4210fa
+	return 1;
4210fa
+#elif defined(__arch64__)
4210fa
+	return 0;
4210fa
+#else /* assuming everything else is 32-bit... */
4210fa
+	return 1;
4210fa
+#endif
4210fa
+}
4210fa
+
4210fa
+static int
4210fa
+image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
4210fa
+{
4210fa
+	/* .Magic is the same offset in all cases */
4210fa
+	if (PEHdr->Pe32Plus.OptionalHeader.Magic
4210fa
+			== EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)
4210fa
+		return 1;
4210fa
+	return 0;
4210fa
+}
4210fa
+
4210fa
+static const UINT16 machine_type =
4210fa
+#if defined(__x86_64__)
4210fa
+	IMAGE_FILE_MACHINE_X64;
4210fa
+#elif defined(__aarch64__)
4210fa
+	IMAGE_FILE_MACHINE_ARM64;
4210fa
+#elif defined(__arm__)
4210fa
+	IMAGE_FILE_MACHINE_ARMTHUMB_MIXED;
4210fa
+#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
4210fa
+	IMAGE_FILE_MACHINE_I386;
4210fa
+#elif defined(__ia64__)
4210fa
+	IMAGE_FILE_MACHINE_IA64;
4210fa
+#else
4210fa
+#error this architecture is not supported by shim
4210fa
+#endif
4210fa
+
4210fa
+static int
4210fa
+image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
4210fa
+{
4210fa
+	/* If the machine type doesn't match the binary, bail, unless
4210fa
+	 * we're in an allowed 64-on-32 scenario */
4210fa
+	if (PEHdr->Pe32.FileHeader.Machine != machine_type) {
4210fa
+		if (!(machine_type == IMAGE_FILE_MACHINE_I386 &&
4210fa
+		      PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 &&
4210fa
+		      allow_64_bit())) {
4210fa
+			return 0;
4210fa
+		}
4210fa
+	}
4210fa
+
4210fa
+	/* If it's not a header type we recognize at all, bail */
4210fa
+	switch (PEHdr->Pe32Plus.OptionalHeader.Magic) {
4210fa
+	case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
4210fa
+	case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
4210fa
+		break;
4210fa
+	default:
4210fa
+		return 0;
4210fa
+	}
4210fa
+
4210fa
+	/* and now just check for general 64-vs-32 compatibility */
4210fa
+	if (image_is_64_bit(PEHdr)) {
4210fa
+		if (allow_64_bit())
4210fa
+			return 1;
4210fa
+	} else {
4210fa
+		if (allow_32_bit())
4210fa
+			return 1;
4210fa
+	}
4210fa
+	return 0;
4210fa
+}
4210fa
+
4210fa
 /*
4210fa
  * Perform the actual relocation
4210fa
  */
4210fa
@@ -134,11 +234,10 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
4210fa
 	int size = context->ImageSize;
4210fa
 	void *ImageEnd = (char *)orig + size;
4210fa
 
4210fa
-#if __LP64__
4210fa
-	context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)data;
4210fa
-#else
4210fa
-	context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)data;
4210fa
-#endif
4210fa
+	if (image_is_64_bit(context->PEHdr))
4210fa
+		context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)(unsigned long)data;
4210fa
+	else
4210fa
+		context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)(unsigned long)data;
4210fa
 
4210fa
 	RelocBase = ImageAddress(orig, size, context->RelocDir->VirtualAddress);
4210fa
 	RelocBaseEnd = ImageAddress(orig, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
4210fa
@@ -157,7 +256,7 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
4210fa
 		Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
4210fa
 
4210fa
 		if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) {
4210fa
-			perror(L"Reloc block size is invalid\n");
4210fa
+			perror(L"Reloc block size %d is invalid\n", RelocBase->SizeOfBlock);
4210fa
 			return EFI_UNSUPPORTED;
4210fa
 		}
4210fa
 
4210fa
@@ -498,7 +597,7 @@ static BOOLEAN secure_mode (void)
4210fa
  * Calculate the SHA1 and SHA256 hashes of a binary
4210fa
  */
4210fa
 
4210fa
-static EFI_STATUS generate_hash (char *data, int datasize_in,
4210fa
+static EFI_STATUS generate_hash (char *data, unsigned int datasize_in,
4210fa
 				 PE_COFF_LOADER_IMAGE_CONTEXT *context,
4210fa
 				 UINT8 *sha256hash, UINT8 *sha1hash)
4210fa
 
4210fa
@@ -572,15 +671,14 @@ static EFI_STATUS generate_hash (char *data, int datasize_in,
4210fa
 	}
4210fa
 
4210fa
 	/* Hash end of certificate table to end of image header */
4210fa
-#if __LP64__
4210fa
-	hashbase = (char *) &context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
4210fa
-	hashsize = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders -
4210fa
-		(int) ((char *) (&context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data);
4210fa
-#else
4210fa
-	hashbase = (char *) &context->PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
4210fa
-	hashsize = context->PEHdr->Pe32.OptionalHeader.SizeOfHeaders -
4210fa
-		(int) ((char *) (&context->PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data);
4210fa
-#endif
4210fa
+	EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1;
4210fa
+	hashbase = (char *)dd;
4210fa
+	hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data);
4210fa
+	if (hashsize > datasize_in) {
4210fa
+		perror(L"Data Directory size %d is invalid\n", hashsize);
4210fa
+		status = EFI_INVALID_PARAMETER;
4210fa
+		goto done;
4210fa
+	}
4210fa
 
4210fa
 	if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
4210fa
 	    !(Sha1Update(sha1ctx, hashbase, hashsize))) {
4210fa
@@ -590,11 +688,7 @@ static EFI_STATUS generate_hash (char *data, int datasize_in,
4210fa
 	}
4210fa
 
4210fa
 	/* Sort sections */
4210fa
-#if __LP64__
4210fa
-	SumOfBytesHashed = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
4210fa
-#else
4210fa
-	SumOfBytesHashed = context->PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
4210fa
-#endif
4210fa
+	SumOfBytesHashed = context->SizeOfHeaders;
4210fa
 
4210fa
 	/* Validate section locations and sizes */
4210fa
 	for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
4210fa
@@ -682,14 +776,7 @@ static EFI_STATUS generate_hash (char *data, int datasize_in,
4210fa
 	/* Hash all remaining data */
4210fa
 	if (datasize > SumOfBytesHashed) {
4210fa
 		hashbase = data + SumOfBytesHashed;
4210fa
-		hashsize = (unsigned int)(
4210fa
-			datasize -
4210fa
-#if __LP64__
4210fa
-			context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
4210fa
-#else
4210fa
-			context->PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
4210fa
-#endif
4210fa
-			SumOfBytesHashed);
4210fa
+		hashsize = datasize - context->SecDir->Size - SumOfBytesHashed;
4210fa
 
4210fa
 		if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
4210fa
 		    !(Sha1Update(sha1ctx, hashbase, hashsize))) {
4210fa
@@ -843,24 +930,31 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
4210fa
 	EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
4210fa
 	unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
4210fa
 
4210fa
-	if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) {
4210fa
+	if (datasize < sizeof (PEHdr->Pe32)) {
4210fa
 		perror(L"Invalid image\n");
4210fa
 		return EFI_UNSUPPORTED;
4210fa
 	}
4210fa
 
4210fa
 	if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
4210fa
 		PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
4210fa
-#if __LP64__
4210fa
-	context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
4210fa
-	context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
4210fa
-	context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
4210fa
-	OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
4210fa
-#else
4210fa
-	context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
4210fa
-	context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
4210fa
-	context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
4210fa
-	OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
4210fa
-#endif
4210fa
+
4210fa
+	if (!image_is_loadable(PEHdr)) {
4210fa
+		perror(L"Platform does not support this image\n");
4210fa
+		return EFI_UNSUPPORTED;
4210fa
+	}
4210fa
+
4210fa
+	if (image_is_64_bit(PEHdr)) {
4210fa
+		context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
4210fa
+		context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
4210fa
+		context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
4210fa
+		OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
4210fa
+	} else {
4210fa
+		context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
4210fa
+		context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
4210fa
+		context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
4210fa
+		OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
4210fa
+	}
4210fa
+
4210fa
 	context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
4210fa
 
4210fa
 	if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) {
4210fa
@@ -908,17 +1002,19 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
4210fa
 	}
4210fa
 
4210fa
 	context->PEHdr = PEHdr;
4210fa
-#if __LP64__
4210fa
-	context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
4210fa
-	context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
4210fa
-	context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
4210fa
-	context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
4210fa
-#else
4210fa
-	context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
4210fa
-	context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
4210fa
-	context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
4210fa
-	context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
4210fa
-#endif
4210fa
+
4210fa
+	if (image_is_64_bit(PEHdr)) {
4210fa
+		context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
4210fa
+		context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
4210fa
+		context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
4210fa
+		context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
4210fa
+	} else {
4210fa
+		context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
4210fa
+		context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
4210fa
+		context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
4210fa
+		context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
4210fa
+	}
4210fa
+
4210fa
 	context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
4210fa
 
4210fa
 	if (context->ImageSize < context->SizeOfHeaders) {
4210fa
@@ -939,21 +1035,6 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
4210fa
 	return EFI_SUCCESS;
4210fa
 }
4210fa
 
4210fa
-static const UINT16 machine_type =
4210fa
-#if defined(__x86_64__)
4210fa
-	IMAGE_FILE_MACHINE_X64;
4210fa
-#elif defined(__aarch64__)
4210fa
-	IMAGE_FILE_MACHINE_ARM64;
4210fa
-#elif defined(__arm__)
4210fa
-	IMAGE_FILE_MACHINE_ARMTHUMB_MIXED;
4210fa
-#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
4210fa
-	IMAGE_FILE_MACHINE_I386;
4210fa
-#elif defined(__ia64__)
4210fa
-	IMAGE_FILE_MACHINE_IA64;
4210fa
-#else
4210fa
-#error this architecture is not supported by shim
4210fa
-#endif
4210fa
-
4210fa
 /*
4210fa
  * Once the image has been loaded it needs to be validated and relocated
4210fa
  */
4210fa
@@ -977,11 +1058,6 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
4210fa
 		return efi_status;
4210fa
 	}
4210fa
 
4210fa
-	if (context.PEHdr->Pe32.FileHeader.Machine != machine_type) {
4210fa
-		perror(L"Image is for a different architecture\n");
4210fa
-		return EFI_UNSUPPORTED;
4210fa
-	}
4210fa
-
4210fa
 	/*
4210fa
 	 * We only need to verify the binary if we're in secure mode
4210fa
 	 */
4210fa
-- 
4210fa
1.9.3
4210fa