Next Previous Contents

3. Linux early setup

(from linux/arch/i386/boot/setup.S and linux/arch/i386/boot/video.S)

NOTE: Register notation is %regname and constant notation is a number, with or without a leading '$' sign.

3.1 IA-32 Kernel Setup

"setup.S" is responsible for getting the system data from the BIOS and putting them into the appropriate places in system memory.

Both "setup.S" and the kernel have been loaded by the boot block.

"setup.S" is assembled as 16-bit real-mode code. It switches the processor to 32-bit protected mode and jumps to the 32-bit kernel code.

This code asks the BIOS for memory/disk/other parameters, and puts them in a "safe" place: 0x90000-0x901FF, that is, where the boot block used to be. It is then up to the protected mode system to read them from there before the area is overwritten for buffer-blocks.

The "setup.S" code begins with a jmp instruction around the "setup header", which must begin at location %cs:2.

This is the setup header:


                .ascii  "HdrS"          # header signature
                .word   0x0202          # header version number
realmode_swtch: .word   0, 0            # default_switch, SETUPSEG
start_sys_seg:  .word   SYSSEG
                .word   kernel_version  # pointer to kernel version string
type_of_loader: .byte   0
loadflags:
LOADED_HIGH     = 1             # If set, the kernel is loaded high
#ifndef __BIG_KERNEL__
                .byte   0
#else
                .byte   LOADED_HIGH
#endif
setup_move_size: .word  0x8000  # size to move, when setup is not
                                # loaded at 0x90000.
code32_start:                   # here loaders can put a different
                                # start address for 32-bit code.
#ifndef __BIG_KERNEL__
                .long   0x1000  # default for zImage
#else
                .long   0x100000# default for big kernel
#endif
ramdisk_image:  .long   0       # address of loaded ramdisk image
ramdisk_size:   .long   0       # its size in bytes
bootsect_kludge: .word  bootsect_helper, SETUPSEG
heap_end_ptr:   .word   modelist+1024   # (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.
trampoline:     call    start_of_setup  # no return from start_of_setup
                .space  1024
# End of setup header #####################################################

start_of_setup:

Read second hard drive DASD type

Read the DASD type of the second hard drive (BIOS int. 0x13, %ax=0x1500, %dl=0x81).

# Bootlin depends on this being done early. [TBD:why?]

Check that LILO loaded us right

Check the signature words at the end of setup. Signature words are used to ensure that LILO loaded us right. If the two words are not found correctly, copy the setup sectors and check for the signature words again. If they still aren't found, panic("No setup signature found ...").

Check old loader trying to load a big kernel

If the kernel image is "big" (and hence is "loaded high"), then if the loader cannot handle "loaded high" images, then panic ("Wrong loader, giving up...").

Determine system memory size

Get the extended memory size {above 1 MB} in KB. First clear the extended memory size to 0.

#ifndef STANDARD_MEMORY_BIOS_CALL

Clear the E820 memory area counter.

Try three different memory detection schemes.
First, try E820h, which lets us assemble a memory map, then try E801h, which returns a 32-bit memory size, and finally 88h, which returns 0-64 MB.

Method E820H populates a table in the empty_zero_block that contains a list of usable address/size/type tuples. In "linux/arch/i386/kernel/setup.c", this information is transferred into the e820map, and in "linux/arch/i386/mm/init.c", that new information is used to mark pages reserved or not.

Method E820H:
Get the BIOS memory map. E820h returns memory classified into different types and allows memory holes. We scan through this memory map and build a list of the first 32 memory areas {up to 32 entries or BIOS says that there are no more entries}, which we return at "E820MAP". [See URL: http://www.teleport.com/ acpi/acpihtml/topic245.htm]

Method E801H:
We store the 0xe801 memory size in a completely different place, because it will most likely be longer than 16 bits.

This is the sum of 2 registers, normalized to 1 KB chunk sizes: %ecx = memory size from 1 MB to 16 MB range, in 1 KB chunks + %edx = memory size above 16 MB, in 64 KB chunks.

Ye Olde Traditional Methode:
BIOS int. 0x15/AH=0x88 returns the memory size (up to 16 MB or 64 MB, depending on the BIOS). We always use this method, regardless of the results of the other two methods.

#endif

Set the keyboard repeat rate to the maximum rate using using BIOS int. 0x16.

Video adapter modes

Find the video adapter and its supported modes and allow the user to browse video modes.

call video # {see Video section below}

Get Hard Disk parameters

Get hd0 data: Save the hd0 descriptor (from int. vector 0x41) at INITSEG:0x80 length 0x10.

Get hd1 data: Save the hd1 descriptor (from int. vector 0x46) at INITSEG:0x90 length 0x10.

Check that there IS an hd1, using BIOS int. 0x13. If not, clear its descriptor.

Get Micro Channel bus information

Check for Micro Channel (MCA) bus:

Check for mouse

Check for PS/2 pointing device by using BIOS int. 0x11 {get equipment list}.

Check for APM BIOS support

Check for an APM BIOS (if kernel is configured for APM support):

Prepare to move to protected mode

We build a jump instruction to the kernel's code32_start address. (The loader may have changed it.)

Move the kernel to its correct place if necessary.

Load the segment descriptors (load %ds = %cs).

Make sure that we are at the right position in memory, to accommodate the command line and boot parameters at their fixed locations.

Load the IDT pointer register with 0,0.

Calculate the linear base address of the kernel GDT (table) and load the GDT pointer register with its base address and limit. This early kernel GDT describes kernel code as 4 GB, with base address 0, code/readable/executable, with granularity of 4 KB. The kernel data segment is described as 4 GB, with base address 0, data/readable/writable, with granularity of 4 KB.

Enable address line A20

Make sure any possible coprocessor is properly reset

Mask all interrupts

Now we mask all interrupts; the rest is done in init_IRQ().

Move to Protected Mode

Now is the time to actually move into protected mode. To make things as simple as possible, we do no register setup or anything, we let the GNU-compiled 32-bit programs do that. We just jump to absolute address 0x1000 (or the loader supplied one), in 32-bit protected mode.

Note that the short jump isn't strictly needed, although there are reasons why it might be a good idea. It won't hurt in any case.

Set the PE (Protected mode Enable) bit in the MSW and jump to the following instruction to flush the instruction fetch queue.

Clear %bx to indicate that this is the BSP (first CPU only).

Jump to startup_32 code

Jump to the 32-bit kernel code (startup_32).

NOTE: For high-loaded big kernels we need:

        jmpi    0x100000,__KERNEL_CS
but we yet haven't reloaded the %cs register, so the default size of the target offset still is 16 bit. However, using an operand prefix (0x66), the CPU will properly take our 48-bit far pointer. (INTeL 80386 Programmer's Reference Manual, Mixing 16-bit and 32-bit code, page 16-6).

        .byte 0x66, 0xea                # prefix + jmpi-opcode
code32: .long   0x1000                  # or 0x100000 for big kernels
        .word   __KERNEL_CS

This jumps to "startup_32" in "linux/arch/i386/kernel/head.S".

3.2 Video Setup

"linux/arch/i386/boot/video.S" is included into "linux/arch/i386/boot/setup.S", so they are assembled together. The file separation is a logical module separation even though the two modules aren't built separately.

"video.S" handles Linux/i386 display adapter and video mode setup. For more information about Linux/i386 video modes, see "linux/Documentation/svga.txt" by Martin Mares [mj@ucw.cz].

Video mode selection is a kernel build option. When it is enabled, You can select a specific (fixed) video mode to be used during kernel booting or you can ask to view a selection menu and then choose a video mode from that menu.

There are a few esoteric (!) "video.S" build options that not covered here. See "linux/Documentation/svga.txt" for all of them.

CONFIG_VIDEO_SVGA (for automatic detection of SVGA adapters and modes) is normally #undefined. The normal method of video adapter detection on Linux/i386 is VESA (CONFIG_VIDEO_VESA, for autodetection of VESA modes).

"video:" is the main entry point called by "setup.S". The %ds register *must* be pointing to the bootsector. The "video.S" code uses different segments from the main "setup.S" code.

This is a simplified description of the code flow in "video.S". It does not address the CONFIG_VIDEO_LOCAL, CONFIG_VIDEO_400_HACK, and CONFIG_VIDEO_GFX_HACK build options and it does not dive deep into video BIOS calls or video register accesses.

video:

basic_detect:

mode_params:

#ifdef CONFIG_VIDEO_SELECT

mopar_gr:

mode_menu:

Build the mode list table and display the mode menu.

mode_set:

For the selected video mode, use BIOS int. 0x10 calls or register writes as needed to set some or all of:

Some video modes require register writes to set:

{end of mode_set}

#ifdef CONFIG_VIDEO_RETAIN /* Normally _IS_ #defined */

store_screen:

CONFIG_VIDEO_RETAIN is used to retain screen contents when switching modes. This option stores the screen contents to a temporary memory buffer (if there is enough memory) so that they can be restored later.

restore_screen:

Restores screen contents from temporary buffer (if already saved).

#endif /* CONFIG_VIDEO_RETAIN */

mode_table:

Build the table of video modes at `modelist'.

mode_scan:

Scans for video modes.

#ifdef CONFIG_VIDEO_SVGA

svga_modes:

Try to detect the type of SVGA card and supply (usually approximate) video mode table for it.

#endif /* CONFIG_VIDEO_SVGA */

#endif /* CONFIG_VIDEO_SELECT */


Next Previous Contents