跳转到主要内容

ARM

聊聊ARM下的函数栈

editor /

这篇文章简要说说函数是怎么传入参数的,我们都知道,当一个函数调用使用少量参数(ARM上是少于等于4个)时,参数是通过寄存器进行传值(ARM上是通过r0,r1,r2,r3),而当参数多于4个时,会将多出的参数压入栈中进行传递(其实在函数调用过程中也会把r0,r1,r2,r3传递的参数压入栈),具体是什么实现的呢,我们看看。

<strong>函数栈</strong>
  
首先我们需要了解一下linux下一个进程的内存地址空间是如何布局的,在linux中,0~3G的虚拟地址为进程所有,3G~4G由内核所使用,每一个进程都有自己独立的0~3G内存地址空间。当进程进行函数调用时,我们都知道传入被调用函数的参数是通过栈进行操作的,这里我们只需要简单了解一下linux的内存地址空间中的栈是自顶向下生长的,就是栈底出于高地址处,栈顶出于低地址处。

ARM编译中的RO、RW和ZI DATA区段

kelly /

<strong>ARM程序(指在ARM系统中正在执行的程序,而非保存在ROM中的bin文件)的组成</strong>

一个ARM程序包含3部分:RO段,RW段和ZI段

RO是程序中的指令和常量

RW是程序中的已初始化变量

ZI是程序中的未初始化的变量

由以上3点说明可以理解为:

RO就是readonly,

RW就是read/write,

ZI就是zero

<strong>ARM映像文件的组成</strong>

所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。

Image文件包含了RO和RW数据。

之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。

Q:为什么Image中必须包含RO和RW?

A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。

<strong>ARM程序的执行过程</strong>

ARM64的启动过程之(三):为打开MMU而进行的CPU初始化

kelly /

作者:linuxer

<strong>一、前言</strong>

上一节主要描述了为了打开MMU而进行的Translation table的建立,本文延续之前的话题,主要是进行CPU的初始化(注:该初始化仅仅为是为了turn on MMU)。

本文主要分析ARM64初始化过程中的__cpu_setup函数,代码位于arch/arm64/mm/proc.S中。主要的内容包括:

1、cache和TLB的处理

2、Memory attributes lookup table的构建

3、SCTLR_EL1、TCR_EL1的设定

<strong>二、cache和TLB的处理</strong>

<strong>1、oerview</strong>

一颗ARM架构芯片的软硬件构成

kelly /

作者:suipingsp

硬件和软件是一颗芯片系统互相依存的两大部分,本文总结了一颗芯片的软硬件组成,作为对芯片的入门级概括吧。

<strong>(一)硬件</strong>

主控CPU:运算和控制核心。基带芯片基本构架采用微处理器+数字信号处理器(DSP)的结构,微处理器是整颗芯片的控制中心,会运行一个实时嵌入式操作系统(如Nucleus PLUS),DSP子系统负责基带处理。应用处理器则可能包括多颗微处理器,还有GPU。微处理器是ARM的不同系列的产品(也可以是x86架构),可以是64位或者32位。处理器内部通过“内部总线”将CPU所有单元相连,其位宽可以是8-64位。

总线:计算机的总线按功能可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。CPU内部部件由内部总线互联,外部总线则是CPU、内存、输入、输出设备传递信息的公用通道,主机的各个部件通过总线相连接。外部设备通过相应的接口电路再与外部总线相连接,从而形成了硬件系统。外部总线通过总线接口单元BLU与CPU内部相连。

ARM64的启动过程之(二):创建启动阶段的页表

kelly /

作者:linuxer

<strong>一、前言</strong>

本文主要描述了ARM64启动过程中,如何建立初始化阶段页表的过程。我们知道,从bootloader到kernel的时候,MMU是off的(顺带的负作用是无法打开data cache),为了提高性能,加快初始化速度,我们必须某个阶段(越早越好)打开MMU和cache,而在此之前,我们必须要设定好页表。

在初始化阶段,我们mapping三段地址,一段是identity mapping,其实就是把物理地址mapping到物理地址上去,在打开MMU的时候需要这样的mapping(ARM ARCH强烈推荐这么做的)。第二段是kernel image mapping,内核代码欢快的执行当然需要将kernel running需要的地址(kernel txt、dernel rodata、data、bss等等)进行映射了,第三段是blob memory对应的mapping。

对ARM异常(Exceptions)的理解

kelly /

ARM支持7种异常。问题时发生了异常后ARM是如何响应的呢?下面一起来学习一下:

所有的系统引导程序前面中会有一段类似的代码,如下:

<center><img src="http://mm32.eetrend.com/files/2016-09/wen_zhang_/100002849-9339-8ecbff5…; alt=""></center>

从中我们可以看出,ARM支持7种异常。问题时发生了异常后ARM是如何响应的呢?第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么怎么会执行到后面几个异常处理函数呢?

看看书后,明白了ARM对异常的响应过程,于是就能够回答以前的这个疑问。

当一个异常出现以后,ARM会自动执行以下几个步骤:

使用Keil判断ARM的冷启动和热启动的方法

kelly /

常把单片机系统的复位分为冷启动和热启动。所谓冷启动,也就是一般所说的上电复位,冷启动后片内外RAM的内容是随机的,通常是0x00或0xFF;单片机的热启动是通过外部电路给运行中的单片机的复位端一复位电平而实现的,也就是所说的按键复位或看门狗复位。复位后,RAM的内容都没有改变。在某些场合,必须区分出设备的重启是热重启还是冷重启。常用的方法是:确定某内存单位为标志位(如0x40003FF4~0x40003FF7 RAM单元),启动时首先读该内存单元的内容,如果它等于一个特定的值(例如为0xAA55AA55),就认为是热启动,否则就是冷启动。

根据以上的设计思路思路定义一个变量:

uint32 unStartFlag;

在程序启动时判断:

if(unStartFlag==0xAA55AA55)

{

//热启动处理

}

else

{

//冷启动处理

unStartFlag=0xAA55AA55;

}

ARM64的启动过程之(一):内核第一个脚印

kelly /

作者:linuxer

<strong>一、前言</strong>

kernel的整个启动过程涉及的内容很多,不可能每一个细节都描述清楚,因此我打算针对部分和ARM64相关的启动步骤进行学习、整理,并方便后续查阅。本文实际上描述在系统启动最开始的时候,bootloader和kernel的交互以及kernel如何保存bootloader传递的参数并进行校验,此外,还有一些最基础的硬件初始化的内容。
本文中的source来自4.1.10内核,这是一个long term的版本,后续一段时间的文章都会基于这个long term版本进行。

<strong>二、进入kernel之前</strong>

系统启动过程中,linux kernel不是一个人在战斗,在kernel之前bootloader会执行若干的动作,然后把控制权转移给linux kernel。需要特别说明的是:这里bootloader是一个宽泛的概念,其实就是为kernel准备好执行环境的那些软件,可能是传统意义的bootloader(例如Uboot),也可能是Hypervisor或者是secure monitor。具体bootloader需要执行的动作包括:

ARM WFI和WFE指令

kelly /

<strong>1. 前言</strong>

蜗蜗很早以前就知道有WFI和WFE这两个指令存在,但一直似懂非懂。最近准备研究CPU idle framework,由于WFI是让CPU进入idle状态的一种方法,就下决心把它们弄清楚。

WFI(Wait for interrupt)和WFE(Wait for event)是两个让ARM核进入low-power standby模式的指令,由ARM architecture定义,由ARM core实现。听着挺简单,但怎么会有两个指令?它们的区别是什么?使用场景是什么?深究起来,还挺有意思,例如:能想象WFE和spinlock的关系吗?

<strong>2. WFI和WFE</strong>

<strong>1)共同点</strong>

WFI和WFE的功能非常类似,以ARMv8-A为例(参考DDI0487A_d_armv8_arm.pdf的描述),主要是“将ARMv8-A PE(Processing Element, 处理单元)设置为low-power standby state”。

基于ARM的十一种嵌入式操作系统概述

kelly /

嵌入式操作系统(Embedded Operation System,EOS)是指用于嵌入式系统的操作系统。嵌入式系统分为4层,硬件层、驱动层、操作系统层和应用层。嵌入式操作系统是负责嵌入式系统的全部软、硬件资源的分配、任务调度,控制、协调并发活动。它必须体现其所在系统的特征,能够通过装卸某些模块来达到系统所要求的功能,是一种用途广泛的系统软件。

<strong>嵌入式LINUX</strong>

嵌入式Linux 是将日益流行的Linux操作系统进行裁剪修改,使之能在嵌入式计算机系统上运行的一种操作系统。Linux做嵌入式的优势,首先,Linux是开放源代码;其次,Linux的内核小、效率高,可以定制,其系统内核最小只有约134KB;第三,Linux是免费的OS,Linux还有着嵌入式操作系统所需要的很多特色,突出的就是Linux适应于多种CPU和多种硬件平台而且性能稳定,裁剪性很好,开发和使用都很容易。同时,Linux内核的结构在网络方面是非常完整的,Linux对网络中最常用的TCP/IP协议有最完备的支持。提供了包括十兆、百兆、千兆的以太网络,以及无线网络,Token Ring(令牌环网)、光纤甚至卫星的支持。