亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区
Chinaunix
標(biāo)題:
amd64 arch啟動(dòng)代碼早期paging相關(guān)的疑問
[打印本頁]
作者:
captivated
時(shí)間:
2020-03-31 18:11
標(biāo)題:
amd64 arch啟動(dòng)代碼早期paging相關(guān)的疑問
本帖最后由 captivated 于 2020-03-31 18:44 編輯
x86 arch 的啟動(dòng)代碼比較復(fù)雜,為了說清楚我的具體問題,所以先做一個(gè)簡述(免得說我自己沒把基本問題搞清楚,省點(diǎn)抬杠時(shí)間)。
此外以下簡述主要針對(duì) x86_64 arch 而不是 i386.
1. 啟動(dòng)映像生成:
x86 arch 的啟動(dòng)映像為 $OUT/arch/x86/boot/bzImage. $OUT 表示 kernel 編譯輸出目錄(如果編譯時(shí)不指定 -o 輸出目錄,則和編譯源碼是同一個(gè)目錄)。
bzImage 構(gòu)成如下。為了方便,$OUT/arch/x86/boot 目錄簡稱為 $(boot) 路徑.
1) 最終的 bzImage 是由 $(boot)/setup.bin 和 $(boot)/vmlinux.bin 組裝成的.
$(boot)/vmlinux.bin 又由 $(boot)/compressed/vmlinux 通過 objcopy 剝離掉 .comment 和 .note 段而成.
而 $(boot)/compressed/vmlinux 則由 $(boot)/compressed 下面的各個(gè)目標(biāo)文件組成, 里面最重要的是
$(boot)/compressed/piggy.o 這個(gè)文件.
bzImage <-+ $(boot)/setup.bin
#######-+ $(boot)/vmlinux.bin <-+ $(boot)/compressed/vmlinux <-+ $(boot)/compressed/head_64.o
############################################### $(boot)/compressed/misc.o
############################################### ...
############################################### $(boot)/compressed/piggy.o
復(fù)制代碼
2) 為什么說 piggy.o 重要, 因?yàn)樗怯蓧嚎s了的 vmlinux 構(gòu)成的. 這里默認(rèn)選 gzip 壓縮.
而 vmlinux.bin.gz 又是由 compressed 同目錄下的 vmlinux.bin 和 vmlinux.relocs 構(gòu)成的.
vmlinux.bin 和 vmlinux.relocs, 則來自于 $OUT 目錄下的 VMLINUX(實(shí)際文件名就是 $OUT/vmlinux. 大寫的意思是表示它其實(shí)就是編譯最終的鏈接文件, 從它到最終的 bzImage 不過是一堆二進(jìn)制工具為了啟動(dòng)加載協(xié)議做的一堆變換而已).
$(boot)/compressed/piggy.o <+ $(boot)/compressed/vmlinux.bin.gz <-+ $(boot)/compressed/vmlinux.bin
############################################### $(boot)/compressed/vmlinux.relocs
復(fù)制代碼
2. x86 arch 啟動(dòng)問題
x86 相關(guān)的 boot protocal 有幾種. 不管哪一種,總之 bzImage 得要 bootloader 加載到內(nèi)存里面然后去運(yùn)行才行.
前面的映像構(gòu)建過程為什么是那樣子, 估計(jì)也和 x86 arch “歷史悠久”, boot protocal 復(fù)雜相關(guān).
但大概來說, 有這么幾種 boot protocal:
1) 實(shí)模式 boot protocal.
這種情況下, bootloader 是運(yùn)行在實(shí)模式的. 就算 bootloader 自己切換了保護(hù)模式, 它加載完 kernel(bzImage)并跳轉(zhuǎn)過去執(zhí)行前, 也得切回實(shí)模式, 因?yàn)?protocal 就是協(xié)議嘛, 大家要有個(gè)商量. 這時(shí) bootloader 切換過去后, 最先從 setup.bin 開始運(yùn)行(bzImage 是由 setup.bin + vmlinux.bin 包的), 也就是走
arch/x86/boot/header.S -> _start -> calll main
arch/x86/boot/main.c -> main -> go_to_protected_mode
arch/x86/boot/pm.c -> go_to_protected_mode -> protected_mode_jump
arch/x86/boot/pmjump.S -> GLOBAL(protected_mode_jump) -> jmpl *%eax
經(jīng)過這些步驟, 就會(huì)開始執(zhí)行到 vmlinux.bin 里面的東西了.
2) 32 位 boot protocal
現(xiàn)在 bootloader 已經(jīng)很強(qiáng)大了, 所以通常 bootloader 自己已經(jīng)切到 32 位 protected mode, 然后就不走 setup.bin 那一套了.
前面說了 bzImage 是由 setup.bin + vmlinux.bin(都在 $(boot) 目錄下)組裝而成, 跳過 setup.bin 的話, 當(dāng)然就直接從 vmlinux.bin 開始執(zhí)行了.
bootloader 為什么可以這樣做, 因?yàn)?bzImage 就是 bootloader 加載的, 它當(dāng)然知道 setup.bin vmlinux.bin 分別在什么內(nèi)存位置.
一般來說, setup.bin 必須要運(yùn)行在實(shí)模式, 因此其加載地址是物理內(nèi)存 1MB 以下的, 而 vmlinux.bin 會(huì)剛剛好從 1MB 位置開始放置.
anyway, 這時(shí)候走的是這個(gè):
arch/x86/boot/compressed/head_64.S -> ENTRY(startup_32) -> ENTRY(startup_64) -> jmp *%eax
這里還要說明一下. 前面映像構(gòu)建過程說得清楚, 真正的 kernel 是 piggy.o(由 $(OUT)/vmlinux 壓縮, 然后生成的目標(biāo)文件).
startup_32 -> startup_64 主要是從 32 位保護(hù)模式切換到 64 bit long mode. 切換之前會(huì)建立一個(gè) 4GB 簡單 identity map(因?yàn)榍袚Q到 64 bit long mode 的條件之一就是, paging 必須是開的, 即 CR0.PG == 1).
而 startup_64 呢, 則會(huì)調(diào)用一個(gè)解壓縮函數(shù)將 vmlinux.bin.gz 解壓, 然后跳轉(zhuǎn)到解壓后的 kernel 入口去執(zhí)行. 這里面可能還會(huì)有重定位處理等等.
如果關(guān)掉 KASLR, 那么解壓后的 kernel, 也就是真正的 $(OUT)/vmlinux, 默認(rèn)會(huì)放在 16MB 的位置.
3) 64 位 boot protocal
顧名思義, 就是 bootloader 有點(diǎn)過分強(qiáng)大(或者討厭)了, 跳轉(zhuǎn)到 kernel 之前, 它會(huì)自己先切到 64 bit long mode.
也就是直接執(zhí)行 arch/x86/boot/compressed/head_64.S 中的 startup_64 了.
這時(shí)最初的 paging 是 bootloader 建立的.
3. vmlinux entry
好了,前面這些復(fù)雜的過程略過. 不管是先從 arch/x86/boot/setup.S 開始, 還是先從 arch/x86/boot/compressed/head_64.S 開始,
最終解壓縮后的 kernel 入口點(diǎn)是:
arch/x86/kernel/head_64.S 里面的 startup_64 函數(shù)(or 一個(gè)全局符號(hào)).
有人可能會(huì)問這不和 compressed/head_64.S 里面的 startup_64 重名嗎, 答案是它們根本沒鏈接在一起, 各管各的, 不會(huì)有重定義錯(cuò)誤放心.
啊, 約了不知道多少次電影請喝了多少次奶茶爬了多少次山, 終于可以約爬床了, 終于寬衣解帶了, 興奮之情溢于言表啊...
結(jié)果才看了特么幾行, 我就被郁悶到了...
...
具體問題敘述還有點(diǎn)小麻煩, 呆會(huì)貼上來.
...
作者:
captivated
時(shí)間:
2020-03-31 18:31
回來了
在線代碼見
https://elixir.bootlin.com/linux ... 86/kernel/head_64.S
.Ljump_to_C_code:
/*
* Jump to run C code and to be on a real kernel address.
* Since we are running on identity-mapped space we have to jump
* to the full 64bit address, this is only possible as indirect
* jump. In addition we need to ensure %cs is set so we make this
* a far return.
*
* Note: do not change to far jump indirect with 64bit offset.
*
* AMD does not support far jump indirect with 64bit offset.
* AMD64 Architecture Programmer's Manual, Volume 3: states only
* JMP FAR mem16:16 FF /5 Far jump indirect,
* with the target specified by a far pointer in memory.
* JMP FAR mem16:32 FF /5 Far jump indirect,
* with the target specified by a far pointer in memory.
*
* Intel64 does support 64bit offset.
* Software Developer Manual Vol 2: states:
* FF /5 JMP m16:16 Jump far, absolute indirect,
* address given in m16:16
* FF /5 JMP m16:32 Jump far, absolute indirect,
* address given in m16:32.
* REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
* address given in m16:64.
*/
pushq $.Lafter_lret # put return address on stack for unwinder
xorl %ebp, %ebp # clear frame pointer
movq initial_code(%rip), %rax
pushq $__KERNEL_CS # set correct cs
pushq %rax # target address in negative space
lretq
復(fù)制代碼
這里跳轉(zhuǎn)到
x86_64_start_kernel@head64.c
, 在線代碼
https://elixir.bootlin.com/linux ... x86/kernel/head64.c
代碼
asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
{
...
cr4_init_shadow();
/* Kill off the identity-map trampoline */
reset_early_page_tables();
clear_bss();
clear_page(init_top_pgt);
/*
* SME support may update early_pmd_flags to include the memory
* encryption mask, so it needs to be called before anything
* that may generate a page fault.
*/
sme_early_init();
kasan_early_init();
idt_setup_early_handler();
復(fù)制代碼
這里面, reset_early_page_tables 反手就把 pgt 給清了!
/* Wipe all early page tables except for the kernel symbol map */
static void __init reset_early_page_tables(void)
{
memset(early_top_pgt, 0, sizeof(pgd_t)*(PTRS_PER_PGD-1));
next_early_pgt = 0;
write_cr3(__sme_pa_nodebug(early_top_pgt));
}
復(fù)制代碼
但這時(shí)候,中斷處理還沒設(shè)立,%cr3 里面就是 early_top_pgt,這么把 PGT 一清,不會(huì) paging 異常嗎???。!
作者:
captivated
時(shí)間:
2020-03-31 21:22
哦,我知道了。
沒注意
memset(early_top_pgt, 0, sizeof(pgt_t) * (PTRS_PER_PGD - 1));
而不是
memset(early_top_pgt, 0, sizeof(pgt_t) * PTRS_PER_PGD);
并沒有全清, 也就是說 early_top_gpt[511] 是沒有清掉的, 那么 l2_kernel_pgt 映射的 512MB 還在, 但是 head64.c 里面 __startup_64 建立的 identity mapping 被清掉了.
艸, 把我困惑了好兩天, 原來是自己看代碼太不仔細(xì)了
歡迎光臨 Chinaunix (http://www.72891.cn/)
Powered by Discuz! X3.2