<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM Linux中斷向量表搬移設(shè)計(jì)過(guò)程

          ARM Linux中斷向量表搬移設(shè)計(jì)過(guò)程

          作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏

          Preface引言

          本文引用地址:http://www.ex-cimer.com/article/201611/317931.htm

          我在這里用一些篇幅來(lái)描述一下arm體系結(jié)構(gòu)下Linux中怎樣來(lái)初始化中斷向量表的,因?yàn)檫@個(gè)方法很具有通用性,我把它叫做代碼大挪移。您說(shuō)搬代碼誰(shuí)不會(huì)阿,不就是拷貝嗎,的確如此,但是拷貝也有技巧??截惡芎?jiǎn)單啦,其實(shí)就是memcpy,這不用提,我在這里想說(shuō)的是,你怎么把你的代碼設(shè)計(jì)成能隨便拷貝的,換句專(zhuān)業(yè)的術(shù)語(yǔ),叫與位置無(wú)關(guān)的代碼,拷到哪都能用。我以前也用過(guò)類(lèi)似的方法作啟動(dòng),今天拿來(lái)說(shuō)說(shuō)。

          Scenario 1第一場(chǎng)景copy

          我們先看實(shí)際動(dòng)作。代碼的位置在arch/arm/traps.c中,kernel version: 2.6.27。這個(gè)是初始化部分的代碼,setup_arch()->early_trap_init().熟悉初始化部分的朋友們可能見(jiàn)到過(guò)這段代碼。

          void __init early_trap_init(void)

          {

          unsigned long vectors = CONFIG_VECTORS_BASE;

          extern char __stubs_start[], __stubs_end[];

          &nsp;extern char __vectors_start[], __vectors_end[];

          extern char __kuser_helper_start[], __kuser_helper_end[];

          int kuser_sz = __kuser_helper_end - __kuser_helper_start;

          /*

          * Copy the vectors, stubs and kuser helpers (in entry-armv.S)

          * into the vector page, mapped at 0xffff0000, and ensure these

          * are visible to the instruction stream.

          */

          memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

          memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

          memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

          }

          實(shí)際copy動(dòng)作一目了然,就是兩個(gè)memcpy(第三個(gè)實(shí)際上是拷貝一些別的東西,原理是一樣的,這里不提了). Copy的源是vectors,這個(gè)值是CONFIG_VECTORS_BASE,一般來(lái)講,是0xffff0000,當(dāng)然你可以根據(jù)硬件的設(shè)定自己配制這個(gè)值。把什么東西往那copy呢?第一部分是從__vectors_start到__vectors_end之間的代碼,第二部分是從__stubs_start到__stubs_end之間的代碼,而第二部分是copy到vectors + 0x200起始的位置。也就是說(shuō),兩部分之間的距離是0x200,即512個(gè)字節(jié)。

          我們來(lái)看__vectors_start,__vectors_end,font face="Times New Roman">__stubs_start,__stubs_end到底是什么東西,只要知道它們?cè)谀睦锒x的,就知道怎么回事了。

          Scenario 2第二場(chǎng)景主角閃亮登場(chǎng)

          它們埋伏在arch/arm/kernel/entry-armv.S中,這個(gè)文件是arm中各個(gè)模式的入口代碼,熟悉arm的朋友們知道arm有幾種模式,不知道的自己查查,不說(shuō)了。我們?nèi)∫粋€(gè)片斷,和我們的闡述相關(guān)的部分。為了讓大家看得更清楚,我刪掉了部分代碼和注釋?zhuān)阎鞲赏癸@出來(lái)。有興趣的朋友可以查看源代碼,研究全部,里面還是比較有內(nèi)涵的。

          .globl__stubs_start

          __stubs_start:

          /*

          * Interrupt dispatcher

          */

          vector_stubirq, IRQ_MODE, 4

          //請(qǐng)注意這里:vector_stub是一個(gè)宏,展開(kāi)后是一塊代碼,下面是個(gè)跳轉(zhuǎn)表,我們將代碼結(jié)//構(gòu)展開(kāi),大致是這樣的結(jié)構(gòu): (后面的vector_stubdabt, ABT_MODE, 8等展開(kāi)過(guò)程全一樣,在此略過(guò)不提)

          // -------------------------------- begin展開(kāi)

          .align5

          vector_irq:

          sublr, lr, 4

          @ Save r0, lr_ (parent PC) and spsr_

          @ (parent CPSR)

          @

          stmiasp, {r0, lr}@ save r0, lr

          mrslr, spsr

          strlr, [sp, #8]@ save spsr

          @ Prepare for SVC32 mode.IRQs remain disabled.

          @

          mrsr0, cpsr

          eorr0, r0, IRQ_MODE ^ SVC_MODE)

          msrspsr_cxsf, r0

          @ the branch table must immediately follow this code

          @

          andlr, lr, #0x0f

          movr0, sp

          ldrlr, [pc, lr, lsl #2]

          movspc, lr@ branch to handler in SVC mode

          // -------------------------------- end展開(kāi)

          .long__irq_usr@0(USR_26 / USR_32)

          .long__irq_invalid@1(FIQ_26 / FIQ_32)

          .long__irq_invalid@2(IRQ_26 / IRQ_32)

          .long__irq_svc@3(SVC_26 / SVC_32)

          。。。

          .long__irq_invalid@f

          /*

          * Data abort dispatcher

          * Enter in ABT mode, spsr = USR CPSR, lr = USR PC

          */

          vector_stubdabt, ABT_MODE, 8

          .long__dabt_usr@0(USR_26 / USR_32)

          .long__dabt_invalid@1(FIQ_26 / FIQ_32)

          .long__dabt_invalid@2(IRQ_26 / IRQ_32)

          .long__dabt_svc@3(SVC_26 / SVC_32)

          。。。

          .long__dabt_invalid@f

          /*

          * Prefetch abort dispatcher

          * Enter in ABT mode, spsr = USR CPSR, lr = USR PC

          */

          vector_stubpabt, ABT_MODE, 4

          .long__pabt_usr@0 (USR_26 / USR_32)

          .long__pabt_invalid@1 (FIQ_26 / FIQ_32)

          .long__pabt_invalid@2 (IRQ_26 / IRQ_32)

          .long__pabt_svc@3 (SVC_26 / SVC_32)

          。。。

          .long__pabt_invalid@f

          /*

          * Undef instr entry dispatcher

          * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC

          */

          vector_stubund, UND_MODE

          .long__und_usr@0 (USR_26 / USR_32)

          .long__und_invalid@1 (FIQ_26 / FIQ_32)

          .long__und_invalid@2 (IRQ_26 / IRQ_32)

          .long__und_svc@3 (SVC_26 / SVC_32)

          。。。

          .long__und_invalid@f

          .align5

          vector_fiq:

          disable_fiq

          subspc, lr, #4

          vector_addrexcptn:

          bvector_addrexcptn

          /*

          * We group all the following data together to optimise

          * for CPUs with separate I & D caches.

          */

          .align5

          .LCvswi:

          .wordvector_swi

          .globl__stubs_end

          __stubs_end:

          .equstubs_offset, __vectors_start + 0x200 - __stubs_start

          .globl__vectors_start

          __vectors_start:

          swiSYS_ERROR0

          bvector_und + stubs_offset

          ldrpc, .LCvswi + stubs_offset

          bvector_pabt + stubs_offset

          bvector_dabt + stubs_offset

          bvector_addrexcptn + stubs_offset

          bvector_irq + stubs_offset

          bvector_fiq + stubs_offset

          .globl__vectors_end

          __vectors_end:

          為了讓大家看得更清,我把代碼的結(jié)構(gòu)再次簡(jiǎn)化成這樣:

          .globl__stubs_start

          __stubs_start:

          .align5

          vector_irq:

          [code part]//展開(kāi)代碼

          [jump table part]//地址跳轉(zhuǎn)表

          。。。

          .align5

          vector_dabt:

          [code part]

          [jump table part]

          。。。

          .align5

          vector_ pabt:

          [code part]

          [jump table part]

          。。。

          .align5

          vector_und:

          [code part]

          [jump table part]

          。。。

          .align5

          vector_fiq:

          。。。

          .globl__stubs_end

          __stubs_end:

          .globl__vectors_start

          __vectors_start:

          swiSYS_ERROR0

          bvector_und + stubs_offset

          ldrpc, .LCvswi + stubs_offset

          bvector_pabt + stubs_offset

          bvector_dabt + stubs_offset

          bvector_addrexcptn + stubs_offset

          bvector_irq + stubs_offset

          bvector_fiq + stubs_offset

          .globl__vectors_end

          __vectors_end:

          在這里我不花過(guò)多的篇幅去解釋代碼的意思,這不是本文的目的,只要你把結(jié)構(gòu)看清,就達(dá)到目的了。但我會(huì)花點(diǎn)時(shí)間研究一下展開(kāi)代碼部分(藍(lán)色)的特征,這部分代碼是與位置無(wú)關(guān)的代碼,我們稍微研究一下,它為什么會(huì)這么寫(xiě)。

          .align5

          vector_irq:

          [code part]//展開(kāi)代碼

          [jump table part]//地址跳轉(zhuǎn)表

          。。。

          首先這部分代碼大致都是一樣的結(jié)構(gòu),前面是一些代碼,后面跟著一個(gè)跳轉(zhuǎn)表。跳轉(zhuǎn)表里面定義了一些地址。我們截取這部分看

          。。。

          @ the branch table must immediately follow this code

          @

          andlr, lr, #0x0f(1)// lr中當(dāng)前存儲(chǔ)了上一個(gè)狀態(tài)寄存器的值,對(duì)后幾位做與,

          //就是取在中斷前處在用戶(hù)態(tài)還是核心態(tài),這個(gè)值用作跳

          //轉(zhuǎn)表的索引

          movr0, sp(2)//用做他用,sp值當(dāng)?shù)谝粋€(gè)參數(shù)傳給后面函數(shù)

          ldrlr, [pc, lr, lsl #2](3)// pc是當(dāng)前執(zhí)行指令地址加8,即跳轉(zhuǎn)表的基地址,lr是索引

          //很好的技巧,取pc找當(dāng)前地址什么時(shí)候都沒(méi)錯(cuò)

          movpc, lr@ branch to handler in SVC mode

          [jump table]

          .long__irq_usr@0(USR_26 / USR_32)

          .long__irq_invalid@1(FIQ_26 / FIQ_32)

          .long__irq_invalid@2(IRQ_26 / IRQ_32)

          .long__irq_svc@3(SVC_26 / SVC_32)

          真正的跳轉(zhuǎn)在最后一句完成,大家都看得很清楚。跳到哪里去了,如果中斷以前是svc模式,就會(huì)跳到__irq_svc。我們發(fā)現(xiàn)這里不會(huì)直接用b(bl,bx等)個(gè),

          ü一是b跳轉(zhuǎn)后面是個(gè)偏移,而這個(gè)偏移是有限制的,不能太大

          ü二是b跳轉(zhuǎn)后面的偏移你不知道在代碼拷貝后還是不是那個(gè)樣子,因?yàn)槲覀円嵋拼a,所以如果你不能確定搬移后的偏移不變,那你就用絕對(duì)地址,而上面的代碼前三句就是算出絕對(duì)地址來(lái),然后用絕對(duì)地址賦值給pc直接完成跳轉(zhuǎn)。

          這些都是一些技巧,總之你要注意的是寫(xiě)位置無(wú)關(guān)的代碼時(shí)涉及到跳轉(zhuǎn)部分,用b跳轉(zhuǎn)還是直接賦成絕對(duì)地址(通過(guò)跳轉(zhuǎn)表實(shí)現(xiàn)),如果你不能保證搬移后的偏移一致,寫(xiě)這部分就要注意了,要用一些技巧的。

          大家可以去用gcc的-fPIC和-S選項(xiàng)匯編一個(gè)小的函數(shù)看看,fPIC就是與位置無(wú)關(guān)選項(xiàng),相信編譯過(guò)動(dòng)態(tài)庫(kù)的人都熟悉,看看它是怎么做的。你會(huì)發(fā)現(xiàn)異曲同工。

          Scenario 3第三場(chǎng)景大搬移

          我用一個(gè)章節(jié)來(lái)介紹大搬移的過(guò)程,以及一些在搬移中Linux出現(xiàn)的問(wèn)題及解決方案。我把整個(gè)的搬移過(guò)程做成一張圖里,然后討論了一些技術(shù)細(xì)節(jié)。我們看到這是一個(gè)巨大無(wú)比的圖,我們這章節(jié)的所闡述的內(nèi)容都在圖里。

          我們將搬移前的代碼組織稱(chēng)為Code/Load視圖,因?yàn)檫@是代碼中的(或image中的)組織情況,把搬移后的代碼組織稱(chēng)為Exec視圖,反映的是代碼執(zhí)行時(shí)代碼在內(nèi)存中的情況。我剛才講過(guò)了第一場(chǎng)景的情況,忘了的回到第一場(chǎng)景中去看,兩個(gè)memcpy的執(zhí)行過(guò)程在圖中也有表示,就是藍(lán)色和紅色的帶箭頭的虛線,這就是代碼從code view到exec view的拷貝過(guò)程,一目了然,不用多說(shuō)。

          現(xiàn)在出現(xiàn)了一個(gè)問(wèn)題,就是我們發(fā)現(xiàn)在__vector_start和__vector_end之間的代碼有點(diǎn)怪異,我們?cè)俅握竭@里來(lái)看:

          .equstubs_offset, __vectors_start + 0x200 - __stubs_start

          .globl__vectors_start

          __vectors_start:

          swiSYS_ERROR0

          bvector_und + stubs_offset

          ldrpc, .LCvswi + stubs_offset

          bvector_pabt + stubs_offset

          bvector_dabt + stubs_offset

          bvector_addrexcptn + stubs_offset

          bvector_irq + stubs_offset

          bvector_fiq + stubs_offset

          .globl__vectors_end

          __vectors_end:

          在第二個(gè)場(chǎng)景中我們說(shuō)過(guò),這叫做位置無(wú)關(guān)的代碼,因?yàn)橐截惖絼e的地方。而且里面都是跳轉(zhuǎn)指令。我們發(fā)現(xiàn)了除了第三個(gè)行代碼用了絕對(duì)地址進(jìn)行了跳轉(zhuǎn),其它都是用的b跳轉(zhuǎn)。舉個(gè)例子,bvector_dabt + stubs_offset,(vector_dabt在__stubs_start和__stubs_end之間),如果你用b vector_dabt,這肯定是有問(wèn)題的,因?yàn)閏opy之后exec view的組織(map)是不一樣的,所以b后這個(gè)偏移就不對(duì)了。這里面,我們就要對(duì)這個(gè)偏移進(jìn)行一次調(diào)整。Stubs_offset就是這個(gè)調(diào)整值,是可以計(jì)算出來(lái)的,具體的計(jì)算過(guò)程在圖中講得比較清楚,這里不提了。大家可以在圖中看到詳細(xì)的推導(dǎo)過(guò)程。

          其實(shí)盡管ldrpc, .LCvswi + stubs_offset這條指令用的是絕對(duì)地址跳轉(zhuǎn),用得跳轉(zhuǎn)表的方法,但找地址的過(guò)程也用到了這個(gè)技術(shù)。我們看到

          .align5

          .LCvswi:

          .wordvector_swi

          .LCvswi這個(gè)位置存儲(chǔ)的是一個(gè)地址,就是要跳到這個(gè)地方。.align 5的意思是32字節(jié)對(duì)齊,這個(gè)是保證cache line對(duì)齊的,不提了。在exec view中找這個(gè)地址,就得加上個(gè)offset.原理是一樣的,因?yàn)?LCvswi在__stubs_start和__stubs_end之間,這個(gè)區(qū)域被搬移了,不能直接用這個(gè)標(biāo)簽地址了,vector_swi沒(méi)有被搬移,所以可以直接用。

          總結(jié)一下。我覺(jué)得我要講的東西雖然是Linux中的技術(shù)細(xì)節(jié),描述的確是代碼搬移過(guò)程原理和注意事項(xiàng)。其實(shí)更重要的是,我們?nèi)绾伟堰@一個(gè)過(guò)程倒過(guò)來(lái),即在涉及到代碼搬移的場(chǎng)合中如何進(jìn)行設(shè)計(jì),如何運(yùn)用這些技術(shù)實(shí)現(xiàn)這一設(shè)計(jì)過(guò)程。你可以遵循這樣的指導(dǎo)步驟:

          1.畫(huà)出那個(gè)大圖來(lái),按自己的要求確定Code view和Exec view,設(shè)計(jì)搬移區(qū)段和設(shè)計(jì)搬移的位置

          2.寫(xiě)出要搬移的代碼,運(yùn)用位置無(wú)關(guān)的技術(shù)(上面提到的)進(jìn)行編碼和檢驗(yàn)

          3.用類(lèi)似memcpy的代碼進(jìn)行搬移



          關(guān)鍵詞: ARMLinux中斷向量

          評(píng)論


          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();