楊皓宇, 紀政良, 李建緯
Power button -> BIOS -> MBR (bootloader)
-> kernel real-mode code
CS
(Code Segment) (CS_selector
)
CS_base
is hidden from programmer.IP
(Instruction Pointer)
CS: 0xFFFF // code segment selector
IP: 0x0000 // instruction pointer
CS: 0xF000
IP: 0xFFF0
CS: 0xF000
IP: 0xFFF0
CS_base: 0xFFFF_0000 // hidden
\[ \begin{aligned} \text{CS Base} &= \text{CS Selector} \times 16\\ \text{Memory Address} &= \text{CS Base} + \text{IP} \end{aligned} \]
0xFFFF_FFF0
.
near jump
instruction, guiding the system to the rest of BIOS boot code.Recall the initial register data and formula:
CS_selector: 0xF000
CS_base: 0xFFFF_0000
IP: 0xFFF0
\[ \begin{aligned} \text{CS Base} &= \text{CS Selector} \times 16\\ \text{Memory Address} &= \text{CS Base} + \text{IP} \end{aligned} \]
According to the formula above, CS_base
should be 0xF_0000
instead of 0xFFFF_0000
.
Why CS_base
is not CS selector
times 16?
0xF_FFF0
0xFFFF_FFF0
In real mode, program can only access 20-bit memory address.
To address this difference and maintain compatibility with the 20-bit memory address system of the real mode, modern x86 CPUs are designed to initialize the CS selector
and CS base
register in a way that aligns with this legacy requirement.
CS_selector: 0xF000
CS_base: 0xFFFF_0000
IP: 0xFFF0
Reset_vector: 0xFFFF_FFF0
Furthermore, the distinction between jump instructions is also important.
A near jump
affects only IP
, leaving the CS
as is. However, when the system employs a far jump
, both the CS selector
and the CS base
are synchronized.
0x7c00
and jumps to thereboot.img
diskboot.img
diskboot.img
grub_main
function.grub_main
grub_main
grub_normal_execute
grub_menu_execute_entry
X + sizeof(KernalBootSector) + 1
MZ
and follows by a PE
header.start_up_setup
bzImage
|
|---- setup.elf // <---- header.S and some c code
|
|---- vmlinux
|
|---- setup.bin
|
|---- vmlinux.bin.gz
lagacy boot sector (512 bytes)
first part of kernel setup
(boot header information)
contains some code to show the error message
if we don't use 3rd party bootloader and let BIOS load the first sector of kernel image into memory at 0x7c00 and run
// first part of setup header
// second part of setup header
Make sure that all segment register values are equal
grub2 loads kernel setup code at address 0x10000, but starts from 0x10200
we want to let cs = 0x1000
Now we have the stack and BSS, so we can jump to the main() C function
0xF_FFF0
0xFFFF_FFF0
0xFFFF_FFFF_FFFF_FFF0
0x55
, 0xaa
) ?0x7C00
0x10000
0x10200
高士軒, 黃爾泰
Protected mode
was the main mode of Intel processors from the 80286 processor until Intel 64 and long mode
came.real mode
, which is only 1 MB
.
real mode
and protected mode
is memory management.Segment Descriptor
.Global Descriptor Table (GDT)
.GDTR
register. There will be an operation for loading it from memory, something like:lgdt gdt
63 56 51 48 45 39 32
------------------------------------------------------------
| | |B| |A| | | | |0|E|W|A| |
| BASE 31:24 |G|/|L|V| LIMIT |P|DPL|S| TYPE | BASE 23:16 |
| | |D| |L| 19:16 | | | |1|C|R|A| |
------------------------------------------------------------
31 16 15 0
------------------------------------------------------------
| | |
| BASE 15:0 | LIMIT 15:0 |
| | |
------------------------------------------------------------
real mode
. 15 3 2 1 0
-----------------------------
| Index | TI | RPL |
-----------------------------
Index
stores the index number of the descriptor in the GDT.RPL
contains the Requester's Privilege Level.GDT address + Index
from the selector.copy_boot_params(void)
.
boot_params
structure includes a member setup_header hdr
which contains some useful parameters in later initialization.memcpy
defined in copy.S
to copy hdr
to boot_params
.console_init
would be called. puts("early console in setup code\n");
puts
function can print character by character by interrypt 0x10
.init_heap
function.
CAN_USE_HEAP
flag from loadflags
.loadflags
is a bitmask and it also contains other mask.stack_end
. stack_end = esp - STACK_SIZE;
char *stack_end;
if (boot_params.hdr.loadflags & CAN_USE_HEAP) {
asm("leal %P1(%%esp),%0"
: "=r" (stack_end) : "i" (-STACK_SIZE));
And heap_end
is defined in other header file.
The last check is whether heap_end
is greater than stack_end
. If it is then stack_end
is assigned to heap_end
to make them equal.
if (heap_end > stack_end)
head_end = stak_end;
protected mode
?validate_cpu
check_cpu
check_cpu(&cpu_level, &req_level, &err_flags);
if (cpu_level < req_level) {
...
return -1;
}
check_cpu
set_bios_mode
is called.
0x15
BIOS interrupt to tell BIOS long mode will be used.detect_memory
function provides a map of available RAM to the CPU.0xe820
, 0xe801
and 0x88
.0xe820
for example.detect_memory_e820
biosregs
structure with 0xe820
call.initregs(&ireg);
ireg.ax = 0xe820;
ireg.cx = sizeof buf;
ireg.edx = SMAP;
ireg.di = (size_t)&buf;
detect_memory_e820
ax
: the number of the function (0xe820)cx
: size of the buffer which will contain data about the memory (sizeof buf
)edx
: SMAP
(ASCII) magic numberes:di
: contain the address of the buffer (&buf
)ebx
: Initialized to zero in the first time.detect_memory_e820
intcall(0x15, &ireg, &oreg)
// in each iteration
intcall(0x15, &ireg, &oreg);
ireg.ebx = oreg.ebx; // update ebx with pervious value
ebx
= 0e820_entry
detect_memory_e820
e820_entry
contain
dmesg
like[ 0.0] e820: BIOS-provided physical RAM map:
[ 0.0] BIOS-e820: [mem 0x00000000-0x0009fbff] usable
[ 0.0] BIOS-e820: [mem 0x0009fc00-0x0009ffff] reserved
[ 0.0] BIOS-e820: [mem 0x000f0000-0x000fffff] reserved
[ 0.0] BIOS-e820: [mem 0x00100000-0x3ffdffff] usable
[ 0.0] BIOS-e820: [mem 0x3ffe0000-0x3fffffff] reserved
[ 0.0] BIOS-e820: [mem 0xfffc0000-0xffffffff] reserved
keyboard_init
function
0x16
to query the status of the keyboardinitregs(&ireg);
ireg.ah = 0x02; /* Get keyboard status */
intcall(0x16, &ireg, &oreg);
boot_params.kbd_status = oreg.al;
0x16
again to set repeat rate and delay.ireg.ax = 0x0305; /* Set keyboard repeat rate */
query_ist
query_ist
function.
0x15
to get the info and saves the result to boot_params
.query_apm_bios
query_apm_bios
calls the interrupt 0x15
with ah=0x53
to check APM
installation.query_apm_bios
0x15
again, but with ah=0x5304
to disconnect APM
interface and connect the 32-bit protected mode interface.boot_params.apm_bios_info
with values obtained from the BIOS.query_apm_bios
query_apm_bios
will be executed only when CONFIG_APM
or CONFIG_APM_MODULE
compile flag was set.query_edd
query_edd
function, which queries EDD(Enhanced Disk Drive
) info from BIOS.query_edd
query_edd
for (devno = 0x80; devno < 0x80 + EDD_MBR_SIG_MAX; devno++) {
if (!get_edd_info(devno, &ei) &&
boot_params.eddbuf_entries < EDDMAXNR) {
memcpy(edp, &ei, sizeof ei);
edp++;
boot_params.eddbuf_entries++;
}
...
...
...
}
validate_cpu
do?detect_memory_e820
do?Ting Shiuan Guan, Tim Lin
main()
in arch/x86/boot/main.c
/* Set the video mode */ set_video(); /* Do the last things and invoke protected mode */ go_to_protected_mode(); }
size (bytes) | 1 | 2 | 4 | 8 |
---|---|---|---|---|
signed type | char | short | int | long |
unsigned type | u8 | u16 | u32 | u64 |
Defined in arch/x86/boot/boot.h
/* Heap -- available for dynamic lists. */ extern char _end[]; extern char *HEAP; extern char *heap_end; #define RESET_HEAP() ((void *)( HEAP = _end )) static inline char *__get_heap(size_t s, size_t a, size_t n) { char *tmp; HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); tmp = HEAP; HEAP += s*n; return tmp; } #define GET_HEAP(type, n) \ ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) static inline bool heap_free(size_t n) { return (int)(heap_end-HEAP) >= (int)n; }
/* Heap -- available for dynamic lists. */ extern char _end[]; extern char *HEAP; extern char *heap_end; #define RESET_HEAP() ((void *)( HEAP = _end )) static inline char *__get_heap(size_t s, size_t a, size_t n) { char *tmp; HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); tmp = HEAP; HEAP += s*n; return tmp; } #define GET_HEAP(type, n) \ ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) static inline bool heap_free(size_t n) { return (int)(heap_end-HEAP) >= (int)n; }
/* Heap -- available for dynamic lists. */ extern char _end[]; extern char *HEAP; extern char *heap_end; #define RESET_HEAP() ((void *)( HEAP = _end )) static inline char *__get_heap(size_t s, size_t a, size_t n) { char *tmp; HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); tmp = HEAP; HEAP += s*n; return tmp; } #define GET_HEAP(type, n) \ ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) static inline bool heap_free(size_t n) { return (int)(heap_end-HEAP) >= (int)n; }
/* Heap -- available for dynamic lists. */ extern char _end[]; extern char *HEAP; extern char *heap_end; #define RESET_HEAP() ((void *)( HEAP = _end )) static inline char *__get_heap(size_t s, size_t a, size_t n) { char *tmp; HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); tmp = HEAP; HEAP += s*n; return tmp; } #define GET_HEAP(type, n) \ ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) static inline bool heap_free(size_t n) { return (int)(heap_end-HEAP) >= (int)n; }
/* Heap -- available for dynamic lists. */ extern char _end[]; extern char *HEAP; extern char *heap_end; #define RESET_HEAP() ((void *)( HEAP = _end )) static inline char *__get_heap(size_t s, size_t a, size_t n) { char *tmp; HEAP = (char *)(((size_t)HEAP+(a-1)) & ~(a-1)); tmp = HEAP; HEAP += s*n; return tmp; } #define GET_HEAP(type, n) \ ((type *)__get_heap(sizeof(type),__alignof__(type),(n))) static inline bool heap_free(size_t n) { return (int)(heap_end-HEAP) >= (int)n; }
main()
in arch/x86/boot/main.c
/* Set the video mode */ set_video(); /* Do the last things and invoke protected mode */ go_to_protected_mode(); }
set_video()
in arch/x86/boot/video.c
void set_video(void) { u16 mode = boot_params.hdr.vid_mode; RESET_HEAP(); store_mode_params(); save_screen(); probe_cards(0); for (;;) { if (mode == ASK_VGA) mode = mode_menu(); if (!set_mode(mode)) break; printf("Undefined video mode number: %x\n", mode); mode = ASK_VGA; } boot_params.hdr.vid_mode = mode; vesa_store_edid(); store_mode_params(); if (do_restore) restore_screen(); }
set_video()
in arch/x86/boot/video.c
void set_video(void) { u16 mode = boot_params.hdr.vid_mode; RESET_HEAP();
vid_mode
0x01FA
/ size 2
vga=<mode>
normal
/ ext
/ ask
set_video()
in arch/x86/boot/video.c
u16 mode = boot_params.hdr.vid_mode; RESET_HEAP(); store_mode_params(); save_screen(); probe_cards(0);
boot_params.screen_info
.In arch/x86/boot/video.c
store_mode_params()
/* * Store the video mode parameters for later usage by the kernel. * This is done by asking the BIOS except for the rows/columns * parameters in the default 80x25 mode -- these are set directly, * because some very obscure BIOSes supply insane values. */ static void store_mode_params(void) { u16 font_size; int x, y; /* For graphics mode, it is up to the mode-setting driver (currently only video-vesa.c) to store the parameters */ if (graphic_mode) return; store_cursor_position(); store_video_mode(); if (boot_params.screen_info.orig_video_mode == 0x07) { /* MDA, HGC, or VGA in monochrome mode */ video_segment = 0xb000; } else { /* CGA, EGA, VGA and so forth */ video_segment = 0xb800; } set_fs(0); font_size = rdfs16(0x485); /* Font size, BIOS area */ boot_params.screen_info.orig_video_points = font_size; x = rdfs16(0x44a); y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; if (force_x) x = force_x; if (force_y) y = force_y; boot_params.screen_info.orig_video_cols = x; boot_params.screen_info.orig_video_lines = y; }
/* * Store the video mode parameters for later usage by the kernel. * This is done by asking the BIOS except for the rows/columns * parameters in the default 80x25 mode -- these are set directly, * because some very obscure BIOSes supply insane values. */ static void store_mode_params(void) { u16 font_size; int x, y; /* For graphics mode, it is up to the mode-setting driver (currently only video-vesa.c) to store the parameters */ if (graphic_mode) return; store_cursor_position(); store_video_mode(); if (boot_params.screen_info.orig_video_mode == 0x07) { /* MDA, HGC, or VGA in monochrome mode */ video_segment = 0xb000; } else { /* CGA, EGA, VGA and so forth */ video_segment = 0xb800; } set_fs(0); font_size = rdfs16(0x485); /* Font size, BIOS area */ boot_params.screen_info.orig_video_points = font_size; x = rdfs16(0x44a); y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; if (force_x) x = force_x; if (force_y) y = force_y; boot_params.screen_info.orig_video_cols = x; boot_params.screen_info.orig_video_lines = y; }
/* * Store the video mode parameters for later usage by the kernel. * This is done by asking the BIOS except for the rows/columns * parameters in the default 80x25 mode -- these are set directly, * because some very obscure BIOSes supply insane values. */ static void store_mode_params(void) { u16 font_size; int x, y; /* For graphics mode, it is up to the mode-setting driver (currently only video-vesa.c) to store the parameters */ if (graphic_mode) return; store_cursor_position(); store_video_mode(); if (boot_params.screen_info.orig_video_mode == 0x07) { /* MDA, HGC, or VGA in monochrome mode */ video_segment = 0xb000; } else { /* CGA, EGA, VGA and so forth */ video_segment = 0xb800; } set_fs(0); font_size = rdfs16(0x485); /* Font size, BIOS area */ boot_params.screen_info.orig_video_points = font_size; x = rdfs16(0x44a); y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; if (force_x) x = force_x; if (force_y) y = force_y; boot_params.screen_info.orig_video_cols = x; boot_params.screen_info.orig_video_lines = y; }
/* * Store the video mode parameters for later usage by the kernel. * This is done by asking the BIOS except for the rows/columns * parameters in the default 80x25 mode -- these are set directly, * because some very obscure BIOSes supply insane values. */ static void store_mode_params(void) { u16 font_size; int x, y; /* For graphics mode, it is up to the mode-setting driver (currently only video-vesa.c) to store the parameters */ if (graphic_mode) return; store_cursor_position(); store_video_mode(); if (boot_params.screen_info.orig_video_mode == 0x07) { /* MDA, HGC, or VGA in monochrome mode */ video_segment = 0xb000; } else { /* CGA, EGA, VGA and so forth */ video_segment = 0xb800; } set_fs(0); font_size = rdfs16(0x485); /* Font size, BIOS area */ boot_params.screen_info.orig_video_points = font_size; x = rdfs16(0x44a); y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; if (force_x) x = force_x; if (force_y) y = force_y; boot_params.screen_info.orig_video_cols = x; boot_params.screen_info.orig_video_lines = y; }
set_video()
in arch/x86/boot/video.c
RESET_HEAP(); store_mode_params(); save_screen(); probe_cards(0);
save_screen()
static void save_screen(void) { /* Should be called after store_mode_params() */ saved.x = boot_params.screen_info.orig_video_cols; saved.y = boot_params.screen_info.orig_video_lines; saved.curx = boot_params.screen_info.orig_x; saved.cury = boot_params.screen_info.orig_y; if (!heap_free(saved.x*saved.y*sizeof(u16)+512)) return; /* Not enough heap to save the screen */ saved.data = GET_HEAP(u16, saved.x*saved.y); set_fs(video_segment); copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16)); }
set_video()
in arch/x86/boot/video.c
store_mode_params(); save_screen(); probe_cards(0); for (;;) {
probe_cards()
in arch/x86/boot/video-mode.c
/* Probe the video drivers and have them generate their mode lists. */ void probe_cards(int unsafe) { struct card_info *card; static u8 probed[2]; if (probed[unsafe]) return; probed[unsafe] = 1; for (card = video_cards; card < video_cards_end; card++) { if (card->unsafe == unsafe) { if (card->probe) card->nmodes = card->probe(); else card->nmodes = 0; } } }
video_vga
in arch/x86/boot/video-vga.c
static __videocard video_vga = { .card_name = "VGA", .probe = vga_probe, .set_mode = vga_set_mode, };
__videocard
macro in arch/x86/boot/video.h
#define __videocard struct card_info __section(".videocards") __attribute__((used))
video_cards
in arch/x86/boot/setup.ld
.videocards : { video_cards = .; *(.videocards) video_cards_end = .; }
set_video()
in arch/x86/boot/video.c
probe_cards(0); for (;;) { if (mode == ASK_VGA) mode = mode_menu(); if (!set_mode(mode)) break; printf("Undefined video mode number: %x\n", mode); mode = ASK_VGA; } boot_params.hdr.vid_mode = mode;
probe_cards(0); for (;;) { if (mode == ASK_VGA) mode = mode_menu(); if (!set_mode(mode)) break; printf("Undefined video mode number: %x\n", mode); mode = ASK_VGA; } boot_params.hdr.vid_mode = mode;
mode_menu()
in arch/x86/boot/video.c
static unsigned int mode_menu(void) { int key; unsigned int sel; puts("Press <ENTER> to see video modes available, " "<SPACE> to continue, or wait 30 sec\n"); kbd_flush(); while (1) { key = getchar_timeout(); if (key == ' ' || key == 0) return VIDEO_CURRENT_MODE; /* Default */ if (key == '\r') break; putchar('\a'); /* Beep! */ } for (;;) { display_menu(); puts("Enter a video mode or \"scan\" to scan for " "additional modes: "); sel = get_entry(); if (sel != SCAN) return sel; probe_cards(1); } }
set_video()
in arch/x86/boot/video.c
for (;;) { if (mode == ASK_VGA) mode = mode_menu(); if (!set_mode(mode)) break; printf("Undefined video mode number: %x\n", mode); mode = ASK_VGA; } boot_params.hdr.vid_mode = mode; vesa_store_edid();
set_mode()
in arch/x86/boot/video-mode.c
/* Set mode (with recalc if specified) */ int set_mode(u16 mode) { int rv; u16 real_mode; /* Very special mode numbers... */ if (mode == VIDEO_CURRENT_MODE) return 0; /* Nothing to do... */ else if (mode == NORMAL_VGA) mode = VIDEO_80x25; else if (mode == EXTENDED_VGA) mode = VIDEO_8POINT; rv = raw_set_mode(mode, &real_mode); if (rv) return rv; if (mode & VIDEO_RECALC) vga_recalc_vertical(); /* Save the canonical mode number for the kernel, not an alias, size specification or menu position */ #ifndef _WAKEUP boot_params.hdr.vid_mode = real_mode; #endif return 0; }
raw_set_mode()
in arch/x86/boot/video-mode.c
/* Set mode (without recalc) */ static int raw_set_mode(u16 mode, u16 *real_mode) { int nmode, i; struct card_info *card; struct mode_info *mi; /* Drop the recalc bit if set */ mode &= ~VIDEO_RECALC; /* Scan for mode based on fixed ID, position, or resolution */ nmode = 0; for (card = video_cards; card < video_cards_end; card++) { mi = card->modes; for (i = 0; i < card->nmodes; i++, mi++) { int visible = mi->x || mi->y; if ((mode == nmode && visible) || mode == mi->mode || mode == (mi->y << 8)+mi->x) { *real_mode = mi->mode; return card->set_mode(mi); } if (visible) nmode++; } } /* Nothing found? Is it an "exceptional" (unprobed) mode? */ for (card = video_cards; card < video_cards_end; card++) { if (mode >= card->xmode_first && mode < card->xmode_first+card->xmode_n) { struct mode_info mix; *real_mode = mix.mode = mode; mix.x = mix.y = 0; return card->set_mode(&mix); } } /* Otherwise, failure... */ return -1; }
static __videocard video_vga = { .card_name = "VGA", .probe = vga_probe, .set_mode = vga_set_mode, };
vga_set_mode()
static int vga_set_mode(struct mode_info *mode) { /* Set the basic mode */ vga_set_basic_mode(); /* Override a possibly broken BIOS */ force_x = mode->x; force_y = mode->y; switch (mode->mode) { case VIDEO_80x25: break; case VIDEO_8POINT: vga_set_8font(); break; case VIDEO_80x43: vga_set_80x43(); break; case VIDEO_80x28: vga_set_14font(); break; case VIDEO_80x30: vga_set_80x30(); break; case VIDEO_80x34: vga_set_80x34(); break; case VIDEO_80x60: vga_set_80x60(); break; } return 0; }
vga_set_8font()
in arch/x86/boot/video-vga.c
static void vga_set_8font(void) { /* Set 8x8 font - 80x43 on EGA, 80x50 on VGA */ struct biosregs ireg; initregs(&ireg); /* Set 8x8 font */ ireg.ax = 0x1112; /* ireg.bl = 0; */ intcall(0x10, &ireg, NULL); /* Use alternate print screen */ ireg.ax = 0x1200; ireg.bl = 0x20; intcall(0x10, &ireg, NULL); /* Turn off cursor emulation */ ireg.ax = 0x1201; ireg.bl = 0x34; intcall(0x10, &ireg, NULL); /* Cursor is scan lines 6-7 */ ireg.ax = 0x0100; ireg.cx = 0x0607; intcall(0x10, &ireg, NULL); }
set_video()
in arch/x86/boot/video.c
} boot_params.hdr.vid_mode = mode; vesa_store_edid(); store_mode_params(); if (do_restore) restore_screen(); }
} boot_params.hdr.vid_mode = mode; vesa_store_edid(); store_mode_params(); if (do_restore) restore_screen(); }
} boot_params.hdr.vid_mode = mode; vesa_store_edid(); store_mode_params(); if (do_restore) restore_screen(); }
} boot_params.hdr.vid_mode = mode; vesa_store_edid(); store_mode_params(); if (do_restore) restore_screen(); }
/* Set the video mode */ set_video(); /* Do the last things and invoke protected mode */ go_to_protected_mode(); }
void go_to_protected_mode(void) { realmode_switch_hook(); if (enable_a20()) { ... } reset_coprocessor(); mask_all_interrupts(); setup_idt(); setup_gdt(); protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
void go_to_protected_mode(void) { /* Hook before leaving real mode, also disables interrupts */ realmode_switch_hook(); /* Enable the A20 gate */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* Reset coprocessor (IGNNE#) */ reset_coprocessor(); /* Mask all interrupts in the PIC */ mask_all_interrupts(); ...
io_delay
asm volatile("outb %%al,%0" : : "dN" (DELAY_PORT));
static void realmode_switch_hook(void) { if (boot_params.hdr.realmode_swtch) { asm volatile("lcallw *%0" : : "m" (boot_params.hdr.realmode_swtch) : "eax", "ebx", "ecx", "edx"); } else { asm volatile("cli"); outb(0x80, 0x70); /* Disable NMI */ io_delay(); } }
If the boot loader runs in a particularly hostile environment (such as
LOADLIN, which runs under DOS) it may be impossible to follow the
standard memory location requirements.
Such a boot loader may use the
following hooks that, if set, are invoked by the kernel at the
appropriate time. The use of these hooks should probably be
considered an absolutely last resort!
static void realmode_switch_hook(void) { if (boot_params.hdr.realmode_swtch) { asm volatile("lcallw *%0" : : "m" (boot_params.hdr.realmode_swtch) : "eax", "ebx", "ecx", "edx"); } else { asm volatile("cli"); outb(0x80, 0x70); /* Disable NMI */ io_delay(); } }
void go_to_protected_mode(void) { /* Hook before leaving real mode, also disables interrupts */ realmode_switch_hook(); * /* Enable the A20 gate */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* Reset coprocessor (IGNNE#) */ reset_coprocessor(); /* Mask all interrupts in the PIC */ mask_all_interrupts(); ...
int 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;
}
void go_to_protected_mode(void) { /* Hook before leaving real mode, also disables interrupts */ realmode_switch_hook(); /* Enable the A20 gate */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* Reset coprocessor (IGNNE#) */ reset_coprocessor(); /* Mask all interrupts in the PIC */ mask_all_interrupts(); ...
outb(0, 0xf0);
outb(0, 0xf1);
void go_to_protected_mode(void) { /* Hook before leaving real mode, also disables interrupts */ realmode_switch_hook(); /* Enable the A20 gate */ if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); } /* Reset coprocessor (IGNNE#) */ reset_coprocessor(); /* Mask all interrupts in the PIC */ mask_all_interrupts(); ...
PIC
(Programmable Interrupt Controller) and primary PIC
IRQ2
on the primary PIC
.IRQ2
line cascade PIC1 and PIC2outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */
outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */
/* Actual transition to protected mode... */ setup_idt(); setup_gdt(); protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
struct gdt_ptr {
u16 len;
u32 ptr;
} __attribute__((packed));
static void setup_idt(void) { static const struct gdt_ptr null_idt = {0, 0}; asm volatile("lidtl %0" : : "m" (null_idt)); }
/* Actual transition to protected mode... */ setup_idt(); setup_gdt(); protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
boot_gdt
static const u64 boot_gdt[] __attribute__((aligned(16))) = {
/* CS: code, read/execute, 4 GB, base 0 */
[GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),
/* DS: data, read/write, 4 GB, base 0 */
[GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),
/* TSS: 32-bit tss, 104 bytes, base 4096 */
/* We only have a TSS here to keep Intel VT happy;
we don't actually use it for anything. */
[GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),
};
static struct gdt_ptr gdt;
gdt.len = sizeof(boot_gdt)-1;
gdt.ptr = (u32)&boot_gdt + (ds() << 4);
asm volatile("lgdtl %0" : : "m" (gdt));
GDT_ENTRY
MacroGDT_ENTRY(base, limit, flag)
flag
of GDT_ENTRY /* Actual transition to protected mode... */ setup_idt(); setup_gdt(); protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4)); }
protected_mode_jump(jump_location, boot_paramters)
cs
Register into bx
&boot_params
into edx
GLOBAL(protected_mode_jump) movl %edx, %esi # Pointer to boot_params table xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx addl %ebx, 2f jmp 1f # Short jump to serialize on 386/486 1: ...
in_pm32
in_pm32
# Transition to 32-bit mode movl %edx, %esi # Pointer to boot_params table xorl %ebx, %ebx movw %cs, %bx shll $4, %ebx addl %ebx, 2f jmp 1f # Short jump to serialize on 386/486 1: movw $__BOOT_DS, %cx movw $__BOOT_TSS, %di
addl %ebx, 2f jmp 1f # Short jump to serialize on 386/486 1: movw $__BOOT_DS, %cx movw $__BOOT_TSS, %di
addl %ebx, 2f jmp 1f # Short jump to serialize on 386/486 1: movw $__BOOT_DS, %cx movw $__BOOT_TSS, %di
addl %ebx, 2f jmp 1f # Short jump to serialize on 386/486 1: movw $__BOOT_DS, %cx movw $__BOOT_TSS, %di
PE
bit in Control Register cr0
movw $__BOOT_DS, %cx movw $__BOOT_TSS, %di movl %cr0, %edx orb $X86_CR0_PE, %dl # Protected mode movl %edx, %cr0
0x66
Prefix which allows us to mix 16-bit and 32-bit code0xea
Jump opcodein_pm32
(cs << 4) + in_pm32__BOOT_CS
Target code segment movl %cr0, %edx orb $X86_CR0_PE, %dl # Protected mode movl %edx, %cr0 # Transition to 32-bit mode .byte 0x66, 0xea # ljmpl opcode 2: .long in_pm32 # offset .word __BOOT_CS # segment ENDPROC(protected_mode_jump)
GLOBAL(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 # The 32-bit code sets up its own stack, but this way we do have # a valid stack if some debugging hack wants to use it. addl %ebx, %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 jmpl *%eax # Jump to the 32-bit entrypoint ENDPROC(in_pm32)
GLOBAL(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 # The 32-bit code sets up its own stack, but this way we do have # a valid stack if some debugging hack wants to use it. addl %ebx, %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 jmpl *%eax # Jump to the 32-bit entrypoint ENDPROC(in_pm32)
GLOBAL(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 # The 32-bit code sets up its own stack, but this way we do have # a valid stack if some debugging hack wants to use it. addl %ebx, %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 jmpl *%eax # Jump to the 32-bit entrypoint ENDPROC(in_pm32)
GLOBAL(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 # The 32-bit code sets up its own stack, but this way we do have # a valid stack if some debugging hack wants to use it. addl %ebx, %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 jmpl *%eax # Jump to the 32-bit entrypoint ENDPROC(in_pm32)
heap_free
function?Thanks