掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??

10年積累的成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有南昌縣免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
?? 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
上一篇文章中介紹了loongarch架構(gòu)中的地址翻譯模式及其配置方法,涉及到虛擬內(nèi)存系統(tǒng)中頁(yè)表相關(guān)的管理。本文中則介紹TLB相關(guān)的異常處理,并結(jié)合代碼進(jìn)行分析。因?yàn)閘oongarch架構(gòu)中采用的是一種軟件管理TLB的方法,所以其處理流程和軟件所需要進(jìn)行的管理操作與很多常見(jiàn)的架構(gòu)不同。
首先介紹TLB表項(xiàng)和頁(yè)表項(xiàng)的格式,作基本的了解。
下圖為loongarch中的頁(yè)表項(xiàng)格式:
下面為各位的說(shuō)明:
其中,大頁(yè)的頁(yè)表項(xiàng)和基本頁(yè)的頁(yè)表項(xiàng)在格式上的主要區(qū)別是H位和G位。并且基本頁(yè)的頁(yè)表項(xiàng)在末級(jí)頁(yè)表,而大頁(yè)的頁(yè)表項(xiàng)實(shí)際上是替代了原來(lái)的頁(yè)目錄項(xiàng)。
另外,對(duì)于基本頁(yè)的頁(yè)表項(xiàng),loongarch中每個(gè)頁(yè)表項(xiàng)存放了相鄰的一對(duì)奇偶相鄰頁(yè)表信息。如下圖:
下圖為loongarch中的TLB表項(xiàng)格式:
其中,每個(gè)TLB表項(xiàng)分為兩個(gè)部分:第一行為比較部分,下面兩行為物理轉(zhuǎn)換部分。
TLB表項(xiàng)的比較部分包括:
因?yàn)門LB和頁(yè)表雙頁(yè)存儲(chǔ)的特性,每個(gè)TLB表項(xiàng)中有兩個(gè)物理轉(zhuǎn)換信息。物理轉(zhuǎn)換信息中PPN即物理頁(yè)號(hào),其他和上文中頁(yè)表中的對(duì)應(yīng)。
類似于MIPS架構(gòu),loongarch中使用的是一種軟件管理TLB的方式。在大多數(shù)其他的架構(gòu)中,采用的都是通過(guò)硬件管理TLB的方式。軟件管理TLB帶來(lái)了更多的靈活性,但性能相對(duì)較差。
硬件管理TLB中,在忽略page fault的細(xì)節(jié)和cache的情況下,虛擬地址轉(zhuǎn)換到物理地址的過(guò)程如下圖:
其中,TLB miss后查找頁(yè)表的這個(gè)過(guò)程是由硬件自動(dòng)完成的,軟件只需處理后面產(chǎn)生的page fault。整個(gè)過(guò)程中最多產(chǎn)生一次page fault。
下圖則為軟件管理TLB方案中虛擬地址轉(zhuǎn)換到物理地址的過(guò)程:
具體解釋如下:
可以看到,TLB miss后查找頁(yè)表的這個(gè)過(guò)程需要軟件進(jìn)行處理,并且整個(gè)過(guò)程中最多能產(chǎn)生三次異常。
另外,硬件上會(huì)保證在TLB重填異常中不能再次產(chǎn)生TLB重填異常。
loongarch中TLB相關(guān)的異常有:
其中,TLB重填異常時(shí)需遍歷頁(yè)表進(jìn)行重填工作。TLB重填異常于一般的異常不同,其擁有獨(dú)立的異常入口、獨(dú)立的用于維護(hù)現(xiàn)場(chǎng)的控制狀態(tài)寄存器和一套獨(dú)立的TLB訪問(wèn)接口控制寄存器,并且因此TLB重填異??梢栽谄渌惓L幚磉^(guò)程中被觸發(fā)。而當(dāng)進(jìn)入TLB重填異常時(shí),硬件會(huì)自動(dòng)設(shè)置CSR.CRMD.DA=1和CSR.CRMD.PG=0,即進(jìn)入直接地址翻譯模式,從而避免在TLB重填異常中不能再次產(chǎn)生TLB重填異常。
而如load操作頁(yè)無(wú)效異常等異常,則需要完成類似于page fault的工作。
在介紹TLB相關(guān)異常的處理之前,先對(duì)loongarch中相關(guān)的指令進(jìn)行介紹。
具體案例可見(jiàn)后文相關(guān)代碼分析。
具體案例可見(jiàn)后文相關(guān)代碼分析。
下面結(jié)合linux源碼對(duì)TLB相關(guān)異常處理進(jìn)行分析。
linux中TLB相關(guān)異常和相關(guān)處理函數(shù)的對(duì)應(yīng)關(guān)系如下:
這里分析handle_tlb_refill、handle_tlb_load和handle_tlb_protect函數(shù)。其中handle_tlb_store和handle_tlb_modify實(shí)際上流程與handle_tlb_load基本一致,只是更新頁(yè)表項(xiàng)時(shí)更新的位不同。
TLB重填異常(handle_tlb_refill)觸發(fā)前后硬件中的處理與一般異常存在差異,主要是TLB重填異常相關(guān)有獨(dú)立的一套寄存器。但都會(huì)有相應(yīng)保存和恢復(fù)現(xiàn)場(chǎng)、跳轉(zhuǎn)和返回操作。值得注意的是,TLB重填異常中出錯(cuò)的地址保存在CSR.TLBRBADV寄存器,而一般異常出錯(cuò)的地址保存在CSR.BADV寄存器。
TLB重填異常的軟件處理過(guò)程如下:
代碼分析如下:
SYM_FUNC_START(handle_tlb_refill)
csrwr t0, LOONGARCH_CSR_TLBRSAVE // 將t0保存到CSR.TLBRSAVE寄存器
csrrd t0, LOONGARCH_CSR_PGD // 讀取pgd基址到t0
lddir t0, t0, 3 // 根據(jù)CSR.TLBRBADV中記錄的缺失虛擬地址,
// 訪問(wèn)3級(jí)頁(yè)表,讀取2級(jí)頁(yè)表基址到t0(pgd為3級(jí)頁(yè)表基址)
#if CONFIG_PGTABLE_LEVELS > 3
lddir t0, t0, 2 // 根據(jù)CSR.TLBRBADV中記錄的缺失虛擬地址,
// 訪問(wèn)2級(jí)頁(yè)表,讀取1級(jí)頁(yè)表基址到t0
#endif
#if CONFIG_PGTABLE_LEVELS > 2
lddir t0, t0, 1 // 根據(jù)CSR.TLBRBADV中記錄的缺失虛擬地址,
// 訪問(wèn)1級(jí)頁(yè)表,讀取末級(jí)頁(yè)表地址或大頁(yè)到t0
#endif
ldpte t0, 0 // 根據(jù)CSR.TLBRBADV中記錄的缺失虛擬地址,
// 訪問(wèn)末級(jí)頁(yè)表或大頁(yè),讀取偶數(shù)號(hào)頁(yè)表項(xiàng)或大頁(yè)到CSR.TLBELO0
ldpte t0, 1 // 根據(jù)CSR.TLBRBADV中記錄的缺失虛擬地址,
// 訪問(wèn)末級(jí)頁(yè)表或大頁(yè),讀取奇數(shù)號(hào)頁(yè)表項(xiàng)或大頁(yè)到CSR.TLBELO1
tlbfill // 根據(jù)CSR.TLBELO0、CSR.TLBELO1等寄存器中信息,
// 將頁(yè)表項(xiàng)填入TLB
csrrd t0, LOONGARCH_CSR_TLBRSAVE // 恢復(fù)t0
ertn // 從異常返回
SYM_FUNC_END(handle_tlb_refill)
load/取指操作頁(yè)無(wú)效異常觸發(fā)前后硬件中的處理與一般異常相同。
handle_tlb_load處理的過(guò)程如下:
代碼分析如下:
SYM_FUNC_START(handle_tlb_load)
// 將t0、t1、ra寫入CSR.SAVE0-CSR.SAVE3,暫存寄存器
csrwr t0, EXCEPTION_KS0
csrwr t1, EXCEPTION_KS1
csrwr ra, EXCEPTION_KS2
/*
* The vmalloc handling is not in the hotpath.
*/
// 如果CSR.BADV不小于0,則繼續(xù)執(zhí)行到vmalloc_done_load
// 將CSR.BADV和CSR.PGDL讀入t0和t1
// 否則跳轉(zhuǎn)到vmalloc_load將CSR.BADV和swapper_pg_dir讀入t0和t1
// 即CSR.BADV不小于0時(shí)使用低半部分內(nèi)核地址的pgd,
// 否則使用高半部分用戶地址的pgd
csrrd t0, LOONGARCH_CSR_BADV
bltz t0, vmalloc_load
csrrd t1, LOONGARCH_CSR_PGDL
vmalloc_done_load:
/* Get PGD offset in bytes */
// 根據(jù)t0中CSR.BADV地址和t1中pgd基址,遍歷頁(yè)表查找
bstrpick.d ra, t0, PTRS_PER_PGD_BITS + PGDIR_SHIFT - 1, PGDIR_SHIFT
alsl.d t1, ra, t1, 3
#if CONFIG_PGTABLE_LEVELS > 3
ld.d t1, t1, 0
bstrpick.d ra, t0, PTRS_PER_PUD_BITS + PUD_SHIFT - 1, PUD_SHIFT
alsl.d t1, ra, t1, 3
#endif
#if CONFIG_PGTABLE_LEVELS > 2
ld.d t1, t1, 0
bstrpick.d ra, t0, PTRS_PER_PMD_BITS + PMD_SHIFT - 1, PMD_SHIFT
alsl.d t1, ra, t1, 3
// 到這里t1中為1級(jí)頁(yè)表(pmd)地址
#endif
// 將1級(jí)頁(yè)表中第一個(gè)表項(xiàng)讀取到ra
ld.d ra, t1, 0
/*
* For huge tlb entries, pmde doesn't contain an address but
* instead contains the tlb pte. Check the PAGE_HUGE bit and
* see if we need to jump to huge tlb processing.
*/
// 如果ra中表項(xiàng)為大頁(yè),則跳轉(zhuǎn)到tlb_huge_update_load
rotri.d ra, ra, _PAGE_HUGE_SHIFT + 1
bltz ra, tlb_huge_update_load
rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1)
bstrpick.d t0, t0, PTRS_PER_PTE_BITS + PAGE_SHIFT - 1, PAGE_SHIFT
alsl.d t1, t0, ra, _PTE_T_LOG2
// 到這里t1中為CSR.BADV對(duì)應(yīng)末級(jí)頁(yè)表項(xiàng)地址
// 讀取頁(yè)表項(xiàng)到t0
#ifdef CONFIG_SMP
smp_pgtable_change_load:
ll.d t0, t1, 0 // smp中使用ll/sc原子指令對(duì)循環(huán)寫入
#else
ld.d t0, t1, 0
#endif
// 如果頁(yè)表項(xiàng)不存在,則跳轉(zhuǎn)到nopage_tlb_load調(diào)用缺頁(yè)處理函數(shù)
// 否則繼續(xù)向下執(zhí)行,寫入對(duì)應(yīng)有效的頁(yè)表項(xiàng)到TLB
andi ra, t0, _PAGE_PRESENT
beqz ra, nopage_tlb_load
// 設(shè)置有效位并更新頁(yè)表項(xiàng)
ori t0, t0, _PAGE_VALID
#ifdef CONFIG_SMP
sc.d t0, t1, 0
beqz t0, smp_pgtable_change_load // 寫入失敗時(shí)跳轉(zhuǎn)
#else
st.d t0, t1, 0
#endif
// 根據(jù)CSR.ASID和CSR.TLBEHI的信息查詢TLB,以便tlbwr指令寫入
// 如果命中則將其索引寫入CSR.TLBIDX,否則將CSR.TLBIDX.NE置為1
// 這里必然會(huì)命中
tlbsrch
// t0 = 偶數(shù)項(xiàng)頁(yè)表項(xiàng),t1 = 奇數(shù)項(xiàng)頁(yè)表項(xiàng)
bstrins.d t1, zero, 3, 3
ld.d t0, t1, 0
ld.d t1, t1, 8
// 寫入TLB相關(guān)寄存器
csrwr t0, LOONGARCH_CSR_TLBELO0
csrwr t1, LOONGARCH_CSR_TLBELO1
// 根據(jù)CSR.TLBELO0、CSR.TLBELO1、CSR.TBLIDX等相關(guān)寄存器信息,
// 將頁(yè)表項(xiàng)信息寫入TLB中CSR.TBLIDX.index對(duì)應(yīng)位置
tlbwr
// 恢復(fù)并返回
csrrd t0, EXCEPTION_KS0
csrrd t1, EXCEPTION_KS1
csrrd ra, EXCEPTION_KS2
ertn
#ifdef CONFIG_64BIT
vmalloc_load:
la.abs t1, swapper_pg_dir
b vmalloc_done_load
#endif
/* This is the entry point of a huge page. */
tlb_huge_update_load:
// 對(duì)于大頁(yè),異常處理的流程和上面基本頁(yè)表項(xiàng)的處理流程基本一致
// 只是填入TLB時(shí)會(huì)做一些額外的格式轉(zhuǎn)換處理等,這里不再贅述
...
nopage_tlb_load:
dbar 0
csrrd ra, EXCEPTION_KS2
la.abs t0, tlb_do_page_fault_0
jr t0
SYM_FUNC_END(handle_tlb_load)
頁(yè)不可讀/不可寫/特權(quán)不合規(guī)異常觸發(fā)前后硬件中的處理與一般異常相同。
handle_tlb_protect處理的過(guò)程實(shí)際上就是調(diào)用缺頁(yè)處理函數(shù),來(lái)填入頁(yè)表。
代碼分析如下:
SYM_FUNC_START(handle_tlb_protect)
// 保存寄存器
BACKUP_T0T1
SAVE_ALL
// 設(shè)置傳參
move a0, sp
move a1, zero
csrrd a2, LOONGARCH_CSR_BADV
REG_S a2, sp, PT_BVADDR
// 調(diào)用do_page_fault
la.abs t0, do_page_fault
jirl ra, t0, 0
// 恢復(fù)并返回
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_tlb_protect)
本文介紹了loongarch架構(gòu)中軟件管理TLB的機(jī)制、TLB重填異常和其他TLB相關(guān)的異常,以及相應(yīng)的異常處理和代碼分析。
軟件管理TLB機(jī)制、處理TLB相關(guān)異常,算是loongarch架構(gòu)中TLB相關(guān)軟件維護(hù)中較為特別的地方。下一篇文章將繼續(xù)介紹loongarch中其他的TLB維護(hù)和相關(guān)指令。
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
?? 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流