Heim  >  Artikel  >  Betrieb und Instandhaltung  >  Analyse des Startvorgangs der RISC-V-Linux-Assembly

Analyse des Startvorgangs der RISC-V-Linux-Assembly

嵌入式Linux充电站
嵌入式Linux充电站nach vorne
2023-08-01 15:40:401440Durchsuche

Der Assembly-Startteil von RISC-V Linux ist relativ einfach und nicht zu kompliziert. Es gibt zwei Kernteile: die Erstellung und Umleitung von Seitentabellen. Die Erstellung der Seitentabelle ist in der Sprache C geschrieben. Heute analysieren wir zunächst den gesamten Assembly-Startprozess und analysieren dann die Umleitung.

Hinweis: Dieser Artikel basiert auf dem Linux5.10.111-Kernel

Assembly-Startprozess

Beginnen wir zunächst mit einer Gesamtanalyse der Funktionsweise der Assembly, und es gibt einen allgemeinen Rahmen.

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

Analyse des Startvorgangs der RISC-V-Linux-Assembly

ENTRY(_start_kernel)

Analyse des Startvorgangs der RISC-V-Linux-Assembly

From ENTRY(_start_kernel)Starten Sie vor dem Start einige Initialisierungen und die Hauptarbeit, bevor Sie die Seitentabelle erstellen: 🎜
  • Alle Interrupts ausschalten.
/* 关闭所有中断 */
    csrw CSR_IE, zero
    csrw CSR_IP, zero
  • Globalen Zeiger GP laden.
/* 加载全局指针gp */
.option push
.option norelax
    la gp, __global_pointer$
.option pop
  • FPU deaktivieren
/* 禁用 FPU 以检测内核空间中浮点的非法使用*/
    li t0, SR_FS
    csrc CSR_STATUS, t0
  • Mache den Mist raus Segment
/* 选择一个核启动 */
    la a3, hart_lottery
    li a2, 1
    amoadd.w a3, a2, (a3)
    bnez a3, .Lsecondary_start
  • Speichern Sie die Hart-ID und die DTB-Adresse
/* 清除bss */
    la a3, __bss_start
    la a4, __bss_stop
    ble a4, a3, clear_bss_done
  • Setzen Sie den SP-Zeiger
/* 保存hatr id和dtb地址,hart id保存到a0,dtb地址保存到a1 */
    mv s0, a0
    mv s1, a1
    la a2, boot_cpu_hartid
  • Nach Abschluss der oben genannten Arbeiten beginnt die Erstellung der temporären Seitentabelle. und springen Sie zur C-Funktion setup_vm, um die temporäre Seitentabelle zu erstellen C Funktion start_kernel, startet die Initialisierung des C-Sprachteils und führt den Assembly-Teil aus kann weiterlaufen. Nachdem setup_vm die Seitentabelle erstellt hat, beginnt es mit der Ausführung der Relocate-Umleitung. Diese Umleitung aktiviert hauptsächlich mmu. Die Zusammenstellung von Relocate wird unten analysiert.
  • relocate
    relocate-Weiterleitung, die MMU aktivieren soll. Der Vorgang zum Einschalten von MMU besteht darin, die Adresse und Berechtigungen der Seitentabelle der ersten Ebene in das
  • -Register zu schreiben. Dies wird als Einschalten von MMU angesehen.
    #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位:

    Analyse des Startvorgangs der RISC-V-Linux-Assembly低12位是偏移量,所以PAGE_SHIFT等于12,将early_pg_dir地址右移12位存到a2。根据satp寄存器定义:

    Analyse des Startvorgangs der RISC-V-Linux-Assembly

    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的指令不复杂,但要理解这个汇编启动的部分,还是需要一点基础和时间。另外,大多数人工作中基本用不上汇编,只有真正用上了理解才会比较深。希望本文能够帮助到有需要的人。

Das obige ist der detaillierte Inhalt vonAnalyse des Startvorgangs der RISC-V-Linux-Assembly. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:嵌入式Linux充电站. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen