RISC-V Linux»ã±àÆô¶¯Àú³ÌÆÊÎö
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_OFFSETÊÇkernelÈë¿ÚµØµã¶ÔÓ¦µÄÐéÄâµØµã£¬_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 mmu£¬0x0´ú±í²»¾ÙÐеصã·Ò룬¼´²»¿ªÆôMMU¡£ÕâÀïSTAP_MODEΪsv39£¬¼´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»ã±àÆô¶¯Àú³ÌÆÊÎöµÄÏêϸÄÚÈÝ£¬¸ü¶àÇë¹Ø×¢±¾ÍøÄÚÆäËüÏà¹ØÎÄÕ£¡