ARM 匯編啟動代碼詳解:從中斷向量表到中斷處理

ARM 匯編啟動代碼詳解:從中斷向量表到中斷處理

引言

在嵌入式系統開發中,ARM 處理器(如 Cortex-A 系列)的啟動代碼是系統初始化和運行的基礎。啟動代碼通常包括中斷向量表的創建、初始化硬件狀態(如關閉緩存和 MMU)、設置棧指針以及處理各種中斷(如 IRQ、FIQ 等)。本文將詳細解析一段典型的 ARM 匯編啟動代碼,涵蓋 _start 函數、中斷向量表、復位處理程序(Reset_Handler)以及 IRQ 中斷處理程序(IRQ_Handler)。代碼參考了 ARM Cortex-A(armV7)編程手冊和 Cortex-A7 技術參考手冊,確保內容準確且實用。


1. 代碼概述

以下是完整代碼的結構:

.global _start  				/* 全局標號 *//** 描述:_start函數,首先是中斷向量表的創建* 參考文檔:ARM Cortex-A(armV7)編程手冊V4.0.pdf P42,3 ARM Processor Modes and Registers(ARM處理器模型和寄存器)* 		 	ARM Cortex-A(armV7)編程手冊V4.0.pdf P165 11.1.1 Exception priorities(異常)*/
_start:ldr pc, =Reset_Handler        /* 復位中斷 */    ldr pc, =Undefined_Handler    /* 未定義中斷 */    ldr pc, =SVC_Handler          /* SVC(Supervisor)中斷 */    ldr pc, =PrefAbort_Handler    /* 預取終止中斷 */    ldr pc, =DataAbort_Handler    /* 數據終止中斷 */    ldr pc, =NotUsed_Handler      /* 未使用中斷 */    ldr pc, =IRQ_Handler          /* IRQ中斷 */    ldr pc, =FIQ_Handler          /* FIQ(快速中斷)未定義中斷 *//* 復位中斷 */    
Reset_Handler:cpsid i                       /* 關閉全局中斷 *//* 關閉 I、D Cache 和 MMU */mrc p15, 0, r0, c1, c0, 0     /* 讀取 CP15 的 C1 寄存器到 R0 中 */    bic r0, r0, #(0x1 << 12)      /* 清除 C1 寄存器的 bit12 位 (I 位),關閉 I Cache */    bic r0, r0, #(0x1 << 2)       /* 清除 C1 寄存器的 bit2 (C 位),關閉 D Cache */    bic r0, r0, #0x2              /* 清除 C1 寄存器的 bit1 (A 位),關閉對齊 */    bic r0, r0, #(0x1 << 11)      /* 清除 C1 寄存器的 bit11 (Z 位),關閉分支預測 */    bic r0, r0, #0x1              /* 清除 C1 寄存器的 bit0 (M 位),關閉 MMU */    mcr p15, 0, r0, c1, c0, 0     /* 將 r0 寄存器中的值寫入到 CP15 的 C1 寄存器中 */#if 0/* 匯編版本設置中斷向量表偏移 */ldr r0, =0X87800000dsbisbmcr p15, 0, r0, c12, c0, 0dsbisb
#endif/* 設置各個模式下的棧指針 *//* 進入 IRQ 模式 */mrs r0, cpsrbic r0, r0, #0x1f             /* 將 r0 寄存器中的低 5 位清零,也就是 cpsr 的 M0~M4 */    orr r0, r0, #0x12             /* r0 或上 0x12, 表示使用 IRQ 模式 */    msr cpsr, r0                  /* 將 r0 的數據寫入到 cpsr_c 中 */    ldr sp, =0x80600000           /* 設置 IRQ 模式下的棧首地址為 0X80600000, 大小為 2MB *//* 進入 SYS 模式 */mrs r0, cpsrbic r0, r0, #0x1f             /* 將 r0 寄存器中的低 5 位清零 */    orr r0, r0, #0x1f             /* r0 或上 0x1f, 表示使用 SYS 模式 */    msr cpsr, r0                  /* 將 r0 的數據寫入到 cpsr_c 中 */    ldr sp, =0x80400000           /* 設置 SYS 模式下的棧首地址為 0X80400000, 大小為 2MB *//* 進入 SVC 模式 */mrs r0, cpsrbic r0, r0, #0x1f             /* 將 r0 寄存器中的低 5 位清零 */    orr r0, r0, #0x13             /* r0 或上 0x13, 表示使用 SVC 模式 */    msr cpsr, r0                  /* 將 r0 的數據寫入到 cpsr_c 中 */    ldr sp, =0X80200000           /* 設置 SVC 模式下的棧首地址為 0X80200000, 大小為 2MB */cpsie i                       /* 打開全局中斷 */#if 0/* 使能 IRQ 中斷 */mrs r0, cpsr                  /* 讀取 cpsr 寄存器值到 r0 中 */    bic r0, r0, #0x80            /* 將 r0 寄存器中 bit7 清零,也就是 CPSR 中的 I 位清零,表示允許 IRQ 中斷 */    msr cpsr, r0                 /* 將 r0 重新寫入到 cpsr 中 */    
#endifb main                        /* 跳轉到 main 函數 *//* 未定義中斷 */
Undefined_Handler:ldr r0, =Undefined_Handlerbx r0/* SVC 中斷 */
SVC_Handler:ldr r0, =SVC_Handlerbx r0/* 預取終止中斷 */
PrefAbort_Handler:ldr r0, =PrefAbort_Handler    bx r0/* 數據終止中斷 */
DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0/* 未使用的中斷 */
NotUsed_Handler:ldr r0, =NotUsed_Handlerbx r0/* IRQ 中斷!重點!!!!! */
IRQ_Handler:push {lr}                    /* 保存 lr 地址 */    push {r0-r3, r12}           /* 保存 r0-r3,r12 寄存器 */mrs r0, spsr                /* 讀取 spsr 寄存器 */    push {r0}                   /* 保存 spsr 寄存器 */mrc p15, 4, r1, c15, c0, 0 /* 從 CP15 的 C0 寄存器內的值到 R1 寄存器中 */                            add r1, r1, #0X2000         /* GIC 基地址加 0X2000,也就是 GIC 的 CPU 接口端基地址 */    ldr r0, [r1, #0XC]          /* GIC 的 CPU 接口端基地址加 0X0C 就是 GICC_IAR 寄存器 */    push {r0, r1}               /* 保存 r0, r1 */cps #0x13                   /* 進入 SVC 模式,允許其他中斷再次進去 */push {lr}                   /* 保存 SVC 模式的 lr 寄存器 */    ldr r2, =system_irqhandler  /* 加載 C 語言中斷處理函數到 r2 寄存器中 */    blx r2                      /* 運行 C 語言中斷處理函數,帶有一個參數,保存在 R0 寄存器中 */pop {lr}                    /* 執行完 C 語言中斷服務函數,lr 出棧 */    cps #0x12                   /* 進入 IRQ 模式 */    pop {r0, r1}                str r0, [r1, #0X10]         /* 中斷執行完成,寫 EOIR */pop {r0}                    msr spsr_cxsf, r0           /* 恢復 spsr */pop {r0-r3, r12}            /* r0-r3, r12 出棧 */    pop {lr}                    /* lr 出棧 */    subs pc, lr, #4             /* 將 lr-4 賦給 pc *//* FIQ 中斷 */
FIQ_Handler:ldr r0, =FIQ_Handler    bx r0

2. 代碼功能詳解

2.1 _start:程序入口和中斷向量表

_start 是程序的入口點,它首先創建中斷向量表。ARM 處理器在啟動或發生異常時會根據向量表跳轉到對應的處理程序。向量表包含 8 個條目,每個條目對應一種異常或中斷:

  • 復位中斷(Reset):系統上電或復位后執行。
  • 未定義中斷(Undefined):執行了未定義的指令。
  • SVC 中斷(Supervisor Call):軟件觸發系統調用。
  • 預取終止中斷(Prefetch Abort):指令預取失敗。
  • 數據終止中斷(Data Abort):數據訪問失敗。
  • 未使用中斷(Not Used):保留,未定義。
  • IRQ 中斷(Interrupt Request):外部設備請求的中斷。
  • FIQ 中斷(Fast Interrupt Request):快速中斷,通常用于高優先級任務。

代碼使用 ldr pc, =handler 將每個處理程序的地址加載到程序計數器 pc,實現跳轉。例如:

ldr pc, =Reset_Handler    /* 復位中斷 */

這表示當發生復位時,處理器會跳轉到 Reset_Handler 執行。


2.2 Reset_Handler:復位處理程序

Reset_Handler 是系統啟動后的第一個執行函數,負責初始化硬件狀態。以下是其主要步驟:

(1) 關閉全局中斷
cpsid i    /* 關閉全局中斷 */
  • 使用 cpsid i 關閉所有 IRQ 中斷,確保初始化過程中不受干擾。
(2) 關閉緩存和 MMU
mrc p15, 0, r0, c1, c0, 0    /* 讀取 CP15 的 C1 寄存器到 R0 */
bic r0, r0, #(0x1 << 12)     /* 關閉 I Cache */
bic r0, r0, #(0x1 << 2)      /* 關閉 D Cache */
bic r0, r0, #0x2             /* 關閉對齊 */
bic r0, r0, #(0x1 << 11)     /* 關閉分支預測 */
bic r0, r0, #0x1             /* 關閉 MMU */
mcr p15, 0, r0, c1, c0, 0    /* 將修改寫入 CP15 C1 */
  • CP15 協處理器:CP15 負責系統配置,這里讀取其控制寄存器 C1。
  • 位操作:使用 bic 清除特定位,分別關閉指令緩存(I Cache)、數據緩存(D Cache)、內存管理單元(MMU)等功能。這是“讀-改-寫”模式,確保初始狀態干凈。
(3) 設置中斷向量表偏移(可選)
#if 0
ldr r0, =0X87800000
dsb
isb
mcr p15, 0, r0, c12, c0, 0
dsb
isb
#endif
  • 這部分被禁用(#if 0),用于將中斷向量表基址設置為 0x87800000,常見于需要調整向量表位置的場景(如從 Flash 移動到 SRAM)。dsbisb 確保操作同步。
(4) 設置不同模式的棧指針

ARM 處理器有多種工作模式(如 IRQ、SVC、SYS),每個模式需要獨立的棧。代碼為 IRQ、SYS 和 SVC 模式設置棧指針:

/* 進入 IRQ 模式 */
mrs r0, cpsr
bic r0, r0, #0x1f    /* 清零模式位 */
orr r0, r0, #0x12    /* 設置為 IRQ 模式 */
msr cpsr, r0
ldr sp, =0x80600000  /* 設置棧頂為 0x80600000,大小 2MB *//* 進入 SYS 模式 */
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x1f    /* 設置為 SYS 模式 */
msr cpsr, r0
ldr sp, =0x80400000  /* 設置棧頂為 0x80400000 *//* 進入 SVC 模式 */
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x13    /* 設置為 SVC 模式 */
msr cpsr, r0
ldr sp, =0x80200000  /* 設置棧頂為 0x80200000 */
  • 模式切換:使用 mrs 讀取當前程序狀態寄存器(CPSR),bic 清零模式位(低 5 位),orr 設置新模式,msr 寫入回 CPSR。
  • 棧設置:棧指針 sp 指向內存區域(這里是 DDR 范圍 0x80000000~0x9FFFFFFF),棧向下增長,必須 4 字節對齊。
(5) 打開全局中斷并跳轉
cpsie i    /* 打開全局中斷 */
b main     /* 跳轉到 main 函數 */
  • cpsie i 重新啟用中斷。
  • b main 跳轉到 C 語言的 main 函數,標志著初始化完成。

2.3 其他中斷處理程序

除了 Reset_Handler,代碼還定義了其他中斷處理程序,但大多數只是簡單地進入死循環:

Undefined_Handler:ldr r0, =Undefined_Handlerbx r0SVC_Handler:ldr r0, =SVC_Handlerbx r0PrefAbort_Handler:ldr r0, =PrefAbort_Handler    bx r0DataAbort_Handler:ldr r0, =DataAbort_Handlerbx r0NotUsed_Handler:ldr r0, =NotUsed_Handlerbx r0FIQ_Handler:ldr r0, =FIQ_Handler    bx r0
  • 這些處理程序使用 ldr 加載自身地址到 r0,然后 bx r0 跳轉回去,形成死循環。這是一種簡單處理方式,實際應用中可能需要更復雜的邏輯。

2.4 IRQ_Handler:IRQ 中斷處理

IRQ_Handler 是代碼中的重點,負責處理外部設備的中斷。以下是詳細解析:

(1) 保存上下文
push {lr}                    /* 保存返回地址 */
push {r0-r3, r12}           /* 保存通用寄存器 */
  • 保存中斷前的 lr(返回地址)和 r0-r3, r12(可能被使用的寄存器)。
(2) 保存狀態
mrs r0, spsr                /* 讀取 spsr */
push {r0}                   /* 保存 spsr */
  • spsr 存儲中斷前的處理器狀態,需保存以便返回。
(3) 獲取 GIC 中斷號
mrc p15, 4, r1, c15, c0, 0 /* 從 CP15 讀取 GIC 基址 */
add r1, r1, #0X2000         /* 計算 GIC CPU 接口地址 */
ldr r0, [r1, #0XC]          /* 從 GICC_IAR 讀取中斷號 */
push {r0, r1}               /* 保存中斷號和基址 */
  • 通過 CP15 獲取 GIC 基址,偏移 0x2000 得到 CPU 接口地址,從 GICC_IAR 寄存器讀取當前中斷號。
(4) 模式切換和調用 C 函數
cps #0x13                   /* 切換到 SVC 模式 */
push {lr}                   /* 保存 SVC 模式下的 lr */
ldr r2, =system_irqhandler  /* 加載 C 函數地址 */
blx r2                      /* 調用 C 中斷處理函數 */
  • 切換到 SVC 模式,調用 C 語言的 system_irqhandler 函數,r0 作為參數傳遞中斷號。
(5) 清理和返回
pop {lr}                    /* 恢復 SVC 模式 lr */
cps #0x12                   /* 切換回 IRQ 模式 */
pop {r0, r1}                
str r0, [r1, #0X10]         /* 寫 EOIR 標記中斷結束 */
pop {r0}                    
msr spsr_cxsf, r0           /* 恢復 spsr */
pop {r0-r3, r12}            /* 恢復寄存器 */
pop {lr}                    /* 恢復 lr */
subs pc, lr, #4             /* 返回 */
  • 恢復所有狀態,通知 GIC 中斷處理完成(寫 GICC_EOIR),返回到中斷前的位置。

3. 總結與應用

3.1 關鍵點回顧

  • 中斷向量表:定義了 8 種異常的入口,啟動時跳轉到 Reset_Handler
  • 復位初始化:關閉中斷、緩存和 MMU,設置棧指針,跳轉到 main
  • IRQ 處理:通過 GIC 獲取中斷號,調用 C 函數處理,恢復現場返回。

3.2 應用場景

這段代碼適用于嵌入式系統(如基于 Cortex-A7 的開發板),用于啟動操作系統或裸機程序。理解這些內容有助于調試硬件初始化問題、優化中斷響應以及開發低級驅動。

3.3 擴展閱讀

  • 參考文檔:ARM Cortex-A(armV7)編程手冊、Cortex-A7 技術參考手冊。
  • 相關知識:CP15 協處理器、GIC 中斷控制器、ARM 模式切換。

4. 附錄:常見問題解答

  • 為什么關閉緩存和 MMU? 為了確保啟動時硬件狀態干凈,避免緩存或虛擬內存的殘留影響。
  • 棧指針為何向下增長? ARM 棧通常向下增長,方便壓棧和出棧操作。
  • GIC 是什么? 通用中斷控制器,管理多個設備的中斷請求。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75881.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75881.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75881.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

4.7學習總結 可變參數+集合工具類Collections+不可變集合

可變參數&#xff1a; 示例&#xff1a; public class test {public static void main(String[] args) {int sumgetSum(1,2,3,4,5,6,7,8,9,10);System.out.println(sum);}public static int getSum(int...arr){int sum0;for(int i:arr){sumi;}return sum;} } 細節&#xff1a…

2023年藍橋杯第十四屆CC++大學B組真題及代碼

目錄 1A&#xff1a;日期統計 解析代碼_暴力_正解 2B&#xff1a;01串的熵 解析代碼_暴力_正解 3C&#xff1a;冶煉金屬 解析代碼_暴力_正解 4D&#xff1a;飛機降落 解析代碼_暴力dfs_正解 5E&#xff1a;接龍數列 解析代碼_dp_正解 6F&#xff1a;島嶼個數 解析代…

rom定制系列------小米10pro機型定制解鎖固件 原生安卓15批量線刷固件 操作解析與界面預覽

注意;固件用于自己機型忘記密碼或者手機號注銷等出現設備鎖 過保修期 售后無視的機型&#xff0c;勿用于非法途徑 目前有粉絲聯系&#xff0c;自己的機型由于手機號注銷導致手機更新系統后出現設備鎖界面。另外也沒有解鎖bl。目前無法使用手機。經過詢問是小米10pro機型。根據…

信息學奧賽一本通 1861:【10NOIP提高組】關押罪犯 | 洛谷 P1525 [NOIP 2010 提高組] 關押罪犯

【題目鏈接】 ybt 1861&#xff1a;【10NOIP提高組】關押罪犯 洛谷 P1525 [NOIP 2010 提高組] 關押罪犯 【題目考點】 1. 圖論&#xff1a;二分圖 2. 二分答案 3. 種類并查集 【解題思路】 解法1&#xff1a;種類并查集 一個囚犯是一個頂點&#xff0c;一個囚犯對可以看…

我的NISP二級之路-01

目錄 一.SSE-CMM系統安全工程-能力成熟度模型(Systems Security Engineering - Capability Maturity Model) 二.ISMS 即信息安全管理體系(Information Security Management System),是一種基于風險管理的、系統化的管理體系 三.Kerberos協議 1. 用戶登錄與 AS 請求 2…

WEB安全--內網滲透--利用Net-NTLMv2 Hash

一、前言 在前兩篇文章中分析了NTLM協議中Net-NTLMv2 Hash的生成、如何捕獲Net-NTLMv2 Hash&#xff0c;現在就來探討一下在內網環境中&#xff0c;如何利用Net-NTLMv2 Hash進行滲透。 二、Net-NTLM Hash的破解 工具&#xff1a;hashcat 原理&#xff1a;利用其內部的字典對…

如何正確使用 `apiStore` 進行 API 管理

在現代前端開發中&#xff0c;API 管理是一個非常重要的環節。apiStore 是一個基于 Pinia 的狀態管理工具&#xff0c;它可以幫助我們更高效地管理和調用 API。本文將詳細介紹如何正確使用 apiStore&#xff0c;包括如何創建 API 配置文件、在組件中使用 apiStore 以及如何配置…

瓦片數據合并方法

影像數據 假如有兩份影像數據 1.全球底層影像0-5級別如下&#xff1a; 2.局部高清影像數據級別9-14如下&#xff1a; 合并方法 將9-14文件夾復制到全球底層0-5的目錄下 如下&#xff1a; 然后合并xml文件 使得Tileset設置到最高級&#xff08;包含所有級別&#xff09;&…

C++中的類和對象(上)

1 類的定義 1.1 類定義的格式 1 class為定義類的關鍵字&#xff0c;Stack為類的名字&#xff0c;{}中為類的主體&#xff0c;注意類定義結束時后面分號不能省 略》。類體中內容稱為類的成員&#xff1a;類中的變量稱為類的屬性或成員變量; 類中的函數稱為類的方法或者成員函數…

【Tauri2】013——前端Window Event與創建Window

前言 【Tauri2】012——on_window_event函數-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146909801?spm1001.2014.3001.5501 前面介紹了on_window_event&#xff0c;這個在Builder中的方法&#xff0c;里面有許多事件&#xff0c;比如Moved&#xff0c;Res…

【問題處理】webpack4升webpack5,報錯Uncaught ReferrnceError: process is not defined

問題 正在做webpack4升webpack5&#xff0c;項目構建項目成功后在瀏覽器打開時報錯 Uncaught ReferrnceError: process is not defined。 原因 webpack 5 不再自動 polyfill Node.js 的核心模塊。 如果你在瀏覽器運行的代碼中使用它&#xff0c;需要從 NPM 中安裝兼容模塊…

軟件工程師減肥計劃

一、目標設定 在 3 個月內減輕體重 5-7kg&#xff0c;改善身體代謝水平和體脂率&#xff0c;增強身體活力和精神狀態&#xff0c;以更好地適應工作強度。 二、飲食調整 &#xff08;一&#xff09;基本原則 控制熱量攝入&#xff0c;保證每天攝入熱量低于消耗熱量 500-800 …

即時訪問成為降低風險的關鍵

云計算和軟件即服務 (SaaS) 解決方案的廣泛采用從根本上重塑了企業的數字格局。 不同行業的組織越來越多地利用云固有的可擴展性和成本效益來推動創新和簡化運營。 這種向基于云的環境的轉變也帶來了一系列新的復雜安全挑戰&#xff0c;需要仔細考慮并制定強有力的緩解策略。…

[環境配置] 1. 開發環境搭建

開發環境搭建 本文檔將詳細介紹如何搭建深度學習開發環境&#xff0c;包括 Python 環境配置、IDE 選擇與配置以及虛擬環境管理。 也會介紹一下最近比較流行的 uv 工具。它是一個用 Rust 編寫的極其快速的 Python 包和項目管理工具。 uv 是一個非常強大的工具&#xff0c;它可…

rust 同時處理多個異步任務,并在一個任務完成退出

use std::thread; use tokio::{sync::mpsc,time::{sleep, Duration}, };async fn check_for_one() {// 該函數會每秒打印一次 "write"loop {println!("write");sleep(Duration::from_secs(1)).await;} }async fn start_print_task() -> Result<(), (…

“群芳爭艷”:CoreData 4 種方法計算最大值的效率比較(上)

概覽 在 CoreData 支持的 App 中&#xff0c;一種常見操作就是計算數據庫表中指定字段的最大值&#xff08;或最小值&#xff09;。就是這樣一種看起來“不足掛齒”的任務&#xff0c;可能稍不留神就會“馬失前蹄”。 在實際的代碼中&#xff0c;我們怎樣才能既迅速又簡潔的…

skynet網絡包庫(lua-netpack.c)的作用解析

目錄 網絡包庫&#xff08;lua-netpack.c&#xff09;的作用解析1. 數據包的分片與重組2. 網絡事件處理3. 內存管理4. 數據打包與解包 動態庫&#xff08;.so&#xff09;在 Lua 中的使用1. 編譯為動態庫2. Lua 中加載與調用(1) 加載模塊(2) 核心方法(3) 使用示例 3. 注意事項 …

計科數據庫第二次上機操作--實驗二 表的簡單查詢

一、建數據庫和表 1&#xff0e;啟動數據庫服務軟件 Navicat 2&#xff0e;在 Navicat 中建立數據庫 test 3. 在test數據庫上建立teacher表&#xff1a; 二、基本查詢 2.1 從teacher表中分別檢索出教師的所有信息 SELECT * FROM teacher WHERE 教工號2000; SELECT * FROM t…

WPF依賴注入

一、IOC 在 WPF 中的原理 控制反轉&#xff08;IOC&#xff09;是一種設計原則&#xff0c;它將對象的創建和依賴關系的管理從對象本身轉移到外部容器&#xff08;IOC 容器&#xff09;。在傳統的編程方式中&#xff0c;一個對象如果需要使用另一個對象&#xff08;即存在依賴…

【大模型深度學習】如何估算大模型需要的顯存

一、模型參數量 參數量的單位 參數量指的是模型中所有權重和偏置的數量總和。在大模型中&#xff0c;參數量的單位通常以“百萬”&#xff08;M&#xff09;或“億”&#xff08;B&#xff0c;也常說十億&#xff09;來表示。 百萬&#xff08;M&#xff09;&#xff1a;表示…