日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

RISC-V Linux的匯編啟動部分比較簡單,不算復雜。有兩個部分比較核心:頁表創建和重定向。頁表創建是用C語言寫的,今天先分析匯編部分,先帶大家分析整體匯編啟動流程,然后分析重定向。

注意:本文基于linux5.10.111內核

匯編啟動流程

先從整體分析匯編做的事情,有個大體框架。

路徑:arch/riscv/kernel/head.S,入口是ENTRY(_start_kernel)

ENTRY(_start_kernel)開始進行啟動前的一些初始化,建立頁表前的主要工作:

    關閉所有中斷
    /* 關閉所有中斷 */
        csrw CSR_IE, zero
        csrw CSR_IP, zero

    登錄后復制

      加載全局指針gp
      /* 加載全局指針gp */
      .option push
      .option norelax
          la gp, __global_pointer$
      .option pop

      登錄后復制

        disable FPU
        /* 禁用 FPU 以檢測內核空間中浮點的非法使用*/
            li t0, SR_FS
            csrc CSR_STATUS, t0

        登錄后復制

          選擇一個核啟動
          /* 選擇一個核啟動 */
              la a3, hart_lottery
              li a2, 1
              amoadd.w a3, a2, (a3)
              bnez a3, .Lsecondary_start

          登錄后復制

            清楚bss段
            /* 清除bss */
                la a3, __bss_start
                la a4, __bss_stop
                ble a4, a3, clear_bss_done

            登錄后復制

              保存hart id和dtb地址
              /* 保存hatr id和dtb地址,hart id保存到a0,dtb地址保存到a1 */
                  mv s0, a0
                  mv s1, a1
                  la a2, boot_cpu_hartid

              登錄后復制

                設置sp指針
                    la sp, init_thread_union + THREAD_SIZE

                登錄后復制

                  上述工作完成,會開始臨時頁表的創建,跳轉到C函數setup_vm建立臨時頁表
                      mv a0, s1
                      call setup_vm // 跳轉到C函數setup_vm,setup_vm會創建臨時頁表

                  登錄后復制

                    重定向
                    #ifdef CONFIG_MMU
                        la a0, early_pg_dir
                        call relocate	//重定向,實際就是開啟MMU
                    #endif

                    登錄后復制

                      設置異常向量地址,重載C環境
                          call setup_trap_vector
                      /* 重載C環境 */
                          la tp, init_task
                          sw zero, TASK_TI_CPU(tp)
                          la sp, init_thread_union + THREAD_SIZE

                      登錄后復制

                        最后跳轉到C函數start_kernel,開始C語言部分初始化,匯編部分執行完畢
                        tail start_kernel

                        登錄后復制

                        完整_start_kernel匯編代碼:

                        ENTRY(_start_kernel)
                        	/* 關閉所有中斷 */
                        	csrw CSR_IE, zero
                        	csrw CSR_IP, zero
                        
                        	/* 在源碼中,這里有一個M模式處理的宏,這里沒有用到,直接跳過*/
                        
                        	/* 加載全局指針gp */
                        .option push
                        .option norelax
                        	la gp, __global_pointer$
                        .option pop
                        
                        	/* 禁用 FPU 以檢測內核空間中浮點的非法使用*/
                        	li t0, SR_FS
                        	csrc CSR_STATUS, t0
                        
                        #ifdef CONFIG_SMP
                        	li t0, CONFIG_NR_CPUS
                        	blt a0, t0, .Lgood_cores
                        	tail .Lsecondary_park
                        .Lgood_cores:
                        #endif
                        
                        	/* 選擇一個核啟動 */
                        	la a3, hart_lottery
                        	li a2, 1
                        	amoadd.w a3, a2, (a3)
                        	bnez a3, .Lsecondary_start
                        
                        	/* 清除bss */
                        	la a3, __bss_start
                        	la a4, __bss_stop
                        	ble a4, a3, clear_bss_done
                        clear_bss:
                        	REG_S zero, (a3)
                        	add a3, a3, RISCV_SZPTR
                        	blt a3, a4, clear_bss
                        clear_bss_done:
                        
                        	/* 保存hatr id和dtb地址,hart id保存到a0,dtb地址保存到a1 */
                        	mv s0, a0
                        	mv s1, a1
                        	la a2, boot_cpu_hartid
                        	REG_S a0, (a2)
                        
                        	/* 初始化頁表,然后重定向到虛擬地址 */
                        	la sp, init_thread_union + THREAD_SIZE
                        	mv a0, s1
                        	call setup_vm // 跳轉到C函數setup_vm,setup_vm會創建臨時頁表
                        #ifdef CONFIG_MMU
                        	la a0, early_pg_dir
                        	call relocate	//重定向,實際就是開啟MMU
                        #endif /* CONFIG_MMU */
                        
                        	call setup_trap_vector
                        	/* 重載C環境 */
                        	la tp, init_task
                        	sw zero, TASK_TI_CPU(tp)
                        	la sp, init_thread_union + THREAD_SIZE
                        
                        #ifdef CONFIG_KASAN
                        	call kasan_early_init
                        #endif
                        	/* Start the kernel */
                        	call soc_early_init
                        	tail start_kernel	//跳轉到C函數start_kernel,開始C語言部分初始化

                        登錄后復制

                        匯編中非常重要的一個部分就是頁表的創建,關乎著后面的程序能不能繼續往下跑。setup_vm創建頁表后就會開始執行relocate重定向,這個重定向主要開啟mmu,下面分析relocate的匯編。

                        relocate

                        relocate重定向,就是在開啟mmu。開啟mmu的操作就是將一級頁表的地址以及權限寫到satp寄存器中,這就算開啟mmu了。

                        #ifdef CONFIG_MMU
                            la a0, early_pg_dir //跳轉到relocate前,先把第一級頁表early_pg_dir的地址存入a0
                            call relocate		//跳轉到relocate,開啟MMU
                        #endif

                        登錄后復制

                        relocate有兩次開啟mmu的操作,第一次開啟mmu使用的是setup_vm()建立的trampoline_gd_dir頁表,這頁表保存的是kernel的前2M內存。第二次開啟MMU使用的是early_pg_dir頁表,這個頁表映射了整個kernel內存以及dtb的4M空間。

                        如果trampoline_pg_dir或者early_pg_dir這兩個頁表的映射沒弄好的話,開啟MMU的時候就會失敗,所以頁表的建立十分關鍵。頁表創建后續再深究,下面分析relocate匯編代碼。

                          計算返回地址

                          返回地址就是ra加上虛擬地址和物理地址之間的偏移量,這個是固定偏移量。PAGE_OFFSETkernel入口地址對應的虛擬地址,_start就是kernel入口地址的虛擬地址,PAGE_OFFSET_start就得到它們之間的偏移,然后再和ra相加,就是返回地址。

                          /* Relocate return address */
                          	li a1, PAGE_OFFSET
                          	la a2, _start
                          	sub a1, a1, a2
                          	add ra, ra, a1

                          登錄后復制

                            將異常入口1f的虛擬地址寫入stvec寄存器

                            因為一旦開啟MMU,地址都變成了虛擬地址,原來訪問的都是物理地址,開啟MMU時,地址發生了改變,VA != PA,從而進入異常,所以要先設置異常入口地址,此時的異常入口為1f

                            /* Point stvec to virtual address of intruction after satp write */
                            	la a2, 1f
                            	add a2, a2, a1
                            	csrw CSR_TVEC, a2

                            登錄后復制

                              提前計算切換到early_pg_dir頁表要寫入satp的值

                              再進入relocate之前,就已經把early_pg_dir賦值給a0了,所以a0是early_pg_dir。srl是邏輯右移,mmu使用的是sv39,虛擬地址39位,物理地址56位:

                              低12位是偏移量,所以PAGE_SHIFT等于12,將early_pg_dir地址右移12位存到a2。根據satp寄存器定義:

                              MODE等于0x8代表使用sv39 mmu0x0代表不進行地址翻譯,即不開啟MMU。這里STAP_MODEsv39,即0x8。將early_pg_dir地址和SATP_MODE進行或運算后,即可得到寫入satp寄存器的值,最后保存到a2

                              /* Compute satp for kernel page tables, but don't load it yet */
                              	srl a2, a0, PAGE_SHIFT
                              	li a1, SATP_MODE	//sv39 mmu
                              	or a2, a2, a1

                              登錄后復制

                                第一次開啟MMU,使用trampoline_pg_dir頁表

                                satp值的計算和上述是一樣的。開啟MMU之前,通過sfence.vma命令先刷新TLB。此時開啟MMU,就會進入下面的標號為1的匯編段

                                	la a0, trampoline_pg_dir
                                	srl a0, a0, PAGE_SHIFT
                                	or a0, a0, a1
                                	sfence.vma	
                                	csrw CSR_SATP, a0

                                登錄后復制

                                進入異常1f段,重新設置異常入口為.Lsecondary_park,然后切換到early_pg_dir頁表,相當于第二次開啟MMU。此時,如果之前建立的early_pg_dir頁表不對,則會就進入.Lsecondary_park.Lsecondary_park里面是個wfi指令,是個死循環。

                                完整relocate匯編代碼:

                                relocate:
                                	/* Relocate return address */
                                	li a1, PAGE_OFFSET
                                	la a2, _start
                                	sub a1, a1, a2
                                	add ra, ra, a1
                                
                                	/* Point stvec to virtual address of intruction after satp write */
                                	la a2, 1f
                                	add a2, a2, a1
                                	csrw CSR_TVEC, a2
                                
                                	/* Compute satp for kernel page tables, but don't load it yet */
                                	srl a2, a0, PAGE_SHIFT
                                	li a1, SATP_MODE
                                	or a2, a2, a1
                                
                                	/*
                                	 * Load trampoline page directory, which will cause us to trap to
                                	 * stvec if VA != PA, or simply fall through if VA == PA.  We need a
                                	 * full fence here because setup_vm() just wrote these PTEs and we need
                                	 * to ensure the new translations are in use.
                                	 */
                                	la a0, trampoline_pg_dir
                                	srl a0, a0, PAGE_SHIFT
                                	or a0, a0, a1
                                	sfence.vma
                                	csrw CSR_SATP, a0
                                .align 2
                                1:
                                	/* Set trap vector to spin forever to help debug */
                                	la a0, .Lsecondary_park
                                	csrw CSR_TVEC, a0
                                
                                	/* Reload the global pointer */
                                .option push
                                .option norelax
                                	la gp, __global_pointer$
                                .option pop
                                
                                	/*
                                	 * Switch to kernel page tables.  A full fence is necessary in order to
                                	 * avoid using the trampoline translations, which are only correct for
                                	 * the first superpage.  Fetching the fence is guarnteed to work
                                	 * because that first superpage is translated the same way.
                                	 */
                                	csrw CSR_SATP, a2
                                	sfence.vma
                                
                                	ret

                                登錄后復制

                                總結

                                以上就是RISC-V Linux的匯編啟動流程,雖說RISC-V的指令不復雜,但要理解這個匯編啟動的部分,還是需要一點基礎和時間。另外,大多數人工作中基本用不上匯編,只有真正用上了理解才會比較深。希望本文能夠幫助到有需要的人。

以上就是RISC-V Linux匯編啟動過程分析的詳細內容,更多請關注www.92cms.cn其它相關文章!

分享到:
標簽:RISC 分析 啟動 匯編 過程
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定