亚洲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è)文件.

  1. bzImage <-+ $(boot)/setup.bin
  2. #######-+ $(boot)/vmlinux.bin <-+ $(boot)/compressed/vmlinux <-+ $(boot)/compressed/head_64.o
  3. ############################################### $(boot)/compressed/misc.o
  4. ############################################### ...
  5. ############################################### $(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é)議做的一堆變換而已).

  1. $(boot)/compressed/piggy.o <+ $(boot)/compressed/vmlinux.bin.gz <-+ $(boot)/compressed/vmlinux.bin
  2. ###############################################  $(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

  1. .Ljump_to_C_code:
  2.         /*
  3.          * Jump to run C code and to be on a real kernel address.
  4.          * Since we are running on identity-mapped space we have to jump
  5.          * to the full 64bit address, this is only possible as indirect
  6.          * jump.  In addition we need to ensure %cs is set so we make this
  7.          * a far return.
  8.          *
  9.          * Note: do not change to far jump indirect with 64bit offset.
  10.          *
  11.          * AMD does not support far jump indirect with 64bit offset.
  12.          * AMD64 Architecture Programmer's Manual, Volume 3: states only
  13.          *        JMP FAR mem16:16 FF /5 Far jump indirect,
  14.          *                with the target specified by a far pointer in memory.
  15.          *        JMP FAR mem16:32 FF /5 Far jump indirect,
  16.          *                with the target specified by a far pointer in memory.
  17.          *
  18.          * Intel64 does support 64bit offset.
  19.          * Software Developer Manual Vol 2: states:
  20.          *        FF /5 JMP m16:16 Jump far, absolute indirect,
  21.          *                address given in m16:16
  22.          *        FF /5 JMP m16:32 Jump far, absolute indirect,
  23.          *                address given in m16:32.
  24.          *        REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
  25.          *                address given in m16:64.
  26.          */
  27.         pushq        $.Lafter_lret        # put return address on stack for unwinder
  28.         xorl        %ebp, %ebp        # clear frame pointer
  29.         movq        initial_code(%rip), %rax
  30.         pushq        $__KERNEL_CS        # set correct cs
  31.         pushq        %rax                # target address in negative space
  32.         lretq
復(fù)制代碼
這里跳轉(zhuǎn)到 x86_64_start_kernel@head64.c, 在線代碼 https://elixir.bootlin.com/linux ... x86/kernel/head64.c

代碼
  1. asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
  2. {
  3.         ...

  4.         cr4_init_shadow();

  5.         /* Kill off the identity-map trampoline */
  6.         reset_early_page_tables();

  7.         clear_bss();

  8.         clear_page(init_top_pgt);

  9.         /*
  10.          * SME support may update early_pmd_flags to include the memory
  11.          * encryption mask, so it needs to be called before anything
  12.          * that may generate a page fault.
  13.          */
  14.         sme_early_init();

  15.         kasan_early_init();

  16.         idt_setup_early_handler();
復(fù)制代碼

這里面, reset_early_page_tables 反手就把 pgt 給清了!
  1. /* Wipe all early page tables except for the kernel symbol map */
  2. static void __init reset_early_page_tables(void)
  3. {
  4.         memset(early_top_pgt, 0, sizeof(pgd_t)*(PTRS_PER_PGD-1));
  5.         next_early_pgt = 0;
  6.         write_cr3(__sme_pa_nodebug(early_top_pgt));
  7. }
復(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