summaryrefslogtreecommitdiff
path: root/src/arch/i386/boot/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/i386/boot/boot.c')
-rw-r--r--src/arch/i386/boot/boot.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/arch/i386/boot/boot.c b/src/arch/i386/boot/boot.c
new file mode 100644
index 0000000000..84c71da800
--- /dev/null
+++ b/src/arch/i386/boot/boot.c
@@ -0,0 +1,182 @@
+#include <console/console.h>
+#include <ip_checksum.h>
+#include <boot/elf.h>
+#include <boot/elf_boot.h>
+#include <string.h>
+
+
+#ifndef CMD_LINE
+#define CMD_LINE ""
+#endif
+
+
+
+#define UPSZ(X) ((sizeof(X) + 3) &~3)
+
+static struct {
+ Elf_Bhdr hdr;
+ Elf_Nhdr ft_hdr;
+ unsigned char ft_desc[UPSZ(FIRMWARE_TYPE)];
+ Elf_Nhdr bl_hdr;
+ unsigned char bl_desc[UPSZ(BOOTLOADER)];
+ Elf_Nhdr blv_hdr;
+ unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)];
+ Elf_Nhdr cmd_hdr;
+ unsigned char cmd_desc[UPSZ(CMD_LINE)];
+} elf_boot_notes = {
+ .hdr = {
+ .b_signature = 0x0E1FB007,
+ .b_size = sizeof(elf_boot_notes),
+ .b_checksum = 0,
+ .b_records = 4,
+ },
+ .ft_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(FIRMWARE_TYPE),
+ .n_type = EBN_FIRMWARE_TYPE,
+ },
+ .ft_desc = FIRMWARE_TYPE,
+ .bl_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(BOOTLOADER),
+ .n_type = EBN_BOOTLOADER_NAME,
+ },
+ .bl_desc = BOOTLOADER,
+ .blv_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(BOOTLOADER_VERSION),
+ .n_type = EBN_BOOTLOADER_VERSION,
+ },
+ .blv_desc = BOOTLOADER_VERSION,
+ .cmd_hdr = {
+ .n_namesz = 0,
+ .n_descsz = sizeof(CMD_LINE),
+ .n_type = EBN_COMMAND_LINE,
+ },
+ .cmd_desc = CMD_LINE,
+};
+
+
+int elf_check_arch(Elf_ehdr *ehdr)
+{
+ return (
+ ((ehdr->e_machine == EM_386) || (ehdr->e_machine == EM_486)) &&
+ (ehdr->e_ident[EI_CLASS] == ELFCLASS32) &&
+ (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+ );
+
+}
+
+void jmp_to_elf_entry(void *entry, unsigned long buffer)
+{
+ extern unsigned char _ram_seg, _eram_seg;
+ unsigned long lb_start, lb_size;
+ unsigned long adjust, adjusted_boot_notes;
+ unsigned long type;
+
+ elf_boot_notes.hdr.b_checksum =
+ compute_ip_checksum(&elf_boot_notes, sizeof(elf_boot_notes));
+
+ type = 0x0E1FB007;
+ lb_start = (unsigned long)&_ram_seg;
+ lb_size = (unsigned long)(&_eram_seg - &_ram_seg);
+ adjust = buffer + lb_size - lb_start;
+
+ adjusted_boot_notes = (unsigned long)&elf_boot_notes;
+ adjusted_boot_notes += adjust;
+
+ printk_spew("entry = 0x%08lx\n", (unsigned long)entry);
+ printk_spew("lb_start = 0x%08lx\n", lb_start);
+ printk_spew("lb_size = 0x%08lx\n", lb_size);
+ printk_spew("adjust = 0x%08lx\n", adjust);
+ printk_spew("buffer = 0x%08lx\n", buffer);
+ printk_spew(" elf_boot_notes = 0x%08lx\n", (unsigned long)&elf_boot_notes);
+ printk_spew("adjusted_boot_notes = 0x%08lx\n", adjusted_boot_notes);
+
+ /* Jump to kernel */
+ __asm__ __volatile__(
+ " cld \n\t"
+ /* Save the callee save registers... */
+ " pushl %%esi\n\t"
+ " pushl %%edi\n\t"
+ " pushl %%ebx\n\t"
+ /* Save the parameters I was passed */
+ " pushl $0\n\t" /* 20 adjust */
+ " pushl %0\n\t" /* 16 lb_start */
+ " pushl %1\n\t" /* 12 buffer */
+ " pushl %2\n\t" /* 8 lb_size */
+ " pushl %3\n\t" /* 4 entry */
+ " pushl %4\n\t" /* 0 elf_boot_notes */
+ /* Compute the adjustment */
+ " xorl %%eax, %%eax\n\t"
+ " subl 16(%%esp), %%eax\n\t"
+ " addl 12(%%esp), %%eax\n\t"
+ " addl 8(%%esp), %%eax\n\t"
+ " movl %%eax, 20(%%esp)\n\t"
+ /* Place a copy of linuxBIOS in it's new location */
+ /* Move ``longs'' the linuxBIOS size is 4 byte aligned */
+ " movl 12(%%esp), %%edi\n\t"
+ " addl 8(%%esp), %%edi\n\t"
+ " movl 16(%%esp), %%esi\n\t"
+ " movl 8(%%esp), %%ecx\n\n"
+ " shrl $2, %%ecx\n\t"
+ " rep movsl\n\t"
+
+ /* Adjust the stack pointer to point into the new linuxBIOS image */
+ " addl 20(%%esp), %%esp\n\t"
+ /* Adjust the instruction pointer to point into the new linuxBIOS image */
+ " movl $1f, %%eax\n\t"
+ " addl 20(%%esp), %%eax\n\t"
+ " jmp *%%eax\n\t"
+ "1: \n\t"
+
+ /* Copy the linuxBIOS bounce buffer over linuxBIOS */
+ /* Move ``longs'' the linuxBIOS size is 4 byte aligned */
+ " movl 16(%%esp), %%edi\n\t"
+ " movl 12(%%esp), %%esi\n\t"
+ " movl 8(%%esp), %%ecx\n\t"
+ " shrl $2, %%ecx\n\t"
+ " rep movsl\n\t"
+
+ /* Now jump to the loaded image */
+ " movl $0x0E1FB007, %%eax\n\t"
+ " movl 0(%%esp), %%ebx\n\t"
+ " call *4(%%esp)\n\t"
+
+ /* The loaded image returned? */
+ " cli \n\t"
+ " cld \n\t"
+
+ /* Copy the saved copy of linuxBIOS where linuxBIOS runs */
+ /* Move ``longs'' the linuxBIOS size is 4 byte aligned */
+ " movl 16(%%esp), %%edi\n\t"
+ " movl 12(%%esp), %%esi\n\t"
+ " addl 8(%%esp), %%esi\n\t"
+ " movl 8(%%esp), %%ecx\n\t"
+ " shrl $2, %%ecx\n\t"
+ " rep movsl\n\t"
+
+ /* Adjust the stack pointer to point into the old linuxBIOS image */
+ " subl 20(%%esp), %%esp\n\t"
+
+ /* Adjust the instruction pointer to point into the old linuxBIOS image */
+ " movl $1f, %%eax\n\t"
+ " subl 20(%%esp), %%eax\n\t"
+ " jmp *%%eax\n\t"
+ "1: \n\t"
+
+ /* Drop the parameters I was passed */
+ " addl $24, %%esp\n\t"
+
+ /* Restore the callee save registers */
+ " popl %%ebx\n\t"
+ " popl %%edi\n\t"
+ " popl %%esi\n\t"
+
+ ::
+ "g" (lb_start), "g" (buffer), "g" (lb_size),
+ "g" (entry), "g"(adjusted_boot_notes)
+ );
+}
+
+