👹
Carlos's Tech Blog
  • 🧔ECUs
    • ZYNQ_Documents
      • [ZYNQ] 构建ZYNQ的BSP工程
      • [ZYNQ] 启动流程
      • [ZYNQ] Secure Boot Flow
      • [ZYNQ] Provisioning Guideline
      • [ZYNQ] Decrypting Partition by the Decrypt Agent Using PUF key
      • [ZYNQ] enabling the cryptsetup on ramdisk
      • [ZYNQ] Encrypt external files based on file system using PUF key
      • [ZYNQ] Loading an Encrypted Linux kernel at U-Boot with a KUP Key
      • [ZYNQ] cross-compile the cryptsetup on Xilinx ZYNQ aarch64 platform
      • [ZYNQ] Linux Linaro系统镜像制作SD卡启动
    • S32G_Documents
      • [S32G] Going through the s32g hard/soft platform
      • [S32G] S32g247's Secure Boot using HSE firmware
        • S32g2 HSE key config
        • How S32g verify secure boot image
        • S32g secure boot signature generation
        • How to download and build S32g Secure boot image
        • [S32G] OTA with Secure Boot
    • RT117x_Documents
      • [RT-117x]IMX RT1170 Provisioning Guideline
      • [RT-117x] Going through the MX-RT1170 hard/soft platform
      • [RT-117x] i.MX-RT1170's Secure Boot
        • [RT-117x]Signing image with the HSM (SignServer)
    • LS104x_Documents
      • [LS104x] bsp project
      • [LS104x] boot flow
      • [LS104x] secure boot
      • [LS104x] Application Note, Using the PKCS#11 in TCU platform
      • [LS104x] 使用ostree更新rootfs
      • [LS104x] ostree的移植
      • [LS104x] Starting with Yocto
      • [LS104x] 使用FIT的kernel格式和initramfs
    • IMX6/8_Documents
      • [IMX6] Defining A U-Boot Command
      • NXP IMX6 嵌入式板子一些笔记
      • NXP-imx6 initialization
    • Vehicle_Apps
      • [SecOC] Tree
        • [SecOC] SecOC Freshness and MAC Truncation
  • 😾TECH
    • Rust Arm OS
      • ARMv7m_Using_The_RUST_Cross_Compiler
    • ARM
      • ARM-v7-M
        • 01_ARMv7-M_处理器架构技术综述
        • 02_ARMv7-M_编程模型与模式
        • 03_ARMv7-M_存储系统结构
        • 04_ARMv7-M_异常处理及中断处理
      • ARM-v8-A
        • 02_ARMv8_基本概念
        • 03_ARMv8_指令集介绍_加载指令集和存储指令集
        • 04_ARMv8_指令集_运算指令集
        • 05_ARMv8_指令集_跳转_比较与返回指令
        • 06_ARMv8_指令集_一些重要的指令
        • 0X_ARMv8_指令集_基于汇编的UART驱动
        • 07_ARMv8_汇编器Using as
        • 08_ARMv8_链接器和链接脚本
        • 09_ARMv8_内嵌汇编(内联汇编)Inline assembly
        • 10_ARMv8_异常处理(一) - 入口与返回、栈选择、异常向量表
        • 11_ARMv8_异常处理(二)- Legacy 中断处理
        • 12_ARMv8_异常处理(三)- GICv1/v2中断处理
        • 13_ARMv8_内存管理(一)-内存管理要素
        • 14_ARMv8_内存管理(二)-ARM的MMU设计
        • 15_ARMv8_内存管理(三)-MMU恒等映射及Linux实现
        • 16_ARMv8_高速缓存(一)cache要素
        • 17_ARMv8_高速缓存(二)ARM cache设计
        • 18_ARMv8_高速缓存(三)多核与一致性要素
        • 19_ARMv8_TLB管理(Translation Lookaside buffer)
        • 20_ARMv8_barrier(一)流水线和一致性模型
        • 21_ARMv8_barrier(二)内存屏障案例
      • ARM Boot Flow
        • 01_Embedded_ARMv7/v8 non-secure Boot Flow
        • 02_Embedded_ARMv8 ATF Secure Boot Flow (BL1/BL2/BL31)
        • 03_Embedded_ARMv8 BL33 Uboot Booting Flow
      • ARM Compiler
        • Compiler optimization and the volatile keyword
      • ARM Development
        • 在MACBOOK上搭建ARMv8架构的ARM开发环境
        • Starting with JLink debugger or QEMU
    • Linux
      • Kernel
        • 0x01_LinuxKernel_内核的启动(一)之启动前准备
        • 0x02_LinuxKernel_内核的启动(二)SMP多核处理器启动过程分析
        • 0x21_LinuxKernel_内核活动(一)之系统调用
        • 0x22_LinuxKernel_内核活动(二)中断体系结构(中断上文)
        • 0x23_LinuxKernel_内核活动(三)中断体系结构(中断下文)
        • 0x24_LinuxKernel_进程(一)进程的管理(生命周期、进程表示)
        • 0x25_LinuxKernel_进程(二)进程的调度器的实现
        • 0x26_LinuxKernel_设备驱动(一)综述与文件系统关联
        • 0x27_LinuxKernel_设备驱动(二)字符设备操作
        • 0x28_LinuxKernel_设备驱动(三)块设备操作
        • 0x29_LinuxKernel_设备驱动(四)资源与总线系统
        • 0x30_LinuxKernel_设备驱动(五)模块
        • 0x31_LinuxKernel_内存管理(一)物理页面、伙伴系统和slab分配器
        • 0x32_LinuxKernel_内存管理(二)虚拟内存管理、缺页与调试工具
        • 0x33_LinuxKernel_同步管理_原子操作_内存屏障_锁机制等
        • 01_LinuxDebug_调试理论和基础综述
      • Userspace
        • Linux-用户空间-多线程与同步
        • Linux进程之间的通信-管道(上)
        • Linux进程之间的通信-管道(下)
        • Linux进程之间的通信-信号量(System V)
        • Linux进程之间的通信-内存共享(System V)
        • Linux进程之间的通信-消息队列(System V)
        • Linux应用调试(一)方法、技巧和工具 - 综述
        • Linux应用调试(二)工具之coredump
        • Linux应用调试(三)工具之Valgrind
        • Linux机制之内存池
        • Linux机制之对象管理和引用计数(kobject/ktype/kset)
        • Linux机制copy_{to, from}_user
        • Linux设备树 - DTS语法、节点、设备树解析等
        • Linux System : Managing Linux Services - inittab & init.d
        • Linux System : Managing Linux Services - initramfs
      • Kernel Examples
        • Linux Driver - GPIO键盘驱动开发记录_OMAPL138
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write
        • 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ
        • Linux内核调用SPI驱动_实现OLED显示功能
        • Linux内核调用I2C驱动_驱动嵌套驱动方法MPU6050
    • OPTEE
      • 01_OPTEE-OS_基础之(一)功能综述、简要介绍
      • 02_OPTEE-OS_基础之(二)TrustZone和ATF功能综述、简要介绍
      • 03_OPTEE-OS_系统集成之(一)编译、实例、在QEMU上执行
      • 05_OPTEE-OS_系统集成之(三)ATF启动过程
      • 06_OPTEE-OS_系统集成之(四)OPTEE镜像启动过程
      • 07_OPTEE-OS_系统集成之(五)REE侧上层软件
      • 08_OPTEE-OS_系统集成之(六)TEE的驱动
      • 09_OPTEE-OS_内核之(一)ARM核安全态和非安全态的切换
      • 10_OPTEE-OS_内核之(二)对安全监控模式的调用的处理
      • 11_OPTEE-OS_内核之(三)中断与异常的处理
      • 12_OPTEE-OS_内核之(四)对TA请求的处理
      • 13_OPTEE-OS_内核之(五)内存和cache管理
      • 14_OPTEE-OS_内核之(六)线程管理与并发
      • 15_OPTEE-OS_内核之(七)系统调用及IPC机制
      • 16_OPTEE-OS_应用之(一)TA镜像的签名和加载
      • 17_OPTEE-OS_应用之(二)密码学算法和安全存储
      • 18_OPTEE-OS_应用之(三)可信应用的开发
      • 19_OPTEE-OS_应用之(四)安全驱动开发
      • 20_OPTEE-OS_应用之(五)终端密钥在线下发系统
    • Binary
      • 01_ELF文件_目标文件格式
      • 02_ELF文件结构_浅析内部文件结构
      • 03_ELF文件_静态链接
      • 04_ELF文件_加载进程虚拟地址空间
      • 05_ELF文件_动态链接
      • 06_Linux的动态共享库
      • 07_ELF文件_堆和栈调用惯例以ARMv8为例
      • 08_ELF文件_运行库(入口、库、多线程)
      • 09_ELF文件_基于ARMv7的Linux系统调用原理
      • 10_ELF文件_ARM的镜像文件(.bin/.hex/.s19)
    • Build
      • 01_Script_makefile_summary
    • Rust
      • 02_SYS_RUST_文件IO
    • Security
      • Crypto
        • 1.0_Security_计算机安全概述及安全需求
        • 2.0_Security_随机数(伪随机数)
        • 3.0_Security_对称密钥算法加解密
        • 3.1_Security_对称密钥算法之AES
        • 3.2_Security_对称密钥算法之MAC(CMAC/HMAC)
        • 3.3_Security_对称密钥算法之AEAD
        • 8.0_Security_pkcs7(CMS)_embedded
        • 9.0_Security_pkcs11(HSM)_embedded
      • Tools
        • Openssl EVP to implement RSA and SM2 en/dec sign/verify
        • 基于Mac Silicon M1 的OpenSSL 编译
        • How to compile mbedtls library on Linux/Mac/Windows
    • Embedded
      • eMMC启动介质
  • 😃Design
    • Secure Boot
      • JY Secure Boot Desgin
    • FOTA
      • [FOTA] Module of ECUs' FOTA unit design
        • [FOTA] Tech key point: OSTree Deployment
        • [FOTA] Tech key point: repositories role for onboard
        • [FOTA] Tech key point: metadata management
        • [FOTA] Tech key point: ECU verifying and Decrpting
        • [FOTA] Tech key point: time server
      • [FOTA] Local-OTA for Embedded Linux System
    • Provisioning
      • [X-Shield] Module of the Embedded Boards initialization
    • Report
由 GitBook 提供支持
在本页
  • 11_ARMv8_异常处理(二)- Legacy 中断处理
  • 1 Overview interrupt
  • 2 Example
  • 3 Reference
  1. TECH
  2. ARM
  3. ARM-v8-A

11_ARMv8_异常处理(二)- Legacy 中断处理

https://github.com/carloscn/blog/issues/49

上一页10_ARMv8_异常处理(一) - 入口与返回、栈选择、异常向量表下一页12_ARMv8_异常处理(三)- GICv1/v2中断处理

最后更新于1年前

11_ARMv8_异常处理(二)- Legacy 中断处理

异常处理还没有完成,在异常处理(一)里面算是把ARMV8a上面的一些比较基本的异常相关的知识罗列出来了,但是还没有具体的分析。在ARM的世界,中断是异常的一种。这一节的目标是利用Linux内核研究:

  • ARMv8架构下的中断处理过程?

  • Linux内核在ARMv8a的架构下中断是如何处理?

  • ARMv8a中断一些局限性,Linux内核的机制如何弥补?

  • Cortex-A72多核多CPU的中断协调问题?

  • Cortex-M33(armv8-m)与arm-v7a中断处理与armv8a的不同?

  • freeRTOS事件处理机制在Cortex-M33上的处理?

ARMv8的中断有两种模式可以配置,一种是传统的中断处理Legacy模式,一种是使用GIC中断管理器模式。我们这次目标是Legacy模式,下一节异常处理(三)会学习GIC中断管理。


1 Overview interrupt

image-20220416132633869

外部设备assert一个中断线,中断控制器会产生一个IRQ使CPU进入到异常处理流程,保存一些PC->ELR(子函数返回地址),保存PSTATE状态信息,还会切入EL1异常等级(栈指针),接着就是CPU和Kernel共同完成的中断向量表,根据中断向量表的地址进入到内核的异常handler,执行内核中断上下文。

1.1 Interrupt controller

1.1.1 RTL design

我并非研究ARM的RTL设计的人员,但是从RTL里面能给我们提供一些本质的信息。我们这里用ZYNQ设计的双核Cortex-A9为例,看看整个中断控制器如何工作的。

中断控制器的RTL设计如图所示,我在图片上标注了中断控制器的输入输出,输入一共是两个,分别是图中的source 1和source 2,一个是外部的通过外设引脚输入进来的中断源,一个是内部的CPU产生的中断源,通过或的关系合并成FIQ和IRQ两个中断输出,中断的输出被接入到CPU0和CPU1的接口。我们在CPU上能观测到的就是异常的产生。

  • CPU内部中断:Timer,AWDT

  • CPU外部中断:SPI

我们可以在PSTATE寄存器中控制IRQ和FIQ的开关。

1.1.2 Legacy interrupt controller

参考一些做CortexA72的二级产商的技术手册,NXP的和德州仪器TDA4VM,并没有设计相应的legacy中断,我以树莓派的Cortex A72(x4)为例,了解一下legacy interrupt怎么设计的,我们在异常处理(三)中研究一下不同厂商如何将GIC集成到自己的ARM处理器上的。

树莓派是这样做的,将中断线分为几类,ARM coreN(arm核心组中断),ARM_LOCAL(只有CPU能访问的中断), ARMC(CPU和GPU共享的中断),videocore(GPU的中断)和ETH_PCIe(网络PCIE中断)。

中断比较多,传统的中断寄存器就是使用串联的方法,待定寄存器三个FIQn/IRQn_PENDING1 + FIQn/IRQn_PENDING0,还有一个PENDING2,串联起来,相当于把ARMC和外设类的中断打包,共用一个位,最后组装成ARM_LOCAL FIQ/IRQ_SOURCEn寄存器。所以我们使用这些寄存器来控制和读取中断状态。

在树莓派的手册里面举了个例子,一个UART4的FIQ中断发送到ARM core3上的处理时序:

  • 进入到FIQ的handler

  • 读FIQ_SOURCE3

  • 判断FIQ_SOURCES3[8]为1, 就去读FIQ3_PENDING2

  • 判断FIQ3_PENDING2[25]为1,就去读FIQ3_PENDING1

  • 判定FIQ3_PENDING1[25]为1,就去读PACTL_CS[20:16]

  • 判断PACTRL_CS[17]为1,就去读UART4_MIS断定什么造成的中断。

这部分应该是中断上半段处理的内容。

1.2 CPU work

中断也是一种异常和异常处理(一)的处理方式一样,摘录于异常处理(一):

CPU自动做的事情:

  • S1: PSTATE保存到SPSR_ELx

  • S2: 返回地址PC保存到ELR_ELx

  • S3: PSTATE寄存器里面的DAIF域都设定为1(等同于关闭调试异常、SError,IRQ FIQ中断)

  • S4: 更新ESR_ELx寄存器(包含了同步异常发生的原因)

  • S5: SP执行SP_ELx

  • S6: 切换到对应的EL,接着跳转到异常向量表执行

操作系统需要做的事情:

  • 备份上面的寄存器到栈。

  • 识别异常发生的类型

  • 跳转到合适的异常向量表(包含异常跳转函数)

  • 处理异常

  • 操作系统执行eret指令

当中断发生的时候有两个现场需要保护:

  • CPU需要保护中断现场,CPU在EL0异常等级,将中断现场都保存在EL1的栈里面。

  • Kernel保护中断现场,发生在中断上半段。

这两个中断现场有个比较明显的分界线,就是是否跳转异常向量表。CPU的中断现场在跳转异常向量表之前,此时还没有进入到EL1,但是备份到EL1;Kernel的保护中断现场上半段,此时已经在CPU的EL1,栈已经切到了EL1的栈空间上面,保护程序逻辑的现场。

我们看一下CPU保护中断现场的工作,kernel保护中断现场放在1.4.1 top half来说。CPU发生异常的时候会自动把SP/PC/PSTATE这些数据备份到寄存器里面,但是这里有个问题,如果我们对这个数据不加以备份,当异常嵌套异常的时候,寄存器的值就会被冲走,我们只能恢复一级的异常,因此还要备份异常到相应的栈里面,当多级嵌套的异常从根节点一路路返回的时候,从栈中拿出寄存器的数据进行返回。这个好比QQ幻想里面的飞空艇仙子,龙城->长乐村->桃源村->天都,我们手里的信息只能回溯到上一站,等我们返回桃源村的时候,打开飞空艇仙子的对话框,就不知道去哪里了。因此我们每飞一个地方之后,需要在各个城市的飞空艇仙子的复印一个副本,告诉他我来自哪里,到天都之后我就知道回到桃源村,到桃源村的时候我就知道来自长乐村,最后我就能返回龙城。这就相当于中断现场的保护,在每一次中断过来之后,备份SP/PC/PSTATE到栈空间。

在entry.S中kernel_entry就是CPU保护中断现场的工作,当然处理保护中断现场,还有很多关于内存的指令,这里面先不做过多的关注。

REGS
STACK
Content

SPSR_EL1

pt.pstate

PSTATE

ELR_EL1

pt.sp

SP

LR

pt.regs[30]

LR

X29

pt.regs[29]

X29

X28-X0

pt.regs[28..0]

X28-X0

1.4 kernel:exception handler

这部分我们在Linux Kernel专题里面来讨论,在ARM64处理器这块就暂时不讨论了。

2 Example

对于Legacy中断模式,我们启动系统的定时器,在Cortex-A72处理器的树莓派4b上面实现一个定时器,需要自己完成寄存器的配置,异常向量表的跳转,寄存器的备份工作。

2.1 Timer Pre-knowledge

2.1.2 regs

Cortex-A72一共是有4个定时器:

定时器
ELx
安全模式
虚拟
中断源

PS定时器

EL1

安全模式

物理定时器

CNT_PS_IRQ

PNS定时器

EL1

非安全模式

物理定时器

CNT_PNS_IRQ

HP定时器

EL2

x

虚拟环境下的物理定时器

CNT_HP_IRQ

V定时器

EL1

x

虚拟定时器

CNT_V_IRQ

相关寄存器也在这里:

ame
Type
Reset
Width
Description

CNTKCTL_EL1

RW

32-bit

Timer Control register (EL1)

CNTFRQ_EL0

UNK

32-bit

Timer Counter Frequency register

CNTPCT_EL0

RO

UNK

64-bit

Physical Timer Count register

CNTVCT_EL0

RO

UNK

64-bit

Virtual Timer Count register

CNTP_TVAL_EL0

RW

UNK

32-bit

Physical Timer TimerValue (EL0)

CNTP_CTL_EL0

RW

32-bit

Physical Timer Control register (EL0)

CNTP_CVAL_EL0

RW

UNK

64-bit

Physical Timer CompareValue register (EL0)

CNTV_TVAL_EL0

RW

UNK

32-bit

Virtual Timer TimerValue register

CNTV_CTL_EL0

RW

32-bit

Virtual Timer Control register

CNTV_CVAL_EL0

RW

UNK

64-bit

Virtual Timer CompareValue register

CNTVOFF_EL2

RW

UNK

64-bit

Virtual Timer Offset register

CNTHCTL_EL2

RW

32-bit

Timer Control register (EL2)

CNTHP_TVAL_EL2

RW

UNK

32-bit

Physical Timer TimerValue register (EL2)

CNTHP_CTL_EL2

RW

32-bit

Physical Timer Control register (EL2)

CNTHP_CVAL_EL2

RW

UNK

64-bit

Physical Timer CompareValue register (EL2)

CNTPS_TVAL_EL1

RW

UNK

32-bit

Physical Timer TimerValue register (EL2)

CNTPS_CTL_EL1

RW

32-bit

Physical Secure Timer Control register (EL1)

CNTPS_CVAL_EL1

RW

UNK

64-bit

Physical Secure Timer CompareValue register (EL1)

4个通用定时器总中断设置是在ARM_LOCAL的中断组的寄存器中配置。到此,其实我读到这里就有个疑问了,根据手册,CPU的4个通用定时器是在ARM_Core的中断组的,ARM_LOCAL里面只有一个local timer并不是这4个ARM的通用定时器,为什么要在ARM_LOCAL中断组寄存器里面配置。后面看了legacy中断的路由信息可以注意到:

虽然几个定时器被分配到了ARMCore组,但是实际上是在ARM_LOCAL里面的寄存器处理的,这个设计真的是有点令人很迷惑。

2.1.2 config

定时器需要配置,根据常识都需要:

  • 配置时间,计数多久?

  • 中断子开关,中断总开关。

  • 开始计时

ARM: CNTP_CTRL_EL0

我们选定的是CNT定时器,也就是EL1里面的PS定时器。在手册里面,CNTP_CTL_EL0来配置,这里还有个奇怪的事情,就是PS定时器是EL1里面的,但是配置寄存器是在CNTP_CTL_EL0内配置的(我搜了一下手册,是没有CNTP_CTL_EL1的,可能配置都是EL0寄存器里面完成的)

CNTP_CTL_EL0, counter_timer physical timer control register.

bits
function

0: ENABLE

Enables the timer. 0 关闭,1打开

1: IMASK

interrupt 掩码位, 0不会被iMASK bit掩码;1会被 iMASK bit掩码

2: ISTATUS

定时器的状态,0定时器中断状态不满足,1定时器中断状态满足

timer_ps0_enable:
    ldr x4, =TIMER_CNTRL0_REG_ADDR
    mov x5, #2
    str x5, [x4]
    ret

ARM: CNTP_TVAL_EL0

Timervalue的初始值,这个值会递减到0的时候会触发中断,还需要在handler里面重新赋值。

#define ARM_LOCAL_REG_BASE_ADDR (0xFF800000)
#define TIMER_CNTRL0_REG_ADDR   (ARM_LOCAL_REG_BASE_ADDR + 0x40)

timer_ps0_enable:
    ldr x4, =TIMER_CNTRL0_REG_ADDR
    mov x5, #2
    str x5, [x4]
    ret

CortexA72: TIMER_CNTRLx

树莓派一共三个这样的寄存器,这个寄存器用于软件决定从ARM core接收一个FIQ的中断请求。我们关注的应该是BIT0,是cortex-A72处理器内核的PS定时器。

timer_ps0_set_value:
    msr cntp_tval_el0, x0
    ret

ARM: PSTATE

配置PSTATE上面的DAIF使能总的中断开关。

arch_enable_daif:
		msr	daifclr, #2
    ret

arch_disable_daif:
    msr	daifset, #2
    ret

2.1.3 process

// IRQ_SOURCE0_REG_ADDR
void irq_handle(void)
{
	unsigned int irq = 0;
	unsigned int regs = IRQ_SOURCE0_REG_ADDR;

	irq = readl(IRQ_SOURCE0_REG_ADDR);
	switch (irq) {
		case (CNT_PNS_IRQ):
		handle_timer_irq(100);
		break;

		default:
		printk("Unkown IRQ 0x%x\n", irq);
		break;
	}
}

void handle_timer_irq(unsigned int val)
{
	timer_ps0_set_value(val);
	printk("Core0 timer interrupt recved\n\r");
}

3 Reference

  1. BCM2711 ARM Peripherals

image-20220416134100235
image-20220416145105813

-

RW 

-

-

-

-

-

image-20220417123141473

😾
DRA829/TDA4VM Technical Reference Manual (Rev. C) - 9.1 Interrupt Architecture
ARM Cortex-A72 MPCore Processor Technical Reference Manual r0p3 - Generic Timer functional description
a
b
c
c
d
c
c