亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 42838 | 回復: 2
打印 上一主題 下一主題

[內存管理] amd64 arch啟動代碼早期paging相關的疑問 [復制鏈接]

論壇徽章:
3
15-16賽季CBA聯(lián)賽之山東
日期:2016-10-30 08:47:3015-16賽季CBA聯(lián)賽之佛山
日期:2016-12-17 00:06:31CU十四周年紀念徽章
日期:2017-12-03 01:04:02
跳轉到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2020-03-31 18:11 |只看該作者 |倒序瀏覽
本帖最后由 captivated 于 2020-03-31 18:44 編輯

x86 arch 的啟動代碼比較復雜,為了說清楚我的具體問題,所以先做一個簡述(免得說我自己沒把基本問題搞清楚,省點抬杠時間)。
此外以下簡述主要針對 x86_64 arch 而不是 i386.


1. 啟動映像生成:

x86 arch 的啟動映像為 $OUT/arch/x86/boot/bzImage. $OUT 表示 kernel 編譯輸出目錄(如果編譯時不指定 -o 輸出目錄,則和編譯源碼是同一個目錄)。
bzImage 構成如下。為了方便,$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 下面的各個目標文件組成, 里面最重要的是
    $(boot)/compressed/piggy.o 這個文件.

  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
復制代碼


2) 為什么說 piggy.o 重要, 因為它是由壓縮了的 vmlinux 構成的. 這里默認選 gzip 壓縮.
而 vmlinux.bin.gz 又是由 compressed 同目錄下的 vmlinux.bin 和 vmlinux.relocs 構成的.
vmlinux.bin 和 vmlinux.relocs, 則來自于 $OUT 目錄下的 VMLINUX(實際文件名就是 $OUT/vmlinux. 大寫的意思是表示它其實就是編譯最終的鏈接文件, 從它到最終的 bzImage 不過是一堆二進制工具為了啟動加載協(xié)議做的一堆變換而已).

  1. $(boot)/compressed/piggy.o <+ $(boot)/compressed/vmlinux.bin.gz <-+ $(boot)/compressed/vmlinux.bin
  2. ###############################################  $(boot)/compressed/vmlinux.relocs
復制代碼


2. x86 arch 啟動問題


x86 相關的 boot protocal 有幾種. 不管哪一種,總之 bzImage 得要 bootloader 加載到內存里面然后去運行才行.
前面的映像構建過程為什么是那樣子, 估計也和 x86 arch “歷史悠久”, boot protocal 復雜相關.
但大概來說, 有這么幾種 boot protocal:

1) 實模式 boot protocal.
這種情況下, bootloader 是運行在實模式的. 就算 bootloader 自己切換了保護模式, 它加載完 kernel(bzImage)并跳轉過去執(zhí)行前, 也得切回實模式, 因為 protocal 就是協(xié)議嘛, 大家要有個商量. 這時 bootloader 切換過去后, 最先從 setup.bin 開始運行(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)過這些步驟, 就會開始執(zhí)行到 vmlinux.bin 里面的東西了.


2) 32 位 boot protocal
現(xiàn)在 bootloader 已經(jīng)很強大了, 所以通常 bootloader 自己已經(jīng)切到 32 位 protected mode, 然后就不走 setup.bin 那一套了.
前面說了 bzImage 是由 setup.bin + vmlinux.bin(都在 $(boot) 目錄下)組裝而成, 跳過 setup.bin 的話, 當然就直接從 vmlinux.bin 開始執(zhí)行了.
bootloader 為什么可以這樣做, 因為 bzImage 就是 bootloader 加載的, 它當然知道 setup.bin vmlinux.bin 分別在什么內存位置.
一般來說, setup.bin 必須要運行在實模式, 因此其加載地址是物理內存 1MB 以下的, 而 vmlinux.bin 會剛剛好從 1MB 位置開始放置.
anyway, 這時候走的是這個:
arch/x86/boot/compressed/head_64.S -> ENTRY(startup_32) -> ENTRY(startup_64) -> jmp *%eax

這里還要說明一下. 前面映像構建過程說得清楚, 真正的 kernel 是 piggy.o(由 $(OUT)/vmlinux 壓縮, 然后生成的目標文件).
startup_32 -> startup_64 主要是從 32 位保護模式切換到 64 bit long mode. 切換之前會建立一個 4GB 簡單 identity map(因為切換到 64 bit long mode 的條件之一就是, paging 必須是開的, 即 CR0.PG == 1).
而 startup_64 呢, 則會調用一個解壓縮函數(shù)將 vmlinux.bin.gz 解壓, 然后跳轉到解壓后的 kernel 入口去執(zhí)行. 這里面可能還會有重定位處理等等.
如果關掉 KASLR, 那么解壓后的 kernel, 也就是真正的 $(OUT)/vmlinux, 默認會放在 16MB 的位置.



3) 64 位 boot protocal

顧名思義, 就是 bootloader 有點過分強大(或者討厭)了, 跳轉到 kernel 之前, 它會自己先切到 64 bit long mode.
也就是直接執(zhí)行 arch/x86/boot/compressed/head_64.S 中的 startup_64 了.
這時最初的 paging 是 bootloader 建立的.


3. vmlinux entry

好了,前面這些復雜的過程略過. 不管是先從 arch/x86/boot/setup.S 開始, 還是先從 arch/x86/boot/compressed/head_64.S 開始,
最終解壓縮后的 kernel 入口點是:
arch/x86/kernel/head_64.S 里面的 startup_64 函數(shù)(or 一個全局符號).
有人可能會問這不和 compressed/head_64.S 里面的 startup_64 重名嗎, 答案是它們根本沒鏈接在一起, 各管各的, 不會有重定義錯誤放心.

啊, 約了不知道多少次電影請喝了多少次奶茶爬了多少次山, 終于可以約爬床了, 終于寬衣解帶了, 興奮之情溢于言表啊...

結果才看了特么幾行, 我就被郁悶到了...

...

具體問題敘述還有點小麻煩, 呆會貼上來.

...


論壇徽章:
3
15-16賽季CBA聯(lián)賽之山東
日期:2016-10-30 08:47:3015-16賽季CBA聯(lián)賽之佛山
日期:2016-12-17 00:06:31CU十四周年紀念徽章
日期:2017-12-03 01:04:02
2 [報告]
發(fā)表于 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
復制代碼
這里跳轉到 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();
復制代碼

這里面, 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. }
復制代碼

但這時候,中斷處理還沒設立,%cr3 里面就是 early_top_pgt,這么把 PGT 一清,不會 paging 異常嗎???。!



論壇徽章:
3
15-16賽季CBA聯(lián)賽之山東
日期:2016-10-30 08:47:3015-16賽季CBA聯(lián)賽之佛山
日期:2016-12-17 00:06:31CU十四周年紀念徽章
日期:2017-12-03 01:04:02
3 [報告]
發(fā)表于 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 被清掉了.

艸, 把我困惑了好兩天, 原來是自己看代碼太不仔細了
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術有限公司. 版權所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉載本站內容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP