顯示具有 Booting 標籤的文章。 顯示所有文章
顯示具有 Booting 標籤的文章。 顯示所有文章

2010年1月15日 星期五

An Introduction to Linux Kernel Booting Sequence Part 3

上篇文章提到go_to_protected_mode函式最後會執行由組合語言所撰寫的函式protected_mode_jump,此函式工作如下:


函式呼叫原型:
protected_mode_jump(boot_params.hdr.code32_start, (u32)&boot_params + (ds() << 4));

其程式碼:

/*
* void protected_mode_jump(u32 entrypoint, u32 bootparams);
*/
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:

movw $__BOOT_DS, %cx
movw $__BOOT_TSS, %di

### 設定x86之cr0暫存器的PE位元,如此便能進入proctected-mode
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)

.code32
.section ".text32","ax"
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

## 跳至code32_start的標籤
jmpl *%eax # Jump to the 32-bit entrypoint
ENDPROC(in_pm32)


至於code32_start在哪裡呢? 此標籤定義在arch/x86/boot/header.S,如下所示:


code32_start: # here loaders can put a different
# start address for 32-bit code.
.long 0x100000 # 0x100000 = default for big kernel



也就是說,核心已做完real-mode Linux程式碼該做的事情,並跳至protected-mode Linux程式碼的起始位址 (1MB),請看下圖:

linux kernel ram content after loading the image into the ram

[Reference]
【The Kernel Boot Process】
【Linux Kernel Source 2.6.31】

2010年1月12日 星期二

An Introduction to Linux Kernel Booting Sequence Part 2

上篇文章Linux核心被載入記憶體並執行start_of_setup標籤(arch/x86/boot/header.S),在start_of_setup標籤末段會執行由C語言撰寫的main函式 (arch/x86/boot/main.c),此函式工作如下所示:

1. 複製開機標頭 (boot header) 至 zero page
2. 設定heap_end變數
3. 偵測記憶體layout
4. 設定鍵盤重複率 (Keyboard Repeat Rate)
5. 查詢MCA (Micro Channel Architecture) 資訊
6. 查詢Intel SpeedStep
7. 設定video模式
8. 進入protected-mode (go_to_protected_mode,arch/x86/boot/pm.c)

其中3-6項,經由BIOS呼叫 (intcall,arch/x86/boot/bioscall.S),以便向BIOS取到相關的資訊。
main函式最後呼叫go_to_protected_mode函式,以便從real-mode轉換至protected-mode。

在真正進入protected-mode之前,有幾項工作必須先被執行,也就是go_to_protected_mode函式裡所做的事情,如下所示:

1. 設定A20位址線,如此便能存取超過1MB以後的資料 (因為在real-mode,只能存取1MB以下的資料)。

2. 設定idt (interrupt descriptor table)。在real-mode,interrupt vector table的起始位址是從記憶體零的位址開始算起。然而,在protected-mode,interrupt vector table是儲存於CPU的暫存器 (IDTR,Interrupt Descriptor Table Register),因此轉換至protected-mode之前,必須先設定idt。

3. 設定gdt (global descriptor table)。由於,real-mode與proctected-mode位址轉換 (由邏輯位址 [logical address] 轉換成線性位址 [linear address]),詳見x86_memory_segmentation。因此轉換至protected-mode之前,必須先設定gdt,以便能正確地根據logical address轉換成linear address。


在go_to_protected_mode函式最後會執行由組合語言所撰寫的函式protected_mode_jump,此函式便是設定CPU為protected-mode,細節留到下回做進一步解釋。

【Reference】 The Kernel Boot Process

2009年12月25日 星期五

An Introduction to Linux Kernel Booting Sequence Part 1

此系列文章將著重於探討Linux x86核心開機過程,
其中, 假設BIOS跟Boot Loader (GRUB, for example) 運行正常, 且Boot Loader已將核心 (/boot/vmlinuz-2.6.31-16-generic) 載入記憶體並跳至載入記憶體之起始位置執行.

Linux核心主要可以分為兩大部份: 1. 執行於CPU之real mode的核心程式碼 2. 執行於CPU之protected mode的核心程式碼. 然而這兩大部份被載入至不同的記憶體區段: 1. real mode核心程式碼載入至0-640KB的某一區間, 2. protected mode核心程式碼載入至1MB記憶體位址. 如下所示:

linux kernel ram content after loading the image into the ram

Figure 1. Linux kernel RAM content after loading the Linux kernel image into RAM


首先第一個被執行的Linux核心檔案為arch/x86/boot/header.S,此檔案由組合語言所撰寫,此檔案前512位元組 (其中有15個位元組為核心標頭) 為早期的核心開機磁區,但現今的boot loader直接跳過此512位元組。緊接著在512位元組之後,即為Linux核心執行的起始點,底下展示此起始點的兩行程式碼 (以Linux核心原始碼版本2.6.31為例):
.byte 0xeb # short (2-byte) jump
.byte start_of_setup-1f


此兩行代表跳至start_of_setup標籤。".byte 0xeb" (arch/x86/boot/header.S:112)代表two-bytes jmp組合語言指令。".byte start_of_setup-1f" (arch/x86/boot/header.S:113)的"f"代表forward short jump,"start_of_setup-1"代表用"start_of_setup" (arch/x86/boot/header.S:241)標籤起始位址減去"1" (arch/x86/boot/header.S:114)標籤起始位址。

所以上述兩個位元組即代表: 以目前的位址向下跳至n個位元組 (即start_of_setup-1),因此便能正確地跳至start_of_setup標籤。

範例 (以Ubuntu 9.10之2.6.31-16-generic核心為例)

此範例利用hexdump將Linux核心dump出來,並觀看其16進制碼用以對照上述之論述。

adrian@adrian-desktop:/boot$ hexdump vmlinuz-2.6.31-16-generic > ~/vmlinuz-2.6.31-16-generic-hexdump
adrian@adrian-desktop:/boot$ gvim ~/vmlinuz-2.6.31-16-generic-hexdump

其結果如下所示:
hexdump
Figure 2. Hexdump from Linux Kernel

其中,0xAA55為Linux早期的核心開機磁區最後一個位元組(第512個位元組,arch/x86/boot/header.S:103)。0x62eb代表往下跳至62個位元組,即0x00000262位址。

本篇文章只介紹Linux之real-mode核心程式碼的entry point,往後會有陸陸續續的文章繼續探討。

【Reference】
The Kernel Boot Process