summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/boot/Kconfig20
-rw-r--r--arch/x86/boot/Makefile13
-rw-r--r--arch/x86/boot/a20.c170
-rw-r--r--arch/x86/boot/bioscall.S99
-rw-r--r--arch/x86/boot/boot.h193
-rw-r--r--arch/x86/boot/boot_hdisk.S176
-rw-r--r--arch/x86/boot/boot_main.S58
-rw-r--r--arch/x86/boot/main_entry.c44
-rw-r--r--arch/x86/boot/pmjump.S89
-rw-r--r--arch/x86/boot/prepare_uboot.c86
-rw-r--r--arch/x86/boot/regs.c34
-rw-r--r--arch/x86/boot/tty.c45
12 files changed, 1027 insertions, 0 deletions
diff --git a/arch/x86/boot/Kconfig b/arch/x86/boot/Kconfig
new file mode 100644
index 0000000000..cdb82e46d9
--- /dev/null
+++ b/arch/x86/boot/Kconfig
@@ -0,0 +1,20 @@
+if X86_BIOS_BRINGUP
+
+menu "BIOS boot source "
+
+config X86_HDBOOT
+ bool "HD boot"
+ help
+ Add code to boot from harddisk
+
+config X86_VESA
+ bool
+ default y if X86_GENERIC_HAS_VIDEO
+
+config X86_VGA
+ bool
+ default y if X86_GENERIC_HAS_VIDEO
+
+endmenu
+
+endif
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
new file mode 100644
index 0000000000..b92b4750fc
--- /dev/null
+++ b/arch/x86/boot/Makefile
@@ -0,0 +1,13 @@
+
+CPPFLAGS += -D__I386__ -fno-strict-aliasing -m32 -g -Os -march=i386 \
+ -mregparm=3 -fno-strict-aliasing -fomit-frame-pointer -ffreestanding \
+ -fno-toplevel-reorder -fno-unit-at-a-time -fno-stack-protector \
+ -mpreferred-stack-boundary=2
+
+obj-$(CONFIG_X86_HDBOOT) += boot_main.o boot_hdisk.o
+
+obj-$(CONFIG_X86_BIOS_BRINGUP) += prepare_uboot.o a20.o bioscall.o regs.o tty.o pmjump.o main_entry.o
+
+obj-$(CONFIG_X86_VESA) += console_vesa.o
+obj-$(CONFIG_X86_VGA) += console_vga.o
+obj-$(CONFIG_X86_SERIAL) += console_serial.o
diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
new file mode 100644
index 0000000000..4b61d91930
--- /dev/null
+++ b/arch/x86/boot/a20.c
@@ -0,0 +1,170 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright 2007-2008 rPath, Inc. - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author H. Peter Anvin
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Enable A20 gate (return -1 on failure)
+ */
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include "boot.h"
+
+#define MAX_8042_LOOPS 100000
+#define MAX_8042_FF 32
+
+/* be aware of: */
+THIS_IS_REALMODE_CODE
+
+static int __bootcode empty_8042(void)
+{
+ u8 status;
+ int loops = MAX_8042_LOOPS;
+ int ffs = MAX_8042_FF;
+
+ while (loops--) {
+ io_delay();
+
+ status = inb(0x64);
+ if (status == 0xff) {
+ /* FF is a plausible, but very unlikely status */
+ if (!--ffs)
+ return -1; /* Assume no KBC present */
+ }
+ if (status & 1) {
+ /* Read and discard input data */
+ io_delay();
+ (void)inb(0x60);
+ } else if (!(status & 2)) {
+ /* Buffers empty, finished! */
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/* Returns nonzero if the A20 line is enabled. The memory address
+ used as a test is the int $0x80 vector, which should be safe. */
+
+#define A20_TEST_ADDR (4*0x80)
+#define A20_TEST_SHORT 32
+#define A20_TEST_LONG 2097152 /* 2^21 */
+
+static int __bootcode a20_test(int loops)
+{
+ int ok = 0;
+ int saved, ctr;
+
+ set_fs(0x0000);
+ set_gs(0xffff);
+
+ saved = ctr = rdfs32(A20_TEST_ADDR);
+
+ while (loops--) {
+ wrfs32(++ctr, A20_TEST_ADDR);
+ io_delay(); /* Serialize and make delay constant */
+ ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
+ if (ok)
+ break;
+ }
+
+ wrfs32(saved, A20_TEST_ADDR);
+ return ok;
+}
+
+/* Quick test to see if A20 is already enabled */
+static int __bootcode a20_test_short(void)
+{
+ return a20_test(A20_TEST_SHORT);
+}
+
+/* Longer test that actually waits for A20 to come on line; this
+ is useful when dealing with the KBC or other slow external circuitry. */
+static int __bootcode a20_test_long(void)
+{
+ return a20_test(A20_TEST_LONG);
+}
+
+static void __bootcode enable_a20_bios(void)
+{
+ struct biosregs ireg;
+
+ initregs(&ireg);
+ ireg.ax = 0x2401;
+ intcall(0x15, &ireg, NULL);
+}
+
+static void __bootcode enable_a20_kbc(void)
+{
+ empty_8042();
+
+ outb(0xd1, 0x64); /* Command write */
+ empty_8042();
+
+ outb(0xdf, 0x60); /* A20 on */
+ empty_8042();
+
+ outb(0xff, 0x64); /* Null command, but UHCI wants it */
+ empty_8042();
+}
+
+static void __bootcode enable_a20_fast(void)
+{
+ u8 port_a;
+
+ port_a = inb(0x92); /* Configuration port A */
+ port_a |= 0x02; /* Enable A20 */
+ port_a &= ~0x01; /* Do not reset machine */
+ outb(port_a, 0x92);
+}
+
+/*
+ * Actual routine to enable A20; return 0 on ok, -1 on failure
+ */
+
+#define A20_ENABLE_LOOPS 255 /* Number of times to try */
+
+int __bootcode enable_a20(void)
+{
+ int loops = A20_ENABLE_LOOPS;
+ int kbc_err;
+
+ while (loops--) {
+ /* First, check to see if A20 is already enabled
+ (legacy free, etc.) */
+ if (a20_test_short())
+ return 0;
+
+ /* Next, try the BIOS (INT 0x15, AX=0x2401) */
+ enable_a20_bios();
+ if (a20_test_short())
+ return 0;
+
+ /* Try enabling A20 through the keyboard controller */
+ kbc_err = empty_8042();
+
+ if (a20_test_short())
+ return 0; /* BIOS worked, but with delayed reaction */
+
+ if (!kbc_err) {
+ enable_a20_kbc();
+ if (a20_test_long())
+ return 0;
+ }
+
+ /* Finally, try enabling the "fast A20 gate" */
+ enable_a20_fast();
+ if (a20_test_long())
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/arch/x86/boot/bioscall.S b/arch/x86/boot/bioscall.S
new file mode 100644
index 0000000000..84d2577eb6
--- /dev/null
+++ b/arch/x86/boot/bioscall.S
@@ -0,0 +1,99 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 2009 Intel Corporation; author H. Peter Anvin
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2 or (at your
+ * option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * "Glove box" for BIOS calls. Avoids the constant problems with BIOSes
+ * touching registers they shouldn't be.
+ */
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+ .file "bioscall.S"
+ .code16
+ .section .boot.text.intcall, "ax"
+
+ .globl intcall
+ .type intcall, @function
+intcall:
+ /* Self-modify the INT instruction. Ugly, but works. */
+ cmpb %al, 3f
+ je 1f
+ movb %al, 3f
+ jmp 1f /* Synchronize pipeline */
+1:
+ /* Save state */
+ pushfl
+ pushw %fs
+ pushw %gs
+ pushal
+
+ /* Copy input state to stack frame */
+ subw $44, %sp
+ movw %dx, %si
+ movw %sp, %di
+ movw $11, %cx
+ rep; movsd
+
+ /* Pop full state from the stack */
+ popal
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+ popfl
+
+ /* Actual INT */
+ .byte 0xcd /* INT opcode */
+3: .byte 0
+
+ /* Push full state to the stack */
+ pushfl
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ pushal
+
+ /* Re-establish C environment invariants */
+ cld
+ movzwl %sp, %esp
+ movw %cs, %ax
+ movw %ax, %ds
+ movw %ax, %es
+
+ /* Copy output state from stack frame */
+ movw 68(%esp), %di /* Original %cx == 3rd argument */
+ andw %di, %di
+ jz 4f
+ movw %sp, %si
+ movw $11, %cx
+ rep; movsd
+4: addw $44, %sp
+
+ /* Restore state and return */
+ popal
+ popw %gs
+ popw %fs
+ popfl
+ retl
+ .size intcall, .-intcall
+
+/* ------------------------------------------------------------------------ */
+ .code16
+ .section .boot.text.die, "ax"
+
+ .globl die
+ .type die, @function
+die:
+ hlt
+ jmp die
+ .size die, .-die
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
new file mode 100644
index 0000000000..d98b0661cd
--- /dev/null
+++ b/arch/x86/boot/boot.h
@@ -0,0 +1,193 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright 2007 rPath, Inc. - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author H. Peter Anvin
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file
+ * @brief Main declarations for the real mode code
+ */
+
+#ifndef BOOT_BOOT_H
+#define BOOT_BOOT_H
+
+#define STACK_SIZE 512 /* Minimum number of bytes for stack */
+
+/** Carry flag */
+#define X86_EFLAGS_CF 0x00000001
+
+/** PE flag */
+#define X86_CR0_PE 0x00000001
+
+#ifndef __ASSEMBLY__
+
+#include <types.h>
+
+/* we are still in real mode here! */
+#define THIS_IS_REALMODE_CODE asm(".code16gcc");
+
+struct biosregs {
+ union {
+ struct {
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t _esp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t _fsgs;
+ uint32_t _dses;
+ uint32_t eflags;
+ };
+ struct {
+ uint16_t di, hdi;
+ uint16_t si, hsi;
+ uint16_t bp, hbp;
+ uint16_t _sp, _hsp;
+ uint16_t bx, hbx;
+ uint16_t dx, hdx;
+ uint16_t cx, hcx;
+ uint16_t ax, hax;
+ uint16_t gs, fs;
+ uint16_t es, ds;
+ uint16_t flags, hflags;
+ };
+ struct {
+ uint8_t dil, dih, edi2, edi3;
+ uint8_t sil, sih, esi2, esi3;
+ uint8_t bpl, bph, ebp2, ebp3;
+ uint8_t _spl, _sph, _esp2, _esp3;
+ uint8_t bl, bh, ebx2, ebx3;
+ uint8_t dl, dh, edx2, edx3;
+ uint8_t cl, ch, ecx2, ecx3;
+ uint8_t al, ah, eax2, eax3;
+ };
+ };
+};
+
+/* functions in the realmode part */
+extern int enable_a20(void);
+extern void initregs(struct biosregs *regs);
+extern void intcall(uint8_t int_no, const struct biosregs *ireg, struct biosregs *oreg);
+extern void boot_puts(char*);
+extern void __attribute__((noreturn)) die(void);
+extern void __attribute__((noreturn)) protected_mode_jump(void);
+
+struct gdt_ptr {
+ uint16_t len;
+ uint32_t ptr;
+} __attribute__((packed));
+
+/* These functions are used to reference data in other segments. */
+
+static inline uint16_t ds(void)
+{
+ uint16_t seg;
+ asm("movw %%ds,%0" : "=rm" (seg));
+ return seg;
+}
+
+static inline void set_fs(uint16_t seg)
+{
+ asm volatile("movw %0,%%fs" : : "rm" (seg));
+}
+
+static inline uint16_t fs(void)
+{
+ uint16_t seg;
+ asm volatile("movw %%fs,%0" : "=rm" (seg));
+ return seg;
+}
+
+static inline void set_gs(uint16_t seg)
+{
+ asm volatile("movw %0,%%gs" : : "rm" (seg));
+}
+
+static inline uint16_t gs(void)
+{
+ uint16_t seg;
+ asm volatile("movw %%gs,%0" : "=rm" (seg));
+ return seg;
+}
+
+typedef unsigned int addr_t;
+
+static inline uint8_t rdfs8(addr_t addr)
+{
+ uint8_t v;
+ asm volatile("movb %%fs:%1,%0" : "=q" (v) : "m" (*(uint8_t *)addr));
+ return v;
+}
+static inline uint16_t rdfs16(addr_t addr)
+{
+ uint16_t v;
+ asm volatile("movw %%fs:%1,%0" : "=r" (v) : "m" (*(uint16_t *)addr));
+ return v;
+}
+static inline uint32_t rdfs32(addr_t addr)
+{
+ uint32_t v;
+ asm volatile("movl %%fs:%1,%0" : "=r" (v) : "m" (*(uint32_t *)addr));
+ return v;
+}
+
+static inline void wrfs8(uint8_t v, addr_t addr)
+{
+ asm volatile("movb %1,%%fs:%0" : "+m" (*(uint8_t *)addr) : "qi" (v));
+}
+static inline void wrfs16(uint16_t v, addr_t addr)
+{
+ asm volatile("movw %1,%%fs:%0" : "+m" (*(uint16_t *)addr) : "ri" (v));
+}
+static inline void wrfs32(uint32_t v, addr_t addr)
+{
+ asm volatile("movl %1,%%fs:%0" : "+m" (*(uint32_t *)addr) : "ri" (v));
+}
+
+static inline uint8_t rdgs8(addr_t addr)
+{
+ uint8_t v;
+ asm volatile("movb %%gs:%1,%0" : "=q" (v) : "m" (*(uint8_t *)addr));
+ return v;
+}
+static inline uint16_t rdgs16(addr_t addr)
+{
+ uint16_t v;
+ asm volatile("movw %%gs:%1,%0" : "=r" (v) : "m" (*(uint16_t *)addr));
+ return v;
+}
+static inline uint32_t rdgs32(addr_t addr)
+{
+ uint32_t v;
+ asm volatile("movl %%gs:%1,%0" : "=r" (v) : "m" (*(uint32_t *)addr));
+ return v;
+}
+
+static inline void wrgs8(uint8_t v, addr_t addr)
+{
+ asm volatile("movb %1,%%gs:%0" : "+m" (*(uint8_t *)addr) : "qi" (v));
+}
+static inline void wrgs16(uint16_t v, addr_t addr)
+{
+ asm volatile("movw %1,%%gs:%0" : "+m" (*(uint16_t *)addr) : "ri" (v));
+}
+static inline void wrgs32(uint32_t v, addr_t addr)
+{
+ asm volatile("movl %1,%%gs:%0" : "+m" (*(uint32_t *)addr) : "ri" (v));
+}
+
+/** use the built in memset function for the real mode code */
+#define memset(d,c,l) __builtin_memset(d,c,l)
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* BOOT_BOOT_H */
diff --git a/arch/x86/boot/boot_hdisk.S b/arch/x86/boot/boot_hdisk.S
new file mode 100644
index 0000000000..40388e993d
--- /dev/null
+++ b/arch/x86/boot/boot_hdisk.S
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 Juergen Beisert, Pengutronix
+ *
+ * This code was inspired by the GRUB2 project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+/**
+ * @file
+ * @brief Loading the barebox image from a disk drive in LBA mode
+ */
+
+/**
+ * @fn void real_start(void)
+ * @brief A very simple and small loader to fetch all required sectors
+ * from the boot media.
+ */
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+ .file "boot_hdisk.S"
+ .code16
+
+ /*
+ * These symbols are generated by the linker, because they need a
+ * special layout. This layout is needed to be able to setup this
+ * bootloader by patching the binary when it gets stored into the
+ * master boot record.
+ */
+ .extern indirect_sector_lba
+ .extern boot_stack
+ .extern start_pre_uboot
+ .extern boot_disk
+ .section .boot_code, "ax"
+
+ .globl real_start
+ .type real_start, @function
+
+real_start:
+
+ xorw %ax, %ax /* set up %ds and %ss as offset from 0 */
+ movw %ax, %ds
+ movw %ax, %ss
+
+ /* set up the REAL stack */
+ movw $boot_stack, %sp
+
+ sti /* we're safe again */
+
+ /* save drive reference first thing! */
+ movb %dl, boot_disk
+ pushw %dx
+
+ movw $notification_string, %si
+ call output_message
+
+ /*
+ * This boot code only supports LBA. We fail here, if the BIOS
+ * does not support LBA for the harddisk
+ */
+
+ /* check if LBA is supported */
+ movb $0x41, %ah
+ movw $0x55aa, %bx
+ int $0x13
+
+ /*
+ * %dl may have been clobbered by INT 13, AH=41H.
+ * This happens, for example, with AST BIOS 1.04.
+ */
+ popw %dx
+ pushw %dx
+
+ /* stop if no LBA support */
+ jc no_lba
+ cmpw $0xaa55, %bx
+ jne no_lba
+ andw $1, %cx
+ jz no_lba
+
+lba_mode:
+ /*
+ * Load the indirect sector. Its content is ready for use,
+ * provided by the installer
+ */
+ movw $indirect_sector_lba, %si
+ movb $0x42, %ah
+ int $0x13
+ jc no_lba /* error? Then die */
+
+ /*
+ * Now loop through all valid entries in the indirect sector
+ */
+ movw $indirect_area, %si
+
+load_loop:
+ /*
+ * Stop if this "Disk Address Packet Structure" is invalid
+ * We call it invalid, if the size member is zero. If it is invalid
+ * we are optimistic and calling the loaded image
+ */
+ movw (%si), %ax
+ cmpw $0x0000, %ax
+ je start_main
+
+ /*
+ * Load this entry
+ */
+ movb $0x42, %ah
+ int $0x13
+ jc no_lba
+
+ addw (%si), %si /* next entry */
+ cmpw $indirect_area + 512, %si
+ jne load_loop
+ /*
+ * fall through to start u-boot.
+ */
+start_main:
+ movw $jmp_string, %si
+ call output_message
+ jmp start_pre_uboot
+/*
+ * die if there is no LBA support
+ */
+no_lba: movw $chs_string, %si
+ call output_message
+ hlt
+
+/*
+ * message: write the string pointed to by %si
+ *
+ * WARNING: trashes %si, %ax, and %bx
+ */
+
+/*
+ * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
+ * %ah = 0xe %al = character
+ * %bh = page %bl = foreground color (graphics modes)
+ */
+
+1:
+ movw $0x0001, %bx
+ movb $0xe, %ah
+ int $0x10 /* display this char */
+
+output_message:
+ lodsb
+ cmpb $0, %al
+ jne 1b /* if not end of string, next char */
+ ret
+
+/* ---------------------------------------------------------------------- */
+
+ .section .boot_data
+
+notification_string: .asciz "UBOOT2 "
+chs_string: .asciz "CHS "
+jmp_string: .asciz "JMP "
+
+#endif
diff --git a/arch/x86/boot/boot_main.S b/arch/x86/boot/boot_main.S
new file mode 100644
index 0000000000..f3d248ae5b
--- /dev/null
+++ b/arch/x86/boot/boot_main.S
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Juergen Beisert, Pengutronix
+ *
+ * This code was inspired by the GRUB2 project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+/**
+ * @file
+ * @brief Common boot sector main routine to be entered by the BIOS
+ */
+/**
+ * @fn void _start(void)
+ *
+ * @brief Fix segment:offset settings of some buggy BIOSs
+ */
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+ .file "boot_main.S"
+ .code16
+
+ .extern real_start
+
+ .section .boot_start, "ax"
+ .type _start, @function
+
+ /*
+ * The BIOS loads this code to address 0x00007c00.
+ * The code should be called with CS:IP 0:0x7c00 (hopefully).
+ */
+ .globl _start
+_start:
+ cli /* we're not safe here! */
+ /*
+ * It seems there are implementations in the wild which call this
+ * code with CS:IP 0x07C0:0000 instead. We fix it immediately.
+ */
+ ljmp $0, $real_start
+
+ .size _start, .-_start
+
+#endif
diff --git a/arch/x86/boot/main_entry.c b/arch/x86/boot/main_entry.c
new file mode 100644
index 0000000000..5f199e985b
--- /dev/null
+++ b/arch/x86/boot/main_entry.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 Juergen Beisert, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+/**
+ * @file
+ * @brief Start of the 32 bit flat mode
+ */
+
+#include <string.h>
+
+/* These symbols are generated by the linker */
+extern char __bss_start;
+extern char __bss_end;
+
+extern void start_barebox(void);
+
+/**
+ * Called plainly from assembler that switches from real to flat mode
+ *
+ * @note The C environment isn't initialized yet
+ */
+void uboot_entry(void)
+{
+ /* clear the BSS first */
+ memset(&__bss_start, 0x00, &__bss_end - &__bss_start);
+ start_barebox();
+}
diff --git a/arch/x86/boot/pmjump.S b/arch/x86/boot/pmjump.S
new file mode 100644
index 0000000000..d48e1983f1
--- /dev/null
+++ b/arch/x86/boot/pmjump.S
@@ -0,0 +1,89 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright 2007 rPath, Inc. - All Rights Reserved
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file
+ * @brief The actual transition into protected mode
+ *
+ * Note: This function is running in flat and real mode. Due to some
+ * other restrictions it must running from an address space below 0x10000
+ */
+
+/**
+ * @fn void protected_mode_jump(void)
+ * @brief Switches the first time from real mode to flat mode
+ */
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include <asm/modes.h>
+#include "boot.h"
+
+ .file "pmjump.S"
+ .code16
+ .section .boot.text.protected_mode_jump, "ax"
+
+ .globl protected_mode_jump
+ .type protected_mode_jump, @function
+
+protected_mode_jump:
+ jmp 1f /* Short jump to serialize on 386/486 */
+1:
+
+ movw $__BOOT_DS, %cx
+ movw $__BOOT_TSS, %di
+
+ movl %cr0, %edx
+ orb $X86_CR0_PE, %dl /* enable protected mode */
+ movl %edx, %cr0
+
+ /* Transition to 32-bit flat mode */
+ data32 ljmp $__BOOT_CS, $in_pm32
+
+/* ------------------------------------------------------------------------ */
+
+ .section ".text.in_pm32","ax"
+ .code32
+
+ .extern uboot_entry
+ .extern __bss_end
+
+ .type in_pm32, @function
+in_pm32:
+ # Set up data segments for flat 32-bit mode
+ movl %ecx, %ds
+ movl %ecx, %es
+ movl %ecx, %fs
+ movl %ecx, %gs
+ movl %ecx, %ss
+/*
+ * Our flat mode code uses its own stack area behind the bss. With this we
+ * are still able to return to real mode temporarely
+ */
+ movl $__bss_end + 32768, %esp
+
+ # Set up TR to make Intel VT happy
+ ltr %di
+
+ # Clear registers to allow for future extensions to the
+ # 32-bit boot protocol
+ xorl %ecx, %ecx
+ xorl %edx, %edx
+ xorl %ebx, %ebx
+ xorl %ebp, %ebp
+ xorl %edi, %edi
+
+ # Set up LDTR to make Intel VT happy
+ lldt %cx
+
+ jmp uboot_entry
+
+ .size protected_mode_jump, .-protected_mode_jump
+
+#endif
diff --git a/arch/x86/boot/prepare_uboot.c b/arch/x86/boot/prepare_uboot.c
new file mode 100644
index 0000000000..a68aceddb6
--- /dev/null
+++ b/arch/x86/boot/prepare_uboot.c
@@ -0,0 +1,86 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright 2007 rPath, Inc. - All Rights Reserved
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Prepare the machine for transition to protected mode.
+ */
+#include <asm/segment.h>
+#include <asm/modes.h>
+#include <asm/io.h>
+#include "boot.h"
+
+/* be aware of: */
+THIS_IS_REALMODE_CODE
+
+/*
+ * While we are in flat mode, we can't handle interrupts. But we can't
+ * switch them off for ever in the PIC, because we need them again while
+ * entering real mode code again and again....
+ */
+static void __bootcode realmode_switch_hook(void)
+{
+ asm volatile("cli");
+ outb(0x80, 0x70); /* Disable NMI */
+ io_delay();
+}
+
+/*
+ * Reset IGNNE# if asserted in the FPU.
+ */
+static void __bootcode reset_coprocessor(void)
+{
+ outb(0, 0xf0);
+ io_delay();
+ outb(0, 0xf1);
+ io_delay();
+}
+
+/**
+ * Setup and register the global descriptor table (GDT)
+ *
+ * @note This is for the first time only
+ */
+static void __bootcode setup_gdt(void)
+{
+ /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead
+ of the gdt_ptr contents. Thus, make it static so it will
+ stay in memory, at least long enough that we switch to the
+ proper kernel GDT. */
+ static struct gdt_ptr __bootdata gdt_ptr;
+
+ gdt_ptr.len = gdt_size - 1;
+ gdt_ptr.ptr = (uint32_t)&gdt + (ds() << 4);
+
+ asm volatile("lgdtl %0" : : "m" (gdt_ptr));
+}
+
+static char a20_message[] __bootdata = "A20 gate not responding, unable to boot...\n";
+
+/*
+ * Actual invocation sequence
+ */
+void __bootcode start_pre_uboot(void)
+{
+ /* Hook before leaving real mode, also disables interrupts */
+ realmode_switch_hook();
+
+ /* Enable the A20 gate */
+ if (enable_a20()) {
+ boot_puts(a20_message);
+ die();
+ }
+
+ /* Reset coprocessor (IGNNE#) */
+ reset_coprocessor();
+
+ setup_gdt();
+ /* Actual transition to protected mode... */
+ protected_mode_jump();
+}
diff --git a/arch/x86/boot/regs.c b/arch/x86/boot/regs.c
new file mode 100644
index 0000000000..ddc515518c
--- /dev/null
+++ b/arch/x86/boot/regs.c
@@ -0,0 +1,34 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 2009 Intel Corporation; author H. Peter Anvin
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2 or (at your
+ * option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file
+ * @brief Simple helper function for initializing a register set.
+ *
+ * Note that this sets EFLAGS_CF in the input register set; this
+ * makes it easier to catch functions which do nothing but don't
+ * explicitly set CF.
+ */
+
+#include <asm/segment.h>
+#include "boot.h"
+
+/* be aware of: */
+THIS_IS_REALMODE_CODE
+
+void __bootcode initregs(struct biosregs *reg)
+{
+ memset(reg, 0, sizeof *reg);
+ reg->eflags |= X86_EFLAGS_CF;
+ reg->ds = ds();
+ reg->es = ds();
+ reg->fs = fs();
+ reg->gs = gs();
+}
diff --git a/arch/x86/boot/tty.c b/arch/x86/boot/tty.c
new file mode 100644
index 0000000000..a81671be3b
--- /dev/null
+++ b/arch/x86/boot/tty.c
@@ -0,0 +1,45 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright 2007 rPath, Inc. - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author H. Peter Anvin
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file
+ * @brief Very simple screen I/O for the initialization stage
+ *
+ * @todo Probably should add very simple serial I/O?
+ * @attention This is real mode code!
+ */
+
+#include <asm/segment.h>
+#include "boot.h"
+
+/* be aware of: */
+THIS_IS_REALMODE_CODE
+
+static void __bootcode putchar(int ch)
+{
+ struct biosregs ireg;
+
+ if (ch == '\n')
+ putchar('\r'); /* \n -> \r\n */
+
+ initregs(&ireg);
+ ireg.bx = 0x0007;
+ ireg.cx = 0x0001;
+ ireg.ah = 0x0e;
+ ireg.al = ch;
+ intcall(0x10, &ireg, NULL);
+}
+
+void __bootcode boot_puts(char *str)
+{
+ while (*str)
+ putchar(*str++);
+}