bImageprefix.S revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/*
Copyright (C) 2000, Entity Cyber, Inc.
Authors: Gary Byers (gb@thinguin.org)
Marty Connor (mdc@thinguin.org)
Eric Biederman (ebiederman@lnxi.com)
the linux kernel.
This software may be used and distributed according to the terms
of the GNU Public License (GPL), incorporated herein by reference.
Description:
This is just a little bit of code and data that can get prepended
to an Etherboot ROM image in order to allow LILO to load the
result as if it were a Linux kernel image.
A real Linux kernel image consists of a one-sector boot loader
(to load the image from a floppy disk), followed a few sectors
of setup code, followed by the kernel code itself. There's
a table in the first sector (starting at offset 497) that indicates
how many sectors of setup code follow the first sector and which
contains some other parameters that aren't interesting in this
case.
When LILO loads the sectors that comprise a kernel image, it doesn't
execute the code in the first sector (since that code would try to
load the image from a floppy disk.) The code in the first sector
below doesn't expect to get executed (and prints an error message
if it ever -is- executed.) LILO's only interested in knowing the
number of setup sectors advertised in the table (at offset 497 in
the first sector.)
Etherboot doesn't require much in the way of setup code.
Historically, the Linux kernel required at least 4 sectors of
setup code. Current versions of LILO look at the byte at
offset 497 in the first sector to indicate how many sectors
of setup code are contained in the image.
The setup code that is present here does a lot of things
exactly the way the linux kernel does them instead of in
ways more typical of etherboot. Generally this is so
the code can be strongly compatible with the linux kernel.
In addition the general etherboot technique of enabling the a20
after we switch into protected mode does not work if etherboot
is being loaded at 1MB.
*/
#ifdef GAS291
#else
/* newer GAS295 require #define LJMPI(x) ljmp *x */
#endif
/* Simple and small GDT entries for booting only */
#define GDT_ENTRY_BOOT_CS 2
/* Signature words to ensure LILO loaded us right */
#define SIG1 0xAA55
#define SIG2 0x5A5A
.text
.org 0
/*
This is a minimal boot sector. If anyone tries to execute it (e.g., if
a .lilo file is dd'ed to a floppy), print an error message.
*/
go:
of bootsect + room for stack */
int $0x10
.org 497
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0xAA55
/*
We're now at the beginning of the second sector of the image -
where the setup code goes.
We don't need to do too much setup for Etherboot.
This code gets loaded at SETUPSEG:0. It wants to start
executing the Etherboot image that's loaded at SYSSEG:0 and
whose entry point is SYSSEG:0.
*/
# change it.
type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
# Bootlin, SYSLX, bootsect...)
# See Documentation/i386/boot.txt for
# assigned ids
# flags, unused bits must be zero (RFU) bit within loadflags
loadflags:
LOADED_HIGH = 1 # If set, the kernel is loaded high
CAN_USE_HEAP = 0x80 # If set, the loader also has set
# heap_end_ptr to tell how much
# space behind setup.S can be used for
# heap purposes.
# Only the loader knows what is free
.byte LOADED_HIGH
setup_move_size: .word 0x8000 # size to move, when setup is not
# loaded at 0x90000. We will move setup
# to 0x90000 then just before jumping
# into the kernel. However, only the
# loader knows how much data behind
# us also needs to be loaded.
code32_start: # here loaders can put a different
# start address for 32-bit code.
.long 0x100000 # 0x100000 = default for big kernel
ramdisk_image: .long 0 # address of loaded ramdisk image
# Here the loader puts the 32-bit
# address where it loaded the image.
# This only will be read by the kernel.
ramdisk_size: .long 0 # its size in bytes
bootsect_kludge:
.long 0 # obsolete
heap_end_ptr: .word 0 # (Header version 0x0201 or later)
# space from here (exclusive) down to
# end of setup code can be used by setup
# for local heap purposes.
pad1: .word 0
cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
# If nonzero, a 32-bit pointer
# to the kernel command line.
# The command line should be
# located between the start of
# setup and the end of low
# memory (0xa0000), or it may
# get overwritten before it
# gets read. If this field is
# used, there is no longer
# anything magical about the
# 0x90000 segment; the setup
# can be located anywhere in
# low memory 0x10000 or higher.
ramdisk_max: .long 0 # (Header version 0x0203 or later)
# The highest safe address for
# the contents of an initrd
trampoline: call start_of_setup
trampoline_end:
.space 1024
# End of setup header #####################################################
start_of_setup:
# Set %ds = %cs, we know that SETUPSEG = %cs at this point
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
# Check signature at end of setup
cmpw $SIG1, (setup_sig1 - setup_code)
jne bad_sig
cmpw $SIG2, (setup_sig2 - setup_code)
jne bad_sig
jmp good_sig1
# Routine to print asciiz string at ds:si
prtstr:
lodsb
andb %al, %al
jz fin
call prtchr
jmp prtstr
fin: ret
# Part of above routine, this one just prints ascii al
prtchr: pushw %ax
pushw %cx
movw $7,%bx
movw $0x01, %cx
movb $0x0e, %ah
int $0x10
popw %cx
popw %ax
ret
no_sig_mess: .string "No setup signature found ..."
good_sig1:
jmp good_sig
bad_sig:
movw %cs, %ax # SETUPSEG
subw $DELTA_INITSEG, %ax # INITSEG
movw %ax, %ds
xorb %bh, %bh
movb (497), %bl # get setup sect from bootsect
subw $4, %bx # LILO loads 4 sectors of setup
shlw $8, %bx # convert to words (1sect=2^8 words)
movw %bx, %cx
shrw $3, %bx # convert to segment
addw $SYSSEG, %bx
movw %bx, %cs:(start_sys_seg - setup_code)
movw $2048, %di # four sectors loaded by LILO
subw %si, %si
pushw %cs
popw %es
movw $SYSSEG, %ax
movw %ax, %ds
rep
movsw
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
cmpw $SIG1, (setup_sig1 - setup_code)
jne no_sig
cmpw $SIG2, (setup_sig2 - setup_code)
jne no_sig
jmp good_sig
no_sig:
lea (no_sig_mess - setup_code), %si
call prtstr
no_sig_loop:
hlt
jmp no_sig_loop
good_sig:
cmpw $0, %cs:(realmode_swtch - setup_code)
jz rmodeswtch_normal
lcall *%cs:(realmode_swtch - setup_code)
jmp rmodeswtch_end
rmodeswtch_normal:
pushw %cs
call default_switch
rmodeswtch_end:
# (loader may have changed it)
movl %cs:(code32_start - setup_code), %eax
movl %eax, %cs:(code32 - setup_code)
# then we load the segment descriptors
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
#
# Enable A20. This is at the very best an annoying procedure.
# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
#
A20_TEST_LOOPS = 32 # Iterations per wait
A20_ENABLE_LOOPS = 255 # Total loops to try
a20_try_loop:
# First, see if we are on a system with no A20 gate.
a20_none:
call a20_test
jnz a20_done
# Next, try the BIOS (INT 0x15, AX=0x2401)
a20_bios:
movw $0x2401, %ax
pushfl # Be paranoid about flags
int $0x15
popfl
call a20_test
jnz a20_done
# Try enabling A20 through the keyboard controller
a20_kbc:
call empty_8042
call a20_test # Just in case the BIOS worked
jnz a20_done # but had a delayed reaction.
movb $0xD1, %al # command write
outb %al, $0x64
call empty_8042
movb $0xDF, %al # A20 on
outb %al, $0x60
call empty_8042
# Wait until a20 really *is* enabled; it can take a fair amount of
# time on certain systems; Toshiba Tecras are known to have this
# problem.
a20_kbc_wait:
xorw %cx, %cx
a20_kbc_wait_loop:
call a20_test
jnz a20_done
loop a20_kbc_wait_loop
# Final attempt: use "configuration port A"
a20_fast:
inb $0x92, %al # Configuration Port A
orb $0x02, %al # "fast A20" version
#
.ascii "linux: fatal error: A20 gate not responding!"
.long 0x100000
# Here's a bunch of information about your current kernel..
kernel_version: .ascii "Etherboot "
.ascii VERSION
.byte 0
# This is the default real mode switch routine.
# to be called just before protected mode transition
default_switch:
cli # no interrupts allowed !
movb $0x80, %al # disable NMI for bootup
# sequence
outb %al, $0x70
lret
# This routine tests whether or not A20 is enabled. If so, it
# exits with zf = 0.
#
# The memory address used, 0x200, is the int $0x80 vector, which
# should be safe.
A20_TEST_ADDR = 4*0x80
a20_test:
pushw %cx
pushw %ax
xorw %cx, %cx
movw %cx, %fs # Low memory
decw %cx
movw %cx, %gs # High memory area
movw $A20_TEST_LOOPS, %cx
movw %fs:(A20_TEST_ADDR), %ax
pushw %ax
a20_test_wait:
incw %ax
movw %ax, %fs:(A20_TEST_ADDR)
call delay # Serialize and make delay constant
cmpw %gs:(A20_TEST_ADDR+0x10), %ax
loope a20_test_wait
popw %fs:(A20_TEST_ADDR)
popw %ax
popw %cx
ret
# This routine checks that the keyboard command queue is empty
# (after emptying the output buffers)
#
# Some machines have delusions that the keyboard buffer is always full
# with no keyboard attached...
#
# If there is no keyboard controller, we will usually get 0xff
# to all the reads. With each IO taking a microsecond and
# a timeout of 100,000 iterations, this can take about half a
# second ("delay" == outb to port 0x80). That should be ok,
# and should also be plenty of time for a real keyboard controller
# to empty.
#
empty_8042:
pushl %ecx
movl $100000, %ecx
empty_8042_loop:
decl %ecx
jz empty_8042_end_loop
call delay
inb $0x64, %al # 8042 status port
testb $1, %al # output buffer?
jz no_output
call delay
inb $0x60, %al # read it
jmp empty_8042_loop
no_output:
testb $2, %al # is input buffer full?
jnz empty_8042_loop # yes - loop
empty_8042_end_loop:
popl %ecx
# Delay is needed after doing I/O
delay:
outb %al,$0x80
ret
# Descriptor tables
#
# NOTE: The intel manual says gdt should be sixteen bytes aligned for
# efficiency reasons. However, there are machines which are known not
# to boot with misaligned GDTs, so alter this at your peril! If you alter
# empty GDT entries (one for NULL and one reserved).
#
# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is
# true for the Voyager Quad CPU card which will not boot without
# This directive. 16 byte aligment is recommended by intel.
#
.balign 16
bImage_gdt:
.fill GDT_ENTRY_BOOT_CS,8,0
.word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
.word 0 # base address = 0
.word 0x00CF # granularity = 4096, 386
# (+5th nibble of limit)
.word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
.word 0 # base address = 0
.word 0x00CF # granularity = 4096, 386
# (+5th nibble of limit)
bImage_gdt_end:
.balign 4
.word 0 # alignment byte
bImage_idt_48:
.word 0 # idt limit = 0
.long 0 # idt base = 0L
.word 0 # alignment byte
bImage_gdt_48:
.word bImage_gdt_end - bImage_gdt - 1 # gdt limit
.long bImage_gdt_48 - setup_code # gdt base (filled in later)
.section ".text16", "ax", @progbits
.globl prefix_exit
prefix_exit:
int $0x19 /* should try to boot machine */
.globl prefix_exit_end
prefix_exit_end:
.previous
.org (PREFIXSIZE - 4)
# Setup signature -- must be last
setup_sig1: .word SIG1
setup_sig2: .word SIG2
/* Etherboot expects to be contiguous in memory once loaded.
* The linux bImage protocol does not do this, but since we
*
*/
/* Load segment registers */
/* Use the internal etherboot stack */
pushl $0 /* No parameters to preserve for exit path */
pushl $0 /* Use prefix exit path mechanism */
/*
That's about it.
*/