Analyse du processus de démarrage de l'assemblage RISC-V Linux

Libérer: 2023-08-01 15:40:40
avant
1438 Les gens l'ont consulté

La partie démarrage de l'assemblage de RISC-V Linux est relativement simple et pas trop compliquée. Il y a deux parties principales : la création de tables de pages et la redirection. La création de tables de pages est écrite en langage C. Aujourd'hui, nous allons d'abord analyser la pièce d'assemblage. Nous vous amènerons d'abord à analyser le processus global de démarrage de l'assemblage, puis à analyser la redirection.

Remarque : cet article est basé sur le noyau Linux5.10.111

processus de démarrage de l'assembly

Tout d'abord, commençons par une analyse globale de ce que fait l'assembly, et il existe un cadre général.

Chemin : arch /riscv/kernel/head.S, l'entrée est ENTRY(_start_kernel)arch/riscv/kernel/head.S,入口是ENTRY(_start_kernel)

Analyse du processus de démarrage de l'assemblage RISC-V Linux

ENTRY(_start_kernel)

Analyse du processus de démarrage de l'assemblage RISC-V Linux

De ENTRY(_start_kernel)Démarrez une initialisation avant le démarrage et le travail principal avant d'établir la table des pages : 🎜"Désactivez toutes les interruptions"
  • /* 关闭所有中断 */
        csrw CSR_IE, zero
        csrw CSR_IP, zero
    Copier après la connexion
Effacer les bss segment
  • /* 加载全局指针gp */
    .option push
    .option norelax
        la gp, __global_pointer$
    .option pop
    Copier après la connexion
Enregistrez l'identifiant hart et l'adresse dtb
  • /* 禁用 FPU 以检测内核空间中浮点的非法使用*/
        li t0, SR_FS
        csrc CSR_STATUS, t0
    Copier après la connexion
Définissez le pointeur sp
  • /* 选择一个核启动 */
        la a3, hart_lottery
        li a2, 1
        amoadd.w a3, a2, (a3)
        bnez a3, .Lsecondary_start
    Copier après la connexion
Une fois le travail ci-dessus terminé, la création de la table de pages temporaire commencera, passez à la fonction C setup_vm pour créer la table de page temporaire
  • /* 清除bss */
        la a3, __bss_start
        la a4, __bss_stop
        ble a4, a3, clear_bss_done
    Copier après la connexion
Redirect
  • /* 保存hatr id和dtb地址,hart id保存到a0,dtb地址保存到a1 */
        mv s0, a0
        mv s1, a1
        la a2, boot_cpu_hartid
    Copier après la connexion
Définissez l'adresse du vecteur d'exception, rechargez l'environnement C
  •     la sp, init_thread_union + THREAD_SIZE
    Copier après la connexion
Enfin, passez à la fonction C start_kernel, démarre l'initialisation de la partie en langage C et la partie assembly est exécutée
  •     mv a0, s1
        call setup_vm // 跳转到C函数setup_vm,setup_vm会创建临时页表
    Copier après la connexion
    Code assembleur _start_kernel complet :
  • #ifdef CONFIG_MMU
        la a0, early_pg_dir
        call relocate	//重定向,实际就是开启MMU
    #endif
    Copier après la connexion
Une partie très importante de l'assembly est la création de la table des pages, qui détermine si le programme suivant peut continuer à courir. Une fois que setup_vm a créé la table de pages, il commencera à exécuter la redirection de relocalisation. Cette redirection active principalement mmu. L'assemblage de relocate est analysé ci-dessous.
  • relocate
  • redirection de relocalisation, qui consiste à activer mmu. L'opération d'activation de mmu consiste à écrire l'adresse et les autorisations de la table des pages de premier niveau dans le registre
  • Ceci est considéré comme activant mmu.
#ifdef CONFIG_MMU
    la a0, early_pg_dir //跳转到relocate前,先把第一级页表early_pg_dir的地址存入a0
    call relocate		//跳转到relocate,开启MMU
#endif
Copier après la connexion

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
Copier après la connexion
  • 将异常入口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
Copier après la connexion
  • 提前计算切换到early_pg_dir页表要写入satp的值

再进入relocate之前,就已经把early_pg_dir赋值给a0了,所以a0是early_pg_dir。srl是逻辑右移,mmu使用的是sv39,虚拟地址39位,物理地址56位:

Analyse du processus de démarrage de l'assemblage RISC-V Linux低12位是偏移量,所以PAGE_SHIFT等于12,将early_pg_dir地址右移12位存到a2。根据satp寄存器定义:

Analyse du processus de démarrage de l'assemblage RISC-V Linux

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
Copier après la connexion
  • 第一次开启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
Copier après la connexion

进入异常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
Copier après la connexion

总结

以上就是RISC-V Linux的汇编启动流程,虽说RISC-V的指令不复杂,但要理解这个汇编启动的部分,还是需要一点基础和时间。另外,大多数人工作中基本用不上汇编,只有真正用上了理解才会比较深。希望本文能够帮助到有需要的人。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:嵌入式Linux充电站
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!