




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
1、SKYEYE 指令動態(tài)翻譯模擬 (DBCT) 實現(xiàn)介紹 v0.0teawater()轉(zhuǎn)載請標明來自 修改記錄: v0.02006-06-16 , v0.0 版本編寫完成。2006-05-27 ,文檔創(chuàng)建。目錄1. 寫在前面2. 概述3. 微指令3.1. 微指令的結(jié)構(gòu)和初始化3.2. 微指令中的變量3.3. 微指令中的函數(shù)調(diào)用3.4. 微指令中的異常處理3.5. 微指令中的跳轉(zhuǎn)3.6. 微指令分類介紹3.6.1. 概述3.6.2. arm2x86.c:op_init3.6.3. arm2x86_test.c:ar
2、m2x86_test_init3.6.4. arm2x86_shift.c:arm2x86_shift_init3.6.5. arm2x86_psr.c:arm2x86_psr_init3.6.6. arm2x86_movl.c:arm2x86_movl_init3.6.7. arm2x86_mul.c:arm2x86_mul_init3.6.8. arm2x86_mem.c:arm2x86_mem_init3.6.9. arm2x86_dp.c:arm2x86_dp_init3.6.10. arm2x86_coproc.c:arm2x86_coproc_init3.6.11. arm2x86
3、_other.c:arm2x86_other_init4. 翻譯塊 (TB)4.1. 概述4.2. tb.h:struct tb_s4.3. TB_TBT_SIZE 和 TB_TBP_SIZE4.4. tb.c:tb_memory_init4.5. tb.c:tb_insn_len_max_init5. 初始化函數(shù) arm2x86.c:arm2x86_init6. 翻譯執(zhí)行過程6.1. armemu.c:ARMul_Emulate32_dbct6.2. tb.c:tb_find6.4. tb.c:tb_translate6.5. tb.c:translate_word1. 寫在前面本文針對 s
4、kyeye-1.2-RC7-3 進行編寫。關(guān)于 skyeye 本身結(jié)構(gòu)的介紹可以參見 skyeye 學習筆記 (/forum /gshowthreaded.php?Cat=&Board=program&Number=522443&page=1& amp;view=collapsed&sb=5&o=all&fpart=) 。在下面為了方便介紹,都將 SKYEYE 指令動態(tài)翻譯模擬簡 稱為 DBCT 。DBCT 的實現(xiàn)并不算是很好的, 個人推薦對動態(tài)翻譯有興趣的朋友們讀一下 QEMU 的代碼。2. 概述DBCT 是在指令模擬思想上參照了 QEMU(h
5、ttp:/fabrice.bellard.free.fr/qemu/) ,但在實現(xiàn)上也 有不同,后面進 行每個部分介紹的時候,我會依次介紹到。DBCT 就是將若干條連續(xù)的被模擬指令組成一組(稱為一個翻譯塊 TB) ,將每條指令根據(jù)其行為直接翻譯成若干條微指令 (這里跟 QEMU 不同, QEMU 在翻譯過程中有中間碼存在 ), 每條微指令代表一種操作行為并由若干條本地指令組成, 最后就得到一組跟 TB 對應的本地 指令, 再在結(jié)尾增加返回指令, 對這組指令開始的地址進行函數(shù)調(diào)用, 就達到了指令模擬的 目的。然后對指令進行解碼分析, 根, SKYEYE 普通指令模擬方C 語言的函數(shù)等,然后編譯其
6、他我所了解指令模擬方式還有最常見的 直接讀入一條指令, 據(jù)需要進行其操作,然后再讀入下一條指令,重復剛才的工作 式就是這種類型。還有一種模擬方式是將要模擬的硬件行為翻譯成某種語言比如 執(zhí)行來進行指令模擬。3. 微指令3.1. 微指令的結(jié)構(gòu)和初始化在 arm2x86_init 函數(shù)中在 tb_insn_len_max_init 函數(shù)之前的被調(diào)用的函數(shù)都是 微指令初始化 函數(shù)。在 DBCT 代碼中,每條微指令都被封裝在定義在 arm2x86.h 的 op_table_t 結(jié)構(gòu)中,這個結(jié)構(gòu) 中的 op 是指向這個微指令的地址,而 len 則是這個微指令的長度 。每條微指令的的初始化 都是由一個名稱為
7、 get_op_xxx 形式的函數(shù)來完成的,這個函數(shù)會將微指令的地址返回,用 來設置在 op 中,而其的參數(shù)指針是用來設置 len 的,這些函數(shù)都會在微指令初始化函數(shù)中 被調(diào)用。在這個函數(shù)中有 2 個定義在 arm2x86.h 中的宏 OP_BEGIN 和 OP_END ,這 2 個宏都是 X86 指令。在這 2 個宏之間的代碼就是微指令的代碼。#define OP_BEGIN (f)_asm_ _volatile_(jmp .f_teawater_op_endnt.f_teawater_op_begin:nt)#define OP_END (f)_asm_ _volatile_ (.f_te
8、awater_op_end:ntmovl$.f_teawater_op_begin,%0ntmovl $.f_teawater_op_end,%1nt:=g(begin), =g(end); OP_BEGIN 先是一條跳轉(zhuǎn)指令, 跳轉(zhuǎn)到聲明在 OP_END 中的符號地址 .f_teawater_op_end , 這句的作用是跳過 OP_BEGIN 和 OP_END 中間的代碼, 防止他們被執(zhí)行, 我為什么沒有直 接使用普通的跳轉(zhuǎn)語句 goto 呢?因為如果使用了 goto 語句,編譯器就會知道微指令的代碼 沒被執(zhí)行, 就會將這部分代碼優(yōu)化掉, 而 使用跳轉(zhuǎn)指令編譯器無法判斷匯編語句的行為 ,所
9、 以不會優(yōu)化掉 2 個宏之間的微指令代碼。 然后是一條聲明符號的偽指令, 這個符號指向的地 址就是微指令開始的地址。OP_END 先是一條聲明符號的偽指令,這個符號的地址就是指向微指令結(jié)束的地址,OP_BEGIN 中的跳轉(zhuǎn)指令也是跳到了這個地址。然后跟著是兩條賦值指令,將 微指令開始 和結(jié)束的地址存到初始化函數(shù)開始聲明的begin 和 end 變量中, 這樣在函數(shù)中就取得微指令的開始和結(jié)束地址。從上面對 2 個宏的的介紹就可以基本清楚微指令的生成過程, 在取得了微指令的開始和結(jié)束 地址后,就可以通過計算取得其的長度最后返回。在 QEMU 的微指令的生成過程跟 DBCT 不同, QEMU 采用的
10、方式是每個微指令都在一個函 數(shù)中,編譯完成后,通過特定的程序?qū)⑽⒅噶畹拈_始地址和長度取出。3.2. 微指令中的變量在微指令的代碼中,都沒有使用動態(tài)局部變量,也就是棧中的地址,而直接使用了寄存器, 當然在寄存器不夠的情況下也使用了全局變量 。這個使用的方法是參考的 QEMU 中微指令 對變量的使用方法。個人認為這樣做因為若干條微指令才能組成一條指令(尤其是在 ARM 這種單條指令實現(xiàn)若干功能的體系結(jié)構(gòu)更是如此 ),這些微指令之間要相互傳遞計算等得到的值,如果通過棧傳 遞也可以,但是實現(xiàn)相對復雜,而且頻繁的內(nèi)存操作速度也會受到影響。對這些 寄存器的聲明在 arm2x86_self.h 這個單獨的頭
11、文件中, 因為對寄存器的聲明有時候會 影響代碼編譯,所以只在 DBCT 相關(guān)的文件中包含了這個頭文件。ebp寄存器聲明為 ARMul_State結(jié)構(gòu)的指針st,其將在微指令的代碼開始之前指向state也就是存儲了 SKYEYE 模擬 CPU 所有信息的結(jié)構(gòu) ,這樣微指令就可以方便的訪問這個結(jié)構(gòu)。ebx、esi和edi聲明為uint32_t類型的變量T0,T1和T2,這幾個變量在微指令中可以靈活使用。 注意因為這幾個寄存器保存了棧指針等信息, 所以在運行微指令以前需要進行保存, 具體在 介紹 DBCT 運行的時候會進行介紹。其他eax等寄存器因為是在 GCC中是局部寄存器,不能作為全局變量聲明,
12、所以不能作為 微指令中的變量使用。3.3. 微指令中的函數(shù)調(diào)用在微指令中有時候需要對函數(shù)進行調(diào)用, 因為微指令使用方法的原因, 這里不能進行普通的 函數(shù)調(diào)用,而需要特殊處理。下面就以 arm2x86.c:get_op_begin 中對 tea_begin 函數(shù)的調(diào)用為例子進行介紹。這里首先是對esp減Oxc也就是在棧中分配 Oxc的空間,然后將ebp也就是指向ARMul_State結(jié)構(gòu)的指針push到棧中,這2條匯編指令是將 st作為參數(shù)傳遞給被調(diào)用函數(shù)tea_begin,前面在棧中分配 0xc 的空間是要保證傳遞參數(shù)的 0x10 對齊, 0xc 加 32位的長度就是 0x10。 沒有存儲使用
13、的ebp、ebx、esi和edi,因為這幾個全局寄存器的值是由被調(diào)用函數(shù)進行保存 的,如果調(diào)用函數(shù)修改了這幾個寄存器的值就進行保存, 在函數(shù)返回時恢復, 如果沒有就使 用就不進行保存和恢復。然后是將tea_begin的地址賦值給T2,然后再對其進行調(diào)用,這樣做的目的是進行直接地址 調(diào)用。因為一般的函數(shù)調(diào)用都是直接地址調(diào)用, 所以調(diào)用跟當前這個調(diào)用指令的地址是相關(guān) 的,但是微指令會被拷貝到 TB 上的某地址執(zhí)行,調(diào)用指令的地址發(fā)生了變化,所以如果進 行相對地址調(diào)用, 肯定要產(chǎn)生錯誤, 所以這里都使用直接地址調(diào)用。 在某些函數(shù)中為了適應 CYGWIN 使用函數(shù)指針進行調(diào)用也是出于同樣的目的。最后是
14、取得返回值,一般一個32位數(shù)的返回值都是放在寄存器eax中,如果有需要就將其存入 T0 等寄存器變量然后使用。3.4. 微指令中的異常處理模擬指令一般來說都模擬異常處理, DBCT 也不例外。DBCT 的做法是當有異常處理的時候,設置 st-trap 或者 state-trap 為異常的類型 (定義在 arm2x86.h,TRAP_XXX 的都是),然后根據(jù)情況調(diào)用 X86匯編指令ret返回到正常模式,然 后在非 DBCT 運行模式中進行實際的處理。因為這種處理減少了微指令中實現(xiàn)的難度,所以類似 TRAP_SETS_R15 、 TRAP_SET_CPSR 以及 TRAP_SET_R15 等幾個
15、非異常處理 也采用了同一種處理方式。3.5. 微指令中的跳轉(zhuǎn)這里的跳轉(zhuǎn)不是指模擬的被模擬指令的跳轉(zhuǎn), 而是指微指令根據(jù)需要跳轉(zhuǎn)指定的長度。 比如 某條被模擬指令有 condition判斷,其中就會需要這種跳轉(zhuǎn),當condition和PSR中的值不符合的時候, 就需要跳過當前指令被翻譯成的微指令代碼, 這個時候就需要跳轉(zhuǎn)過指令的長 度。在 DBCT 中的做法是先寫一條類似_asm_ _volatile_ (jmp 0xffffffff) ;的指令, 一般來說后 4 個字節(jié)就是跳轉(zhuǎn)長度,在翻譯指令的時候?qū)⒁D(zhuǎn)的長度寫入就可以。在后面介紹 指令翻譯過程 的時候,還會對微指令跳轉(zhuǎn)的使用再作詳細介紹。
16、3.6. 微指令分類介紹3.6.1. 概述因為規(guī)劃設計的問題, 微指令的分類有點亂, 所以就以初始化函數(shù)為分類基礎進行分類介紹。3.6.2. arm2x86.c op_init這里初始化的最重要的 2 個微指令是 op_begin 和 op_begin_test_T0 ,這 2 個指令都是在翻譯ARM 指令的時候放在每條指令最開始的部分的。op_begin 是被翻譯的 ARM 指令的 condition 是 AL 或者 NV 也就是不進行條件判斷時候使用的微指令。這里先調(diào)用了函數(shù)arm2x86.c:tea_begin ,而這個函數(shù)調(diào)用了arm2x86.c: tea_check_out,這個函數(shù)
17、類似普通指令執(zhí)行模式中指令開始執(zhí)行時候作的操作一 樣, 檢查是否需要單步返回,檢查是否有硬件中斷發(fā)生進行異常處理,檢查當前 TB 是否已 經(jīng)標記為臟 (如果當前 TB 中執(zhí)行的微指令寫了當前 TB 相關(guān)的內(nèi)存,就會有這樣的情況發(fā) 生,這時返回到普通模式重新執(zhí)行,就會自動對這個 TB 進行重新翻譯 ),最后一步執(zhí)行 armio.c:io_do_cycle 調(diào)用全部虛擬設備執(zhí)行。 tea_begin 函數(shù)返回以后會判斷返回值,如果為 真就返回。op_begin_test_T0 是被翻譯的 ARM 指令需要條件判斷時候使用的微指令 。在這條指令運行 以前, 被翻譯指令的 condition 已經(jīng)被存
18、到 T0 中 ,這里首先以 st 和 T0 為參數(shù)調(diào)用 arm2x86.c:tea_begin_test,這個函數(shù)也是調(diào)用 tea_check_out檢查是否有異常等需要從 DBCT 模式返回普通執(zhí)行模式, 如果沒有則調(diào)用 arm2x86_psr.h:gen_op_condition 判斷當前被翻譯指 令是否可以執(zhí)行。tea_begin_test返回后,先判斷是否有異常需要返回普通執(zhí)行模式;然后判斷是否這條指令是否因為 condition 而不被執(zhí)行,如果是則就執(zhí)行一條 jmp 指令,也就是 前面介紹過的微指令跳轉(zhuǎn)。其他幾個微指令比較簡單不作詳細介紹。3.6.3. arm2x86_test.c
19、:arm2x86_test_init這里初始化了幾個簡單的測試情況然后進行一些處理的微指令。3.6.4. arm2x86_shift.c:arm2x86_shift_init這里初始化的是各種 移位微指令 。 這里移位的長度如果是變量的處理起來比較簡單,前面在別的微指令中存到某個寄存器變 量,然后在這條微指令中直接操作就可以。如果移位的長度是立即數(shù),則處理辦法有點類似前面的微指令跳轉(zhuǎn)。先用一條類似“T1 = T1<< 31; 的語”句,這樣其最后一個值是一個 8位的移位長度 ,然后在實際翻譯的過程中 替換成實際翻譯的立即數(shù)就可以了。3.6.5. arm2x86_psr.c:arm2
20、x86_psr_init這里初始化的是跟 ARM的狀態(tài)寄存器PSR(包括CPSR和SPSR)相關(guān)的微指令。3.6.6. arm2x86_movl.c:arm2x86_movl_init這里 初始化是用來對某模擬寄存器或者某寄存器變量等賦值的微指令。其中的立即數(shù)賦值也比較類似前面的微指令跳轉(zhuǎn) ,先用類似 “T2 = ULONG_MAX”; 的語句, 這樣其最后一個值就是 32 位長度的立即數(shù) ULONG_MAX ,然后在實際翻譯的過程中替換成實際翻譯的立即數(shù)就可以了。3.6.7. arm2x86_mul.c:arm2x86_mul_init這里 初始化是用來對乘法指令進行模擬的微指令 。3.6.
21、8. arm2x86_mem.c:arm2x86_mem_init這里 初始化是用來對內(nèi)存操作進行模擬的微指令 。這里對內(nèi)存的操作采用的辦法是調(diào)用 SKYEYE 原有的內(nèi)存操作函數(shù),直接取得返回值,然 后進行各種操作。這里這么做而不是直接訪問相應的內(nèi)存地址,因為有 MMU 的時候,需 要先通過 MMU 中的 TLB 和頁表等轉(zhuǎn)換地址, 而且還有地址是 IO 地址, 所以直接調(diào)用內(nèi)存 操作函數(shù)是比較簡單的實現(xiàn)方法。3.6.9. arm2x86_dp.c:arm2x86_dp_init這里初始化的是對 ARM 中 DP 指令 進行模擬的微指令。3.6.10. arm2x86_coproc.c:ar
22、m2x86_coproc_init這里初始化的是對 ARM 中 協(xié)處理器指令 進行模擬的微指令。3.6.11. arm2x86_other.c:arm2x86_other_init這里對所有 其他微指令 進行初始化。4. 翻譯塊(TB)4.1.概述在 DBCT 中,將 tb.h:TB_LEN 長度的被模擬指令翻譯成一系列微指令 (這一系列 微指令的長 度最大值為 tb.h:TB_INSN_LEN_MAX ,由 tb.c:tb_insn_len_max_init 取得 ) ,這一系列微指令(存儲這些微指令的內(nèi)存稱為 TBP)以及地址信息等其他信息封裝在一起稱為一個TB。在DBCT 初始化的時候,
23、 會根據(jù)配置文件以及實際情況對 TB 進行初始化, 介紹 tb_memory_init 的時候會詳細進行介紹。4.2. tb.h:struct tb_s這個結(jié)構(gòu)是 TB 的核心結(jié)構(gòu),每個 TB 塊都將對應一個 tb_s 結(jié)構(gòu)。 下面對其的每個成員變量進行介紹:struct list_headlist;當使用第二種方法使用 TB 的時候,這個 list 將所有使用過的 TB 全部用 tb.c:tbp_dynamic_list 以及這個 list 結(jié)構(gòu)組成的鏈表連接起來,在每次使用某個 TB 的時候,都將其先從鏈表中刪 除,然后連接到鏈表的最后。當對某地址進行執(zhí)行 ,而所有 TB 都不是這個地址相
24、關(guān)的,并 且沒有未使用過的 TB ,需要從現(xiàn)有 TB 中選擇一個 TB 使用的時候,就會 使用鏈表第一個 TB 。這樣做的好處是,最不常用的已翻譯過的 TB 肯定是在鏈表第一個,選擇其影響會最 小,提高了效率。在后面介紹翻譯執(zhí)行的時候會進行更具體的介紹。int ted;這個變量為 0 表明這個 TB 中數(shù)據(jù)是沒有翻譯過的,為 1 表明其中數(shù)據(jù)是翻譯過的。在需要 標記某個TB為臟的時候,就可以通過設置ted為0來實現(xiàn)。uint8_t*insn_addrTB_LEN / sizeof(uint8_t *);在翻譯過程中, 將把 每條指令對應的微指令地址 都存儲到這個數(shù)組中, 這樣在執(zhí)行已經(jīng)翻譯 過
25、的 TB 的時候,可以直接取得對應地址的微指令地址開始執(zhí)行。原來的 DBCT 中也采用過不存儲微指令地址,在執(zhí)行的時候再重新翻譯取得地址的方法, 最多是將取得后的地址存儲起來, 后來考慮即使將所有地址都存起來也不 會使用很多內(nèi)存, 所以就用了所有翻譯過的地址都存起來的方法。uint8_t*tbp;這個成員變量指向 當前 TB 存儲微指令的內(nèi)存 ,顯然這里指向內(nèi)存塊的大小為 TB_LEN / sizeof (ARMword) * TB_INSN_LEN_MAX 。ARMwordaddr;這個成員變量是 當前 TB 對應的被模擬指令的地址 。ARMwordtran_addr;在 TB 翻譯過程中,
26、并不是一次將整個 TB 范圍內(nèi)的被模擬指令都翻譯成微指令,而是每次 翻譯到一個 必定發(fā)生返回的指令 ,并且實際要取得的微指令地址(注意翻 譯是從 TB 開始的地址開始的 ) 也已經(jīng)取得, 就不再繼續(xù)進行翻譯, 等下次請求一個地址比翻譯到的地址大的 時候,就繼續(xù)對 TB 進行翻譯。這個成員變量 tran_addr 記錄的就是翻譯到的指令地址的下一個指令的地址,也就是如果繼 續(xù)翻譯的地址。uint8_t *tbp_now;該成員變量指向當前可以寫入微指令的地址, 在 tbt-ted 為 0 也就是這個 TB 從 tbt-addr 開 始翻譯的時候初始化 為 tbt-tbp ,每增加一個微指令都順序
27、增加,并且在向上面 tran_addr 提到的那種繼續(xù)翻譯的時候,可以繼續(xù)使用。ARMword last_addr;uint8_t *last_tbp;這2個成員在指令翻譯的時候使用。last_addr存儲這個TB上次被使用時候的地址,而last_tbp 就是對應的微指令地址。這樣如果下次還使用這個TB的這個地址last_addr,就可以快速取得微指令地址last_tbp,提高執(zhí)行速度。ARMwordret_addr;介紹 tran_addr 的時候,已經(jīng)提到了只翻譯到必定發(fā)生返回的指令就不再繼續(xù)翻譯,但是這 里還有一種情況需要考慮到,就是 DBCT 在翻譯當前 TB 范圍內(nèi)的 被模擬指令跳轉(zhuǎn)
28、的時候 , 都是將這個跳轉(zhuǎn)指令翻譯為微指令跳轉(zhuǎn)指令 ,而不是通常的設置 PC 寄存器然后返回的 方 式,這樣的跳轉(zhuǎn)如果是向后跳轉(zhuǎn),并且超過了翻譯結(jié)束的地址肯定是不行的。如何防止提前翻譯結(jié)束?在翻譯開始的時候設置ret_addr為0,一旦有TB內(nèi)跳轉(zhuǎn)出現(xiàn),并且這個地址的值比ret_addr大,就設置ret_addr為這個地址。在翻譯完一條指令并確定翻譯 也許可以結(jié)束的時候,對ret_addr進行檢查,只有在 ret_addr小于下一條將翻譯的指令地址的時候,翻譯才結(jié)束。4.3. TB_TBT_SIZE 和 TB_TBP_SIZE在 tb.c 中有 TB_TBT_SIZE 和 TB_TBP_SIZ
29、E , TB 的初始化就要根據(jù)其的值來進行,他們的 定義為:#define TB_TBT_SIZEskyeye_config.tb_tbt_size#define TB_TBP_SIZEskyeye_config.tb_tbp_sizeTB_TBT_SIZE是DBCT中所有TB的條目也就是tb_t結(jié)構(gòu)所占空間。如果設置為0則就在 使用的時候分配在被模擬內(nèi)存結(jié)構(gòu) armmem.h:mem_state_t-tbt 上,也就是只要運行某塊內(nèi) 存,就分配其的 tb_t 結(jié)構(gòu)。 如果設置為非 0 就使用 tb.c:tbt_table 和 tb.c:tbt_table_size 的內(nèi)存 進行動態(tài)分配。TB
30、_TBP_SIZE 是 TB 中實際儲存微指令的內(nèi)存所占空間 。如果設置為 0則就在使用的時候 分配在被模擬內(nèi)存結(jié)構(gòu) armmem.h:mem_state_t-tbp 上,也就是只要運行某塊內(nèi)存,就分配 其的tbp。如果設置為非 0會同時標記tbp_dynamic為1表明是TBP動態(tài)分配,并且 TBP 所用的內(nèi)存使用 tb.c:tbp_begin、tb.c:tbp_now 和 tbp_now_size 進行動態(tài)分配。skyeye_config.tb_tbt_size 和 skyeye_config.tb_tbp_size 是從配置文件中讀出的值, 他們的初始 化也就是 設置默認值在 skyey
31、e_options.c:skyeye_option_init 函數(shù)中。在這里我們可以看到 config-tb_tbt_size 也就是 TB_TBT_SIZE 初始化為 0,因為 tb_t 結(jié)構(gòu)占用空間不 大; config-tb_tbp_size 也就是 TB_TBP_SIZE 初始化為 TB_TBP_DEFAULT(1024 * 1024 * 64),這里沒有也初始化因為每條被模擬的 ARM 指令都包含若干條微指令,這樣跟 一個 TB 存儲 的指令對應的微指令存儲空間會比較大 ,甚至有超過 32 位尋址 空間大小的情況, 所以這里 一般不設置為 0。注意 TB_TBT_SIZE 和 TB_
32、TBP_SIZE 并不是從配置文件中讀出后直接使用,而是在 tb_memory_init 進行過初始化后才使用,和其 相關(guān)的幾個變量也是在 tb_memory_init 中進行的初始化。4.4. tb.c:tb_memory_init這個函數(shù)用來對 DBCT 中的 TB 進行初始化。下面介紹執(zhí)行過程: 第一步,先判斷 TB_TBT_SIZE 是否為 0,如果不為 0,就會執(zhí)行一部分針對 TB_TBT_SIZE 的代碼。注意這里我犯了一個比較大的錯誤,其中的結(jié)構(gòu)應該使用tb_t,而我這里全部錯誤的使用了 tb_cache_t,而這個tb_cache_t也是一個不再需要的東西,早應該從代碼中去掉,
33、下面的 介紹全都假定成tb_cache_t已經(jīng)被換成了 tb_t。先對TB_TBT_SIZE進行基本的處理 和檢查,然后取得所有被模擬內(nèi)存一共需要 tb_t 所 占的空間,其跟 TB_TBT_SIZE 進行比 較。如果TB_TBT_SIZE大于等于這個值,則表明 DBCT不需要動態(tài)分配tb_t來節(jié)省空間, 就設置 TB_TBT_SIZE 為 0,使用固定分配的形勢。如果 TB_TBT_SIZE 小于這個值,則初 始化存儲 tb_t 的空間 tbt_table 和 tb_t 的數(shù) 量 tbt_table_size 。這樣 TB_TBT_SIZE 就初始化完成,同時還對 tbt_table 和 t
34、bt_table_size 進行了設置。第二步,如果 TB_TBP_SIZE 為非 0,則對其進行基本的處理和檢查。第三步,再次判斷 TB_TBT_SIZE 是否為 0,然后對 TB_TBP_SIZE 進行處理。如果TB_TBT_SIZE不為0,首先取得這個長度的 tb_t結(jié)構(gòu)組需要的tbp的長度tmp_u64, 跟 TB_TBP_SIZE 進行比較。如果 TB_TBP_SIZE 大于 tmp_u64 或者 TB_TBP_SIZE 為 0, 則 TB_TBP_SIZE 設置為這個值,這么作因為在 TB_TBT_SIZE 動態(tài)分配后, TB 無法對大 于其管理范圍的微指令內(nèi)存 TBP 進行管理,
35、所以進行這個設置。如果 TB_TBP_SIZE 小于 tmp_u64,則設置 tb.c:tbp_dynamic為1,也就是設置 DBCT中TBP為動態(tài)分配。如果 TB_TBT_SIZE 為 0,將判斷 TB_TBP_SIZE 是否為 0。如果為 0 很顯然不需要再作任何 初始化工作, tbp_dynamic 使用默認值 0,全部在 DBCT 運行時根據(jù)需要分配在 mem_state_t-tbt 和 mem_state_t-tbp 上就可以。如 果不為 0 則先取得全部被模擬內(nèi)存需要 的TBP的長度tmp_u64,然后跟 TB_TBP_SIZE進行比較。如果 TB_TBP_SIZE大于等于 tm
36、p_u64,則表明TBP已經(jīng)不需要動態(tài)分配,就設置 TB_TBP_SIZE為0。如果TB_TBP_SIZE 小于tmp_u64,則表明需要動態(tài)分配,設置tbp_dynamic為1。這樣 TB_TBP_SIZE 就初始化完成,同時也根據(jù)需要對 tbp_dynamic 進行了設置。第四步,這時 TB_TBP_SIZE 的值已經(jīng)得到了確定,這里就是給 tbp_begin 分配空間,注意 這里用 mmap 分配內(nèi)存的時候設置了權(quán)限為可運行 PROT_EXEC 。然后對 tbp_now_size 和 tbp_now 也進行了初始化。這樣用來進行 TBP 動態(tài)分配的 tbp_begin、tbp_now_s
37、ize 和 tbp_now 進行了初始化??偨Y(jié)一下, DBCT 中用來維護 TBT 和 TBP 動態(tài)分配的幾個變量用的有點繁瑣了。4.5. tb.c:tb_insn_len_max_init 這個函數(shù)用來對 tb.c:tb_insn_len_max 也就是 TB_INSN_LEN_MAX 進行了初始化。 做法是將所有被翻譯指令被翻譯成的微指令長度都取得,然后進行比較,將最長的設置為 tb_insn_len_max 。5. 初始化函數(shù) arm2x86.c:arm2x86_init這個函數(shù)是 DBCT 的初始化函數(shù),其在函數(shù) arminit.c:ARMul_Reset 中被調(diào)用。 這個函數(shù)先會調(diào)用
38、前面介紹過的幾個微指令初始化函數(shù),然后是函數(shù) tb_insn_len_max_init , 最后是函數(shù) tb_memory_init 。6. 翻譯執(zhí)行過程6.1.armemu.c:ARMul_Emulate32_dbct這是 整個 DBCT 翻譯執(zhí)行的核心函數(shù) ,類似普通指令執(zhí)行方式的 ARMul_Emulate32 函數(shù), 也是在 arminit.c:ARMul_DoProg 和 arminit.c:ARMul_DoInstr 被調(diào)用。下面介紹執(zhí)行過程: 第一步,給 R15 寄存器也就是 PC 寄存器的值增加一個指令長度 INSN_SIZE ,這是因為 ARM 的多級流水線PC寄存器對應用是
39、非透明的,而在這個函數(shù)外面的函數(shù)都將R15當作當前PC值,所以在開始執(zhí)行前先對R15寄存器進行設置。第二步,設置 state-trap 為 0。第三步,調(diào)用函數(shù)tb.c:tb_find,在這個函數(shù)中 根據(jù)參數(shù)提供的PC寄存器值,進行全部的分 配TB以及指令翻譯的工作,最后將跟PC對應的微指令 地址返回。如果返回NULL則表示執(zhí)行失敗,設置 state-trap為TRAP_INSN_ABORT 也就是取指異常,跳轉(zhuǎn)到后面對 state-trap 進行處理的部分。第四步, 對將在微指令中作為變量的寄存器進行保存 ,保存的原因前面介紹過, 因為這幾個 寄存器的值都是被調(diào)用函數(shù)來保存,所以在這里進行保
40、存。調(diào)用取得的指向微指令內(nèi)存的指針gen_func。返回后恢復幾個寄存器的值。第五步,在介紹微指令的時候, 介紹過異常等特殊情況, 都是先設置state-trap然后就返回, 而這里就是實際對 異常等進行處理 的地 方。這部分代碼比較清晰,就是根據(jù) state-trap 進 行不同的處理,不作詳細介紹。第六步,判斷是否還繼續(xù)執(zhí)行 ,或者函數(shù)返回。如果繼續(xù)執(zhí)行就返回到第二步。第七步,state-Reg15減INSN_SIZE,恢復PC指向當前程序執(zhí)行的地址,然后返回。6.2. tb.c:tb_find在這個函數(shù)中 根據(jù)參數(shù)提供的 PC 寄存器值,進行全部的分配 TB 以及指令翻譯的工作 , 最
41、后將跟 PC 對應的微指令地址返回 。下面介紹執(zhí)行過程:第一步,調(diào)用 armmmu.c:mmu_v2p_dbct 函數(shù)通過 SKYEYE 的 MMU 功能 取得跟執(zhí)行地址 ADDR對應的被模擬物理地址addr,如果失敗則函數(shù)出錯返回。然后通過TB_ALIGN取得跟TB_LEN長度對齊的地址 align_addr,這個地址就是 addr對應TB的地址。第二步, 檢查 align_addr 是否和 靜態(tài)局部變量 save_align_addr 相同, 如果相同表明前面已經(jīng) 對這個物理地址的 TB 進行過請求,已經(jīng)取 得了翻譯前需要的各種指針,都存在靜態(tài)局部 變量中, 所以跳過分配 TB 的代碼直接
42、執(zhí)行指令翻譯的代碼。 注意 save_align_addr 的初始值 為 0x1 是為了保證不跟任何地址一樣。第三步,這里開始的就是對 TB 進行分配的代碼, 首先判斷 tbt_table_size 是否為 0 來確定 tb_t 是否是動態(tài)分配的。第四步,如果是動態(tài)分配,就會以哈希計算的方法從 tbt_table 中取出跟 align_addr 對應地址 的 tb_t。比較tbt-addr和align_addr,如果tbt-addr跟align_addr不同表明其先前是其他地址的TB,就會進行一些清除過去記錄的工作,設置 tbt-ted 為 0,設置 tbt-addr 為 align_addr
43、。然后就是取得tbt-tbp也就是TBP。如果tbt-tbp為NULL,則表明這個 TB中的TBP沒有 分配或者已經(jīng)被 其他 TB 使用,這時候需要調(diào)用 tb.c:tb_get_tbp 進行 TBP 的分配。如果 tbt-tbp 不為 NULL ,則 TBP 已經(jīng)分配過, 則按照前面在介紹 tbt-list 那樣,先將其從 tbp_dynamic_list 鏈表中刪除掉。第五步, 如果不是動態(tài)分配, 首先通過函數(shù) tb.c:tb_get_mbp 取得 align_addr 對應模擬內(nèi)存的 mem_bank_t 結(jié)構(gòu)指針 mbp。檢查結(jié)構(gòu)中的 state-mem.tbtbank_num 是否為空
44、,如果為空表明 tbt 和 tbp 未分配相應的空 間,如果 tbp_dynamic為0表明是靜態(tài)分配 TBP,則將先給 state-mem.tbpbank_num分配 空間,然后給 state-mem.tbtbank_num 分配空間。分配好空間后設置 TB 結(jié)構(gòu)。在取得 TB 結(jié)構(gòu)后檢查 tbt-tbp 也就是 TBP 是否為空。如果為空就根據(jù) tbp_dynamic 對其進 行設置,動態(tài)分配跟前面一樣使用 tb.c:tb_get_tbp 函數(shù),靜態(tài)從 state-mem.tbpbank_num 中取得。如果不為空也跟前面一樣判斷 tbp_dynamic 根據(jù)情況將 TB 結(jié)構(gòu)從列表中刪除
45、?,F(xiàn)在, TB 結(jié)構(gòu)和其中的 TBP 都已經(jīng)取得。第六步,用取得的 TB 進行一些設置。設置 state-tb_now 為剛?cè)〉玫?TB 結(jié)構(gòu),其的作用是微指令在運行的時候可以訪問當前運行 的TB,比如在標記 TB為臟之后,微指令可以馬上判斷出來然后退出。設置為 save_align_addr為 align_addr,目 的在第二步介紹過。如果 tbp_dynamic 為真表明是動態(tài) TBP 分配,將 TB 結(jié)構(gòu)增加到 tbp_dynamic_list 鏈表的最后面,這么作的目的在介紹 tbt-list 已經(jīng)介紹過。第七步,現(xiàn)在開始的就是對被模擬指令進行翻譯的代碼。 先判斷 tbt-ted 的
46、值來確定這個 TB 結(jié)構(gòu)是否被翻譯過。第八步,如果這個 TB 結(jié)構(gòu) 已經(jīng)翻譯過 。先檢查 tbt-last_addr 是否跟 addr 相同,如果相同就返回tbt-last_tbp 。這里在前面介紹tbt-last_addr 和 tbt-last_tbp 的已經(jīng)介紹過了。判斷需要翻譯的物理地址 addr 是否大于等于 tbt-tran_addr ,這個 tbt-tran_addr 在前面也介 紹 過。如果 addr 小于 tbt-tran_addr 則表明 TB 中現(xiàn)有微指令代碼已經(jīng)可以滿足 addr 的需要,直接 從 tbt-insn_addr 取出跟 addr 對應的 TBP 地址作為返回
47、值設置到 ret 就可以。如果 addr 大于等于 tbt-tran_addr 則表明需要繼續(xù)翻譯, 首先取得跟 tbt-tran_addr 地址對應 的 在被模擬內(nèi)存塊中指針real_begin_addr ,以及和 addr 對應的在被模擬內(nèi)存塊中指針real_addr。然后就調(diào)用tb.c:tb_translate從給定的real_begin_addr開始的內(nèi)存進行翻譯。最后 取得跟 addr 對應的微指令地址設置到 ret。第九步,如果這個 TB 結(jié)構(gòu) 還沒有翻譯過 ,就需要重新翻譯。也是首先取得跟tbt-tran_addr地址對應的在被模擬內(nèi)存塊中指針real_begin_addr,以及
48、和addr對應的在被模擬內(nèi)存塊中指針 real_addr。然后初始化tbt-tran_addr為align_addr,初始化tbt-tbp_now為tbp,這兩個成員變量在前面介紹過,這里就不再介紹。調(diào)用tb.c:tb_translate從給定的real_begin_addr開始的內(nèi)存進行翻譯。最后取得跟addr對應的微指令地址設置到ret。并且設置tbt-ted為1表明 這個TB已經(jīng)被翻譯過。現(xiàn)在返回值ret,也就是跟ADDR對應的微指令地址已經(jīng)取得。第十步,將 addr 和 ret 都設置到 tbt-last_addr 和 tbt-last_tbp 上,將 ret 返回。6.3. tb.c: tb_get_tbp這個函數(shù)用來對 TBP 進行動態(tài)分配。下面介紹執(zhí)行過程:第一步,判斷 tbp_now
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 創(chuàng)意圖像合成Photoshop試題及答案
- 重癥肺炎患兒的護理
- 人教版-七年級英語詞匯擴展計劃
- 城市公共設施節(jié)能工作職責概述
- 2025七年級下冊數(shù)學復習計劃
- 新能源企業(yè)工會代表選舉合規(guī)性審查與法律保障協(xié)議
- 海外金屬礦產(chǎn)樣本篩選與設備租賃合作協(xié)議
- 電商平臺倉儲配送與客戶服務提升合同
- 在線教育平臺用戶增長股權(quán)投資協(xié)議
- 豪華私人飛機機組人員意外傷害及醫(yī)療救助協(xié)議
- 2025年遼寧省盤錦市中考數(shù)學二模試卷
- 完整版新修訂《厲行節(jié)約反對浪費條例》(課件)
- 貴州國企招聘2025貴州省水利投資(集團)有限責任公司招聘84人筆試參考題庫附帶答案詳解
- 【8生 會考】2022-2024年安徽省初中(八年級)中考初二會考生物試卷(3年真題)
- 2024年河北承德辰飛供電服務有限公司招聘真題
- 滬教版八年級化學(下冊)期末試卷及答案
- DL-T-1878-2018燃煤電廠儲煤場盤點導則
- 小小科學家《物理》模擬試卷A(附答案)
- 體能科學訓練方法智慧樹知到期末考試答案2024年
- 四川民歌智慧樹知到期末考試答案2024年
- 5S點檢表1(日檢查表)
評論
0/150
提交評論