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:

- Set MCA feature table length to 0 in case not found.
- Get System Configuration Parameters (BIOS int. 0x15/%ah=0xc0). This sets %es:%bx to point to the system feature table.
- We keep only the first 16 bytes of the system feature table if found: Structure size, Model byte, Submodel byte, BIOS revision, and Feature information bytes 1-5. Bit 0 or 1 (either one) of Feature byte 1 indicates that the system contains a Micro Channel bus.

### Check for mouse

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

- Clear the pointing device flag (default).
- BIOS int. 0x11: get equipment list.
- If bit 2 (value 0x04) is set, then a mouse is installed and the pointing device flag is set to indicate that the device is present.

### Check for APM BIOS support

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

- start: clear version field to 0, which means no APM BIOS present.
- Check for APM BIOS installation using BIOS int. 0x15.
- If not present, done.
- Check for "PM" signature returned in %bx.
- If no signature, then no APM BIOS: done.
- Check for 32-bit support in %cx.
- If no 32-bit support, no (good) APM BIOS: done. Must have 32-bit APM BIOS support to be used by Linux.
- Save the BIOS code segment, BIOS entry point offset, BIOS 16-bit code segment, BIOS data segment, BIOS code segment length, and BIOS data segment length.
- Record the APM BIOS version and flags.

### 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

- Empty the 8042 (keyboard controller) of any queued keys.
- Write 0xd1 (Write Output Port) to Command Register port 0x64.
- Empty the 8042 (keyboard controller) of any queued keys.
- Write 0xdf (Gate A20 + more) to Output port 0x60.
- Empty the 8042 (keyboard controller) of any queued keys.
- Set bit number 1 (value 0x02: FAST_A20) in the "port 0x92" system control register. This enables A20 on some systems, depending on the chipset used in them.
- Wait until A20 really *is* enabled; it can take a fair amount of time on certain systems. The memory location used here (0x200) is the int 0x80 vector, which should be safe to use. When A20 is disabled, the test memory locations are an alias of each other (segment 0:offset 0x200 and segment 0xffff:offset 0x210). {0xffff0 + 0x210 = 0x100200, but if A20 is disabled, this becomes 0x000200.} We just wait (busy wait/loop) until these memory locations are no longer aliased.

### Make sure any possible coprocessor is properly reset

- Write 0 to port 0xf0 to clear the Math Coprocessor '-busy' signal.
- Write 0 to port 0xf1 to reset the Math Coprocessor.

### Mask all interrupts

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

- Mask off all interrupts on the slave PIC: write 0xff to port 0xa1.
- Mask off all interrupts on the master PIC except for IRQ2, which is the cascaded IRQ input from the slave PIC: write 0xfb to port 0x21.

### 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:

- %fs is set to the original %ds value
- %ds and %es are set to %cs
- %gs is set to zero
- Detect the video adapter type and supported modes. (call basic_detect)
- #ifdef CONFIG_VIDEO_SELECT
- If the user wants to see a list of the supported VGA adapter modes, list them. (call mode_menu)
- Set the selected video mode. (call mode_set)
- #ifdef CONFIG_VIDEO_RETAIN
- Restore the screen contents. (call restore_screen)
- #endif /* CONFIG_VIDEO_RETAIN */
- #endif /* CONFIG_VIDEO_SELECT */
- Store mode parameters for kernel. (call mode_params)
- Restore original DS register value.

### basic_detect:

- Detect if we have CGA, MDA, HGA, EGA, or VGA and pass it to the kernel.
- Check for EGA/VGA using BIOS int. 0x10 calls. This also tells whether the video adapter is CGA/MDA/HGA.
- The "adapter" variable is returned as 0 for CGA/MDA/HGA, 1 for EGA, and 2 for VGA.

### mode_params:

- Store the video mode parameters for later use by the kernel. This is done by asking the BIOS for mode parameters except for the rows/columns parameters in the default 80x25 mode -- these are set directly, because some very obscure BIOSes supply insane values.
- #ifdef CONFIG_VIDEO_SELECT
- For graphics mode with a linear frame buffer, goto mopar_gr.
- #endif /* CONFIG_VIDEO_SELECT */
- For MDA/CGA/HGA/EGA/VGA:
- Read and save cursor position.
- Read and save video page/mode/width.
- For MDA/HGA, change the video_segment to $0xb000. (Leave it at its initial value of $0xb800 for all other adapters.)
- Get the Font size (valid only on EGA/VGA).
- Save the number of video columns and lines.

#ifdef CONFIG_VIDEO_SELECT

### mopar_gr:

- Get VESA frame buffer parameters.
- Get video mem size and protected mode interface information using BIOS int. 0x10 calls.

### 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:

- Reset the video mode
- Number of scan lines
- Font pixel size
- Save the screen size in force_size. "force_size" is used to override possibly broken video BIOS interfaces and is used instead of the BIOS variables.

Some video modes require register writes to set:

- Location of the cursor scan lines
- Vertical sync start
- Vertical sync end
- Vertical display end
- Vertical blank start
- Vertical blank end
- Vertical total
- (Vertical) overflow
- Correct sync polarity
- Preserve clock select bits and color bit

{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.

- Save the current number of video lines and columns, cursor position, and video mode.
- Calculate the image size.
- Save the screen image.
- Set the "do_restore" flag so that the screen contents will be restored at the end of video mode detection/selection.

### restore_screen:

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

- Get parameters of current mode.
- Set cursor position.
- Restore the screen contents.

#endif /* CONFIG_VIDEO_RETAIN */

### mode_table:

Build the table of video modes at `modelist'.

- Store standard modes.
- Add modes for standard VGA.
- #ifdef CONFIG_VIDEO_LOCAL
- Add locally-defined video modes. (call local_modes)
- #endif /* CONFIG_VIDEO_LOCAL */
- #ifdef CONFIG_VIDEO_VESA
- Auto-detect VESA VGA modes. (call vesa_modes)
- #endif /* CONFIG_VIDEO_VESA */
- #ifdef CONFIG_VIDEO_SVGA
- Detect SVGA cards & modes. (call svga_modes)
- #endif /* CONFIG_VIDEO_SVGA */
- #ifdef CONFIG_VIDEO_COMPACT
- Compact the video modes list, removing duplicate entries.
- #endif /* CONFIG_VIDEO_COMPACT */

### mode_scan:

Scans for video modes.

- Start with mode 0.
- Test the mode.
- Test if it's a text mode.
- OK, store the mode.
- Restore back to mode 3.

#ifdef CONFIG_VIDEO_SVGA

### svga_modes:

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

- Test all known SVGA adapters.
- Call the test routine for each adapter.
- If adapter is found, copy the video modes.
- Store pointer to card name.

#endif /* CONFIG_VIDEO_SVGA */

#endif /* CONFIG_VIDEO_SELECT */

Next Previous Contents