[PATCH 33/33] x86_64: Make bzImage a valid 64bit elf executable.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Eric W. Biederman <[email protected]>
---
 arch/x86_64/boot/Makefile      |    2 
 arch/x86_64/boot/bootsect.S    |   93 +++++++++++++++-
 arch/x86_64/boot/tools/build.c |  232 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 297 insertions(+), 30 deletions(-)

diff --git a/arch/x86_64/boot/Makefile b/arch/x86_64/boot/Makefile
index deb063e..80a7492 100644
--- a/arch/x86_64/boot/Makefile
+++ b/arch/x86_64/boot/Makefile
@@ -41,7 +41,7 @@ # --------------------------------------
 
 quiet_cmd_image = BUILD   $@
 cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/bootsect $(obj)/setup \
-	    $(obj)/vmlinux.bin $(ROOT_DEV) > $@
+	    $(obj)/vmlinux.bin $(ROOT_DEV) vmlinux > $@
 
 $(obj)/bzImage: $(obj)/bootsect $(obj)/setup \
 			      $(obj)/vmlinux.bin $(obj)/tools/build FORCE
diff --git a/arch/x86_64/boot/bootsect.S b/arch/x86_64/boot/bootsect.S
index 011b7a4..05bd1f3 100644
--- a/arch/x86_64/boot/bootsect.S
+++ b/arch/x86_64/boot/bootsect.S
@@ -13,6 +13,13 @@
  *
  */
 
+#include <linux/version.h>
+#include <linux/utsrelease.h>
+#include <linux/compile.h>
+#include <linux/elf.h>
+#include <linux/elf-em.h>
+#include <linux/elf_boot.h>
+#include <asm/page.h>
 #include <asm/boot.h>
 
 SETUPSECTS	= 4			/* default nr of setup-sectors */
@@ -42,10 +49,88 @@ #endif
 
 .global _start
 _start:
-
+ehdr:
+	# e_ident is carefully crafted so if this is treated
+	# as an x86 bootsector you will execute through
+	# e_ident and then print the bugger off message.
+	# The 1 store to bx+di is unfortunate it is
+	# unlikely to affect the ability to print
+	# a message and you aren't supposed to be booting a
+	# bzImage directly from a floppy anyway.
+
+	# e_ident
+	.byte ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3
+	.byte ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_STANDALONE
+	.byte 0xeb, 0x3d, 0, 0, 0, 0, 0, 0
+	.word ET_DYN				# e_type
+	.word EM_X86_64				# e_machine
+	.int  1					# e_version
+	.quad 0x0000000000000100		# e_entry (startup_64)
+	.quad phdr - _start			# e_phoff
+	.quad 0					# e_shoff
+	.int  0					# e_flags
+	.word e_ehdr - ehdr			# e_ehsize
+	.word e_phdr1 - phdr			# e_phentsize
+	.word (e_phdr - phdr)/(e_phdr1 - phdr)	# e_phnum
+	.word 64				# e_shentsize
+	.word 0					# e_shnum
+	.word 0					# e_shstrndx
+e_ehdr:
+
+.org 71
+normalize:
 	# Normalize the start address
 	jmpl	$BOOTSEG, $start2
 
+.org 80
+phdr:
+	.int PT_LOAD					# p_type
+	.int PF_R | PF_W | PF_X				# p_flags
+	.quad (SETUPSECTS+1)*512			# p_offset
+	.quad __START_KERNEL_map			# p_vaddr
+	.quad 0x0000000000000000			# p_paddr
+	.quad SYSSIZE*16				# p_filesz
+	.quad 0						# p_memsz
+	.quad 2*1024*1024				# p_align
+e_phdr1:
+
+	.int PT_NOTE					# p_type
+	.int 0						# p_flags
+	.quad b_note - _start				# p_offset
+	.quad 0						# p_vaddr
+	.quad 0						# p_paddr
+	.quad e_note - b_note				# p_filesz
+	.quad 0						# p_memsz
+	.quad 0						# p_align
+e_phdr:
+
+.macro note name, type
+	.balign 4
+	.int	2f - 1f			# n_namesz
+	.int	4f - 3f			# n_descsz
+	.int	\type			# n_type
+	.balign 4
+1:	.asciz "\name"
+2:	.balign 4
+3:
+.endm
+.macro enote
+4:	.balign 4
+.endm
+
+	.balign 4
+b_note:
+	note ELF_NOTE_BOOT, EIN_PROGRAM_NAME
+		.asciz	"Linux"
+	enote
+	note ELF_NOTE_BOOT, EIN_PROGRAM_VERSION
+		.asciz	UTS_RELEASE
+	enote
+	note ELF_NOTE_BOOT, EIN_ARGUMENT_STYLE
+		.asciz	"Linux"
+	enote
+e_note:
+
 start2:
 	movw	%cs, %ax
 	movw	%ax, %ds
@@ -78,11 +163,11 @@ die:
 
 
 bugger_off_msg:
-	.ascii	"Direct booting from floppy is no longer supported.\r\n"
-	.ascii	"Please use a boot loader program instead.\r\n"
+	.ascii	"Booting linux without a boot loader is no longer supported.\r\n"
 	.ascii	"\n"
-	.ascii	"Remove disk and press any key to reboot . . .\r\n"
+	.ascii	"Press any key to reboot . . .\r\n"
 	.byte	0
+ebugger_off_msg:
 
 
 	# Kernel attributes; used by setup
diff --git a/arch/x86_64/boot/tools/build.c b/arch/x86_64/boot/tools/build.c
index eae8669..fd9bf41 100644
--- a/arch/x86_64/boot/tools/build.c
+++ b/arch/x86_64/boot/tools/build.c
@@ -27,6 +27,11 @@ #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <elf.h>
+#include <byteswap.h>
+#define USE_BSD
+#include <endian.h>
+#include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
@@ -48,6 +53,10 @@ byte buf[1024];
 int fd;
 int is_big_kernel;
 
+#define MAX_PHDRS 100
+static Elf64_Ehdr ehdr;
+static Elf64_Phdr phdr[MAX_PHDRS];
+
 void die(const char * str, ...)
 {
 	va_list args;
@@ -57,20 +66,155 @@ void die(const char * str, ...)
 	exit(1);
 }
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
+#endif
+
+static uint16_t elf16_to_cpu(uint16_t val)
+{
+	return le16_to_cpu(val);
+}
+
+static uint32_t elf32_to_cpu(uint32_t val)
+{
+	return le32_to_cpu(val);
+}
+
+static uint64_t elf64_to_cpu(uint64_t val)
+{
+	return le64_to_cpu(val);
+}
+
 void file_open(const char *name)
 {
 	if ((fd = open(name, O_RDONLY, 0)) < 0)
 		die("Unable to open `%s': %m", name);
 }
 
+static void read_ehdr(void)
+{
+	if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
+		die("Cannot read ELF header: %s\n",
+			strerror(errno));
+	}
+	if (memcmp(ehdr.e_ident, ELFMAG, 4) != 0) {
+		die("No ELF magic\n");
+	}
+	if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
+		die("Not a 64 bit executable\n");
+	}
+	if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
+		die("Not a LSB ELF executable\n");
+	}
+	if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
+		die("Unknown ELF version\n");
+	}
+	/* Convert the fields to native endian */
+	ehdr.e_type      = elf16_to_cpu(ehdr.e_type);
+	ehdr.e_machine   = elf16_to_cpu(ehdr.e_machine);
+	ehdr.e_version   = elf32_to_cpu(ehdr.e_version);
+	ehdr.e_entry     = elf64_to_cpu(ehdr.e_entry);
+	ehdr.e_phoff     = elf64_to_cpu(ehdr.e_phoff);
+	ehdr.e_shoff     = elf64_to_cpu(ehdr.e_shoff);
+	ehdr.e_flags     = elf32_to_cpu(ehdr.e_flags);
+	ehdr.e_ehsize    = elf16_to_cpu(ehdr.e_ehsize);
+	ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize);
+	ehdr.e_phnum     = elf16_to_cpu(ehdr.e_phnum);
+	ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize);
+	ehdr.e_shnum     = elf16_to_cpu(ehdr.e_shnum);
+	ehdr.e_shstrndx  = elf16_to_cpu(ehdr.e_shstrndx);
+
+	if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
+		die("Unsupported ELF header type\n");
+	}
+	if (ehdr.e_machine != EM_X86_64) {
+		die("Not for x86_64\n");
+	}
+	if (ehdr.e_version != EV_CURRENT) {
+		die("Unknown ELF version\n");
+	}
+	if (ehdr.e_ehsize != sizeof(Elf64_Ehdr)) {
+		die("Bad Elf header size\n");
+	}
+	if (ehdr.e_phentsize != sizeof(Elf64_Phdr)) {
+		die("Bad program header entry\n");
+	}
+	if (ehdr.e_shentsize != sizeof(Elf64_Shdr)) {
+		die("Bad section header entry\n");
+	}
+	if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+		die("String table index out of bounds\n");
+	}
+}
+
+static void read_phds(void)
+{
+	int i;
+	size_t size;
+	if (ehdr.e_phnum > MAX_PHDRS) {
+		die("%d program headers supported: %d\n",
+			ehdr.e_phnum, MAX_PHDRS);
+	}
+	if (lseek(fd, ehdr.e_phoff, SEEK_SET) < 0) {
+		die("Seek to %d failed: %s\n",
+			ehdr.e_phoff, strerror(errno));
+	}
+	size = sizeof(phdr[0])*ehdr.e_phnum;
+	if (read(fd, &phdr, size) != size) {
+		die("Cannot read ELF section headers: %s\n",
+			strerror(errno));
+	}
+	for(i = 0; i < ehdr.e_phnum; i++) {
+		phdr[i].p_type      = elf32_to_cpu(phdr[i].p_type);
+		phdr[i].p_flags     = elf32_to_cpu(phdr[i].p_flags);
+		phdr[i].p_offset    = elf64_to_cpu(phdr[i].p_offset);
+		phdr[i].p_vaddr     = elf64_to_cpu(phdr[i].p_vaddr);
+		phdr[i].p_paddr     = elf64_to_cpu(phdr[i].p_paddr);
+		phdr[i].p_filesz    = elf64_to_cpu(phdr[i].p_filesz);
+		phdr[i].p_memsz     = elf64_to_cpu(phdr[i].p_memsz);
+		phdr[i].p_align     = elf64_to_cpu(phdr[i].p_align);
+	}
+}
+
+uint64_t vmlinux_memsz(void)
+{
+	uint64_t min, max, size;
+	int i;
+	max = 0;
+	min = ~max;
+	for(i = 0; i < ehdr.e_phnum; i++) {
+		uint64_t start, end;
+		if (phdr[i].p_type != PT_LOAD)
+			continue;
+		start = phdr[i].p_paddr;
+		end   = phdr[i].p_paddr + phdr[i].p_memsz;
+		if (start < min)
+			min = start;
+		if (end > max)
+			max = end;
+	}
+	/* Get the reported size by vmlinux */
+	size = max - min;
+	return size;
+}
+
 void usage(void)
 {
-	die("Usage: build [-b] bootsect setup system [rootdev] [> image]");
+	die("Usage: build [-b] bootsect setup system rootdev vmlinux [> image]");
 }
 
 int main(int argc, char ** argv)
 {
-	unsigned int i, c, sz, setup_sectors;
+	unsigned int i, sz, setup_sectors;
+	uint64_t kernel_offset, kernel_filesz, kernel_memsz;
+	int c;
 	u32 sys_size;
 	byte major_root, minor_root;
 	struct stat sb;
@@ -80,30 +224,25 @@ int main(int argc, char ** argv)
 	    is_big_kernel = 1;
 	    argc--, argv++;
 	  }
-	if ((argc < 4) || (argc > 5))
+	if (argc != 6)
 		usage();
-	if (argc > 4) {
-		if (!strcmp(argv[4], "CURRENT")) {
-			if (stat("/", &sb)) {
-				perror("/");
-				die("Couldn't stat /");
-			}
-			major_root = major(sb.st_dev);
-			minor_root = minor(sb.st_dev);
-		} else if (strcmp(argv[4], "FLOPPY")) {
-			if (stat(argv[4], &sb)) {
-				perror(argv[4]);
-				die("Couldn't stat root device.");
-			}
-			major_root = major(sb.st_rdev);
-			minor_root = minor(sb.st_rdev);
-		} else {
-			major_root = 0;
-			minor_root = 0;
+	if (!strcmp(argv[4], "CURRENT")) {
+		if (stat("/", &sb)) {
+			perror("/");
+			die("Couldn't stat /");
+		}
+		major_root = major(sb.st_dev);
+		minor_root = minor(sb.st_dev);
+	} else if (strcmp(argv[4], "FLOPPY")) {
+		if (stat(argv[4], &sb)) {
+			perror(argv[4]);
+			die("Couldn't stat root device.");
 		}
+		major_root = major(sb.st_rdev);
+		minor_root = minor(sb.st_rdev);
 	} else {
-		major_root = DEFAULT_MAJOR_ROOT;
-		minor_root = DEFAULT_MINOR_ROOT;
+		major_root = 0;
+		minor_root = 0;
 	}
 	fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
 
@@ -143,10 +282,11 @@ int main(int argc, char ** argv)
 		i += c;
 	}
 
+	kernel_offset = (setup_sectors + 1)*512;
 	file_open(argv[3]);
 	if (fstat (fd, &sb))
 		die("Unable to stat `%s': %m", argv[3]);
-	sz = sb.st_size;
+	kernel_filesz = sz = sb.st_size;
 	fprintf (stderr, "System is %d kB\n", sz/1024);
 	sys_size = (sz + 15) / 16;
 	if (!is_big_kernel && sys_size > DEF_SYSSIZE)
@@ -167,7 +307,49 @@ int main(int argc, char ** argv)
 	}
 	close(fd);
 
-	if (lseek(1, 497, SEEK_SET) != 497)		    /* Write sizes to the bootsector */
+	file_open(argv[5]);
+	read_ehdr();
+	read_phds();
+	close(fd);
+	kernel_memsz = vmlinux_memsz();
+
+	if (lseek(1,  88, SEEK_SET) != 88)		    /* Write sizes to the bootsector */
+		die("Output: seek failed");
+	buf[0] = (kernel_offset >>  0) & 0xff;
+	buf[1] = (kernel_offset >>  8) & 0xff;
+	buf[2] = (kernel_offset >> 16) & 0xff;
+	buf[3] = (kernel_offset >> 24) & 0xff;
+	buf[4] = (kernel_offset >> 32) & 0xff;
+	buf[5] = (kernel_offset >> 40) & 0xff;
+	buf[6] = (kernel_offset >> 48) & 0xff;
+	buf[7] = (kernel_offset >> 56) & 0xff;
+	if (write(1, buf, 8) != 8)
+		die("Write of kernel file offset failed");
+	if (lseek(1, 112, SEEK_SET) != 112)
+		die("Output: seek failed");
+	buf[0] = (kernel_filesz >>  0) & 0xff;
+	buf[1] = (kernel_filesz >>  8) & 0xff;
+	buf[2] = (kernel_filesz >> 16) & 0xff;
+	buf[3] = (kernel_filesz >> 24) & 0xff;
+	buf[4] = (kernel_filesz >> 32) & 0xff;
+	buf[5] = (kernel_filesz >> 40) & 0xff;
+	buf[6] = (kernel_filesz >> 48) & 0xff;
+	buf[7] = (kernel_filesz >> 56) & 0xff;
+	if (write(1, buf, 8) != 8)
+		die("Write of kernel file size failed");
+	if (lseek(1, 120, SEEK_SET) != 120)
+		die("Output: seek failed");
+	buf[0] = (kernel_memsz >>  0) & 0xff;
+	buf[1] = (kernel_memsz >>  8) & 0xff;
+	buf[2] = (kernel_memsz >> 16) & 0xff;
+	buf[3] = (kernel_memsz >> 24) & 0xff;
+	buf[4] = (kernel_memsz >> 32) & 0xff;
+	buf[5] = (kernel_memsz >> 40) & 0xff;
+	buf[6] = (kernel_memsz >> 48) & 0xff;
+	buf[7] = (kernel_memsz >> 56) & 0xff;
+	if (write(1, buf, 8) != 8)
+		die("Write of kernel memory size failed");
+	if (lseek(1, 497, SEEK_SET) != 497)
 		die("Output: seek failed");
 	buf[0] = setup_sectors;
 	if (write(1, buf, 1) != 1)
-- 
1.4.2.rc2.g5209e

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux