[LWN Logo]
[LWN.net]
From:	 ebiederm@xmission.com (Eric W. Biederman)
To:	 Linus Torvalds <torvalds@transmeta.com>, Dave Jones <davej@suse.de>
Subject: [PATCH 2.5.12] x86 Boot enhancements, bzElf support 11/11
Date:	 02 May 2002 09:15:57 -0600
Cc:	 <linux-kernel@vger.kernel.org>

Please apply,

This patch allows a bzELF image to be built instead of a bzImage.
A bzELF is just a bzImage represented in the ELF file format.  The transform
from bzELF to bzImage is 1-1.

Eric

diff -uNr linux-2.5.12.boot.linuxbios/arch/i386/Makefile linux-2.5.12.boot.elf/arch/i386/Makefile
--- linux-2.5.12.boot.linuxbios/arch/i386/Makefile	Wed May  1 09:38:47 2002
+++ linux-2.5.12.boot.elf/arch/i386/Makefile	Wed May  1 09:41:44 2002
@@ -119,6 +119,9 @@
 bzImage: vmlinux
 	@$(MAKEBOOT) bzImage
 
+bzElf: vmlinux
+	@$(MAKEBOOT) bzElf
+
 compressed: zImage
 
 zlilo: vmlinux
diff -uNr linux-2.5.12.boot.linuxbios/arch/i386/boot/Makefile linux-2.5.12.boot.elf/arch/i386/boot/Makefile
--- linux-2.5.12.boot.linuxbios/arch/i386/boot/Makefile	Wed May  1 09:40:42 2002
+++ linux-2.5.12.boot.elf/arch/i386/boot/Makefile	Wed May  1 09:41:44 2002
@@ -18,6 +18,9 @@
 bzImage: $(CONFIGURE) tools/build $(TOPDIR)/vmlinux brealmode compressed/bvmlinux 
 	tools/build -b $(TOPDIR)/vmlinux brealmode compressed/bvmlinux bzImage
 
+bzElf: $(CONFIGURE) tools/build $(TOPDIR)/vmlinux brealmode compressed/bvmlinux 
+	tools/build -e -b $(TOPDIR)/vmlinux brealmode compressed/bvmlinux bzElf
+
 compressed/vmlinux: $(TOPDIR)/vmlinux
 	@$(MAKE) -C compressed vmlinux
 
@@ -75,5 +78,5 @@
 clean:
 	rm -f tools/build
 	rm -f realmode zImage
-	rm -f brealmode bzImage
+	rm -f brealmode bzImage bzElf
 	@$(MAKE) -C compressed clean
diff -uNr linux-2.5.12.boot.linuxbios/arch/i386/boot/tools/build.c linux-2.5.12.boot.elf/arch/i386/boot/tools/build.c
--- linux-2.5.12.boot.linuxbios/arch/i386/boot/tools/build.c	Wed May  1 09:40:42 2002
+++ linux-2.5.12.boot.elf/arch/i386/boot/tools/build.c	Wed May  1 09:41:44 2002
@@ -80,11 +80,12 @@
 #define ISEGS  3
 
 /* Segments of the output file */
-#define OREAL 0
-#define OKERN 1
-#define OBSS1 2
-#define OBSS2 3
-#define OSEGS 4
+#define OEHDR 0
+#define OREAL 1
+#define OKERN 2
+#define OBSS1 3
+#define OBSS2 4
+#define OSEGS 5
 
 struct file_seg
 {
@@ -96,6 +97,37 @@
 	unsigned char *data;
 };
 
+/* Kernel version */
+#define LINUX_KERNEL_VERSION \
+	UTS_RELEASE " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ") " UTS_VERSION
+
+typedef uint16_t Elf_Half;
+typedef uint32_t Elf_Word;
+typedef uint64_t Elf_Xword;
+
+typedef struct 
+{
+	Elf_Word n_namesz;		/* Length of the note's name.  */
+	Elf_Word n_descsz;		/* Length of the note's descriptor.  */
+	Elf_Word n_type;		/* Type of the note.  */
+} Elf_Nhdr;
+
+typedef struct 
+{
+	Elf_Word n_paddr;
+	Elf_Word n_size;
+} Elf_Pdesc;
+
+/*  Elf image notes for booting... The name for all of these is ELFBoot */
+
+#define EIN_NOTE_NAME	"ELFBoot"
+#define EIN_PROGRAM_NAME	0x00000001
+/* The program in this ELF file */
+#define EIN_PROGRAM_VERSION	0x00000002
+/* The version of the program in this ELF file */
+#define EIN_PROGRAM_CHECKSUM	0x00000003
+/* ip style checksum of the memory image. */
+
 struct boot_params {
 	uint8_t  reserved1[0x1f1];		/* 0x000 */
 	uint8_t  setup_sects;			/* 0x1f1 */
@@ -335,6 +367,76 @@
 	return result;
 }
 
+static unsigned long compute_ip_checksum(void *addr, unsigned long length)
+{
+	uint16_t *ptr;
+	unsigned long sum;
+	unsigned long len;
+	unsigned long laddr;
+	/* compute an ip style checksum */
+	laddr = (unsigned long )addr;
+	sum = 0;
+	if (laddr & 1) {
+		uint16_t buffer;
+		unsigned char *ptr;
+		/* copy the first byte into a 2 byte buffer.
+		 * This way automatically handles the endian question
+		 * of which byte (low or high) the last byte goes in.
+		 */
+		buffer = 0;
+		ptr = addr;
+		memcpy(&buffer, ptr, 1);
+		sum += buffer;
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+		length -= 1;
+		addr = ptr +1;
+	}
+	len = length >> 1;
+	ptr = addr;
+	while (len--) {
+		sum += *(ptr++);
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+	}
+	addr = ptr;
+	if (length & 1) {
+		uint16_t buffer;
+		unsigned char *ptr;
+		/* copy the last byte into a 2 byte buffer.
+		 * This way automatically handles the endian question
+		 * of which byte (low or high) the last byte goes in.
+		 */
+		buffer = 0;
+		ptr = addr;
+		memcpy(&buffer, ptr, 1);
+		sum += buffer;
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+	}
+	return (~sum) & 0xFFFF;
+	
+}
+
+static unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new)
+{
+	unsigned long checksum;
+	sum = ~sum & 0xFFFF;
+	new = ~new & 0xFFFF;
+	if (offset & 1) {
+		/* byte swap the sum if it came from an odd offset 
+		 * since the computation is endian independant this
+		 * works.
+		 */
+		new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
+	}
+	checksum = sum + new;
+	if (checksum > 0xFFFF) {
+		checksum -= 0xFFFF;
+	}
+	return (~checksum) & 0xFFFF;
+}
+
 static void check_ehdr(Elf32_Ehdr *ehdr, char *name)
 {
 	/* Do some basic to ensure it is an ELF image */
@@ -489,6 +591,7 @@
 
 struct image_info {
 	int is_big_kernel;
+	int is_elf;
 	size_t entry32;
 	size_t setup_sectors;
 	size_t sys_size;
@@ -518,11 +621,114 @@
 	zhdr->output_overhang = cpu_to_le32(info->unzip_overhang);
 	zhdr->kernel_memsz    = cpu_to_le32(iseg[IKERN].mem_size);
 	zhdr->kernel_filesz   = cpu_to_le32(iseg[IKERN].data_size);
+	if (info->is_elf) {
+		/* For ELF images we don't expect the header to be relocated */
+		param->loadflags |= cpu_to_le32(LOADFLAG_STAY_PUT);
+		param->type_of_loader = LOADER_UNKNOWN;
+	}
+}
+
+struct elf_header {
+	Elf32_Ehdr ehdr;
+	Elf32_Phdr phdr[OSEGS - (OEHDR + 1)];
+	Elf_Nhdr   program_name_hdr;
+	uint8_t    program_name_name[8];
+	uint8_t    program_name_desc[8];
+	Elf_Nhdr   program_version_hdr;
+	uint8_t    program_version_name[8];
+	uint8_t    program_version_desc[(sizeof(LINUX_KERNEL_VERSION) + 3) & ~3];
+	Elf_Nhdr   program_checksum_hdr;
+	uint8_t    program_checksum_name[8];
+	uint16_t   program_checksum;
+	uint16_t   program_checksum_pad;
+} __attribute__((packed));
+
+static void build_elf_header(struct file_seg *seg, struct image_info *info)
+{
+	struct elf_header *hdr;
+	size_t checksum, bytes;
+	size_t offset, size;
+	int i;
+	hdr = checked_malloc(sizeof(*hdr));
+	memset(hdr, 0, sizeof(*hdr));
+	memcpy(hdr->ehdr.e_ident, ELFMAG, 4);
+	hdr->ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
+	hdr->ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
+	hdr->ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+	hdr->ehdr.e_type      = cpu_to_le16(ET_EXEC);
+	hdr->ehdr.e_machine   = cpu_to_le16(EM_386);
+	hdr->ehdr.e_version   = cpu_to_le32(EV_CURRENT);
+	hdr->ehdr.e_entry     = cpu_to_le32(info->entry32);
+	hdr->ehdr.e_phoff     = cpu_to_le32(offsetof(struct elf_header, phdr));
+	hdr->ehdr.e_shoff     = cpu_to_le32(0);
+	hdr->ehdr.e_flags     = cpu_to_le16(0);
+	hdr->ehdr.e_ehsize    = cpu_to_le16(sizeof(hdr->ehdr));
+	hdr->ehdr.e_phentsize = cpu_to_le16(sizeof(hdr->phdr[0]));
+	hdr->ehdr.e_phnum     = cpu_to_le16(sizeof(hdr->phdr)/sizeof(hdr->phdr[0]));
+	hdr->ehdr.e_shentsize = cpu_to_le16(0);
+	hdr->ehdr.e_shnum     = cpu_to_le16(0);
+	hdr->ehdr.e_shstrndx  = cpu_to_le16(0);
+
+	offset = offsetof(struct elf_header, program_name_hdr);
+	size = sizeof(*hdr) - offset;
+	hdr->phdr[0].p_type   = cpu_to_le32(PT_NOTE);
+	hdr->phdr[0].p_offset = cpu_to_le32(offset);
+	hdr->phdr[0].p_vaddr  = cpu_to_le32(0);
+	hdr->phdr[0].p_paddr  = cpu_to_le32(0);
+	hdr->phdr[0].p_filesz = cpu_to_le32(size);
+	hdr->phdr[0].p_memsz  = cpu_to_le32(size);
+	hdr->phdr[0].p_flags  = cpu_to_le32(0);
+	hdr->phdr[0].p_align  = cpu_to_le32(0);
+	
+	for(i = OEHDR +1; i < OSEGS; i++) {
+		hdr->phdr[i].p_type = cpu_to_le32(PT_NULL);
+		if (seg[i].mem_size == 0) 
+			continue;
+		hdr->phdr[i].p_type   = cpu_to_le32(PT_LOAD);
+		hdr->phdr[i].p_offset = cpu_to_le32(seg[i].file_offset);
+		hdr->phdr[i].p_vaddr  = cpu_to_le32(seg[i].mem_addr);
+		hdr->phdr[i].p_paddr  = cpu_to_le32(seg[i].mem_addr);
+		hdr->phdr[i].p_filesz = cpu_to_le32(seg[i].data_size);
+		hdr->phdr[i].p_memsz  = cpu_to_le32(seg[i].mem_size);
+		hdr->phdr[i].p_flags  = cpu_to_le32(0);
+		hdr->phdr[i].p_align  = cpu_to_le32(0);
+	}
+	
+	hdr->program_name_hdr.n_namesz = cpu_to_le32(strlen(EIN_NOTE_NAME) +1);
+	hdr->program_name_hdr.n_descsz = cpu_to_le32(strlen("Linux") +1);
+	hdr->program_name_hdr.n_type   = cpu_to_le32(EIN_PROGRAM_NAME);
+	strcpy(hdr->program_name_name, EIN_NOTE_NAME);
+	strcpy(hdr->program_name_desc, "Linux");
+	
+	hdr->program_version_hdr.n_namesz = cpu_to_le32(strlen(EIN_NOTE_NAME) +1);
+	hdr->program_version_hdr.n_descsz = cpu_to_le32(strlen(LINUX_KERNEL_VERSION)+1);
+	hdr->program_version_hdr.n_type   = cpu_to_le32(EIN_PROGRAM_VERSION);
+	strcpy(hdr->program_version_name, EIN_NOTE_NAME);
+	strcpy(hdr->program_version_desc, LINUX_KERNEL_VERSION);
+
+	hdr->program_checksum_hdr.n_namesz = cpu_to_le32(strlen(EIN_NOTE_NAME) +1);
+	hdr->program_checksum_hdr.n_descsz = cpu_to_le32(2);
+	hdr->program_checksum_hdr.n_type   = cpu_to_le32(EIN_PROGRAM_CHECKSUM);
+	strcpy(hdr->program_checksum_name, EIN_NOTE_NAME);
+	hdr->program_checksum = cpu_to_le16(0); /* This is written later */
+
+	/* Compute the image checksum, this covers everything except the
+	 * ELF notes.
+	 */
+	bytes = sizeof(hdr->ehdr) + sizeof(hdr->phdr);
+	checksum = compute_ip_checksum(hdr, bytes);
+	for(i = 1; i < 4; i++) {
+		checksum = add_ip_checksums(bytes, checksum, 
+			compute_ip_checksum(seg[i].data, seg[i].data_size));
+		bytes += seg[i].mem_size;
+	}
+	hdr->program_checksum = cpu_to_le16(checksum);
+	seg[OEHDR].data = (unsigned char *)hdr;
 }
 
 static void usage(void)
 {
-	die("Usage: build [-b] vmlinux realmode compressed/vmlinux image");
+	die("Usage: build [-e] [-b] vmlinux realmode compressed/vmlinux image");
 }
 
 int main(int argc, char ** argv)
@@ -544,6 +750,12 @@
 	memset(oseg, 0, sizeof(oseg));
 	memset(&info, 0, sizeof(info));
 	info.is_big_kernel = 0;
+	info.is_elf = 0;
+	if (argc > 2 && (strcmp(argv[1], "-e") == 0)) {
+		info.is_elf = 1;
+		argc--;
+		argv++;
+	}
 	if (argc > 2 && (strcmp(argv[1], "-b") == 0)) {
 		info.is_big_kernel = 1;
 		argc--;
@@ -626,7 +838,7 @@
 	} else {
 		/* bzImage */
 		size_t unzip_bufsz;
-		
+
 		/* Compute the bzImage decompressor memory size */
 		unzip_bufsz = iseg[IKERN].data_size + info.unzip_overhang;
 		if (unzip_bufsz > oseg[OKERN].mem_size) {
@@ -653,7 +865,15 @@
 	info.setup_sectors = (oseg[OREAL].data_size - 512 + 511)/512;
 	if (info.setup_sectors < SETUP_SECTS)
 		info.setup_sectors = SETUP_SECTS;
+	if (info.is_elf) {
+		oseg[OEHDR].data_size = sizeof(struct elf_header);
+	}
 
+	/* Note: for ELF images I don't have to make OREAL
+	 * info.setup_sectors long, but it makes the conversion back
+	 * to a bzImage just a trivial header removal.
+	 */
+	oseg[OREAL].file_offset = oseg[OEHDR].file_offset + oseg[OEHDR].data_size;
 	oseg[OKERN].file_offset = oseg[OREAL].file_offset + (info.setup_sectors +1)*512;
 
 	/* Check and print the values to write back. */
@@ -692,6 +912,10 @@
 
 	/* Write the values back */
 	update_image(iseg, oseg, &info);
+	if (info.is_elf) {
+		build_elf_header(oseg, &info);
+		printf("elf_header_size=%d\n", oseg[OEHDR].data_size);
+	}
 
 	/* Write destination file */
 	image_fd = checked_open(image, O_RDWR | O_CREAT | O_TRUNC, 0666);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/