x86 boot enhancements, bzELF support 11/11

x86 boot enhancements, bzELF support 11/11

Post by Eric W. Biederm » Fri, 19 Apr 2002 02:20:12



Linus please apply,

Add support for generating an ELF executable kernel.  External
tools are only needed now to manipulate the command line,
and to add a ramdisk.  For netbooting this is very handy.

To revert to a bzImage you simply strip off the ELF header,
this should reduce concerns of the kernel boot format forking.

For my uses an ELF image is the primary format I work with.

In addition with all of these patches applied, the net
growth of a real mode kernel is 35 bytes. An ELF header cost
an additional 324 bytes, and in the kernel proper the size
change is pure noise.

Eric

diff -uNr linux-2.5.8.boot.linuxbios/arch/i386/Makefile linux-2.5.8.boot.elf/arch/i386/Makefile
--- linux-2.5.8.boot.linuxbios/arch/i386/Makefile       Tue Apr 16 12:14:31 2002
+++ linux-2.5.8.boot.elf/arch/i386/Makefile     Wed Apr 17 02:06:40 2002
@@ -119,6 +119,9 @@
 bzImage: vmlinux
        @$(MAKEBOOT) bzImage

+bzElf: vmlinux
+       @$(MAKEBOOT) bzElf
+
 compressed: zImage

 zlilo: vmlinux
diff -uNr linux-2.5.8.boot.linuxbios/arch/i386/boot/Makefile linux-2.5.8.boot.elf/arch/i386/boot/Makefile
--- linux-2.5.8.boot.linuxbios/arch/i386/boot/Makefile  Wed Apr 17 01:20:18 2002
+++ linux-2.5.8.boot.elf/arch/i386/boot/Makefile        Wed Apr 17 02:06:40 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

Binary files linux-2.5.8.boot.linuxbios/arch/i386/boot/bzElf and linux-2.5.8.boot.elf/arch/i386/boot/bzElf differ
diff -uNr linux-2.5.8.boot.linuxbios/arch/i386/boot/tools/build.c linux-2.5.8.boot.elf/arch/i386/boot/tools/build.c
--- linux-2.5.8.boot.linuxbios/arch/i386/boot/tools/build.c     Wed Apr 17 01:22:21 2002
+++ linux-2.5.8.boot.elf/arch/i386/boot/tools/build.c   Wed Apr 17 02:06:51 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

1. x86 boot enhancements, link enhancements 2/11

#2  2.5.8.boot.vmlinuxlds
============================================================
- i386/Makefile remove bogus linker command line of -e stext
- Fix vmlinux.lds so vmlinux knows it loads at 0x100000 (1MB)
- Fix vmlinux.lds so we correctly use startup_32 for our entry point
- Make startup_32 global

diff -uNr linux-2.5.8.boot.boot_params/arch/i386/Makefile linux-2.5.8.boot.vmlinuxlds/arch/i386/Makefile
--- linux-2.5.8.boot.boot_params/arch/i386/Makefile     Thu Apr 12 13:20:31 2001

 LD=$(CROSS_COMPILE)ld -m elf_i386
 OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
-LDFLAGS=-e stext
+LDFLAGS=
 LINKFLAGS =-T $(TOPDIR)/arch/i386/vmlinux.lds $(LDFLAGS)

 CFLAGS += -pipe
diff -uNr linux-2.5.8.boot.boot_params/arch/i386/kernel/head.S linux-2.5.8.boot.vmlinuxlds/arch/i386/kernel/head.S
--- linux-2.5.8.boot.boot_params/arch/i386/kernel/head.S        Wed Mar 20 07:18:31 2002

  *
  * On entry, %esi points to the real-mode code as a 32-bit pointer.
  */
-startup_32:
+ENTRY(startup_32)
 /*
  * Set segments to known values
  */
diff -uNr linux-2.5.8.boot.boot_params/arch/i386/vmlinux.lds linux-2.5.8.boot.vmlinuxlds/arch/i386/vmlinux.lds
--- linux-2.5.8.boot.boot_params/arch/i386/vmlinux.lds  Sun Mar 10 20:09:08 2002

  */
 OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
 OUTPUT_ARCH(i386)
-ENTRY(_start)
+physical_startup_32 = startup_32 - 0xC0000000;
+ENTRY(physical_startup_32)
+PHDRS
+{
+       text PT_LOAD AT(0x100000);
+
+}
 SECTIONS
 {

        *(.text)
        *(.fixup)
        *(.gnu.warning)
-       } = 0x9090
+       } :text = 0x9090

   _etext = .;                  /* End of text section */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

2. How do I test the network speed between 2 linux boxes?

3. x86 boot enhancements, x86 boot protocol 2.04 9/11

4. pthread - threads and semaphores - help!

5. x86 boot enhancements, linuxbios support 10/11

6. Orb 2.2gb Zip style removable disk drive.

7. x86 Boot enhancements, boot parameters 1/11

8. X copy/paste issue

9. x86 boot enhancements, boot time heap 5/11

10. x86 Boot enhancements, boot params 1/11

11. x86 boot enhancements, boot bean counting 8/11

12. x86 boot enhancements, Clean up the 32bit entry points 6/11

13. x86 boot enhancements, cleanups 4/11