Arm Linux 内存管理(一)————开启MMU

2023-11-09

首先我们根据vmlinux.lds可以找到内核入口函数为 stext,我们就直接从stext开始,主要干了几件事情

1.safe_svcmode_maskall r9			    //设置CPU运行模式为SVC,并关中断  
2.bl	__vet_atags	                    //验证atags或者dtb是否有效 
3.bl	__create_page_tables            //创建临时映射        
4.b	__enable_mmu			            //使能mmu             
5.__mmap_switched                       //初始化堆栈,并进入start_kernel

一个个来看:

.macro safe_svcmode_maskall reg:req
	setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, \reg
.endm

.macro	setmode, mode, reg
	msr	cpsr_c, #\mode
.endm

这里把很多条件编译给去除掉了,其实就是将CPSR寄存器FIQ和IRQ位置0,以及设置成SVC模式。

__vet_atags:
	tst	r2, #0x3			@ aligned?
	bne	1f

	ldr	r5, [r2, #0]
#ifdef CONFIG_OF_FLATTREE
	ldr	r6, =OF_DT_MAGIC		@ is it a DTB?
	cmp	r5, r6					//是不是DTB
	beq	2f
#endif
	cmp	r5, #ATAG_CORE_SIZE		@ is first tag ATAG_CORE?
	cmpne	r5, #ATAG_CORE_SIZE_EMPTY
	bne	1f
	ldr	r5, [r2, #4]
	ldr	r6, =ATAG_CORE
	cmp	r5, r6
	bne	1f

2:	ret	lr				@ atag/dtb pointer is ok

1:	mov	r2, #0
	ret	lr
ENDPROC(__vet_atags)

首先判断是不是DTB(根据头部MAGIC判断),如果是则返回,否则,判断是否为ATAG,如果是则返回,否则将r2寄存器清0,表示既不是DTB也不是ATAG。

__create_page_tables:        //页表映射初始化
    pgtbl	r4, r8				@ page table address

	/*
	 * Clear the swapper page table
	 */
	mov	r0, r4                       
	mov	r3, #0
	add	r6, r0, #PG_DIR_SIZE            
1:	str	r3, [r0], #4					//页表映射清0
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6
	bne	1b
__create_page_tables:        //恒等映射
    ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

	/*
	 * Create identity mapping to cater for __enable_mmu.
	 * This identity mapping will be removed by paging_init().
	 */
	adr	r0, __turn_mmu_on_loc								//这一段做恒等映射
	ldmia	r0, {r3, r5, r6}
	sub	r0, r0, r3			@ virt->phys offset
	add	r5, r5, r0			@ phys __turn_mmu_on
	add	r6, r6, r0			@ phys __turn_mmu_on_end
	mov	r5, r5, lsr #SECTION_SHIFT
	mov	r6, r6, lsr #SECTION_SHIFT

1:	orr	r3, r7, r5, lsl #SECTION_SHIFT	@ flags + kernel base
	str	r3, [r4, r5, lsl #PMD_ORDER]	@ identity mapping
	cmp	r5, r6
	addlo	r5, r5, #1			@ next section
	blo	1b
__create_page_tables:       //代码段,数据段等映射(PAGE_OFFSET ------_end-1)
    add	r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)		
	ldr	r6, =(_end - 1)											
	orr	r3, r8, r7
	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)		
1:	str	r3, [r0], #1 << PMD_ORDER				
	add	r3, r3, #1 << SECTION_SHIFT		
	cmp	r0, r6
	bls	1b
__create_page_tables:               //DTB映射
    mov	r0, r2, lsr #SECTION_SHIFT										//DTB进行映射
	movs	r0, r0, lsl #SECTION_SHIFT								//物理地址 20位对齐
	subne	r3, r0, r8												//RAM偏移
	addne	r3, r3, #PAGE_OFFSET									//虚拟地址
	addne	r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)			//对应页表地址
	orrne	r6, r7, r0												//实际要写入页表的东西
	strne	r6, [r3], #1 << PMD_ORDER								//写入
	addne	r6, r6, #1 << SECTION_SHIFT								//下一个1m空间
	strne	r6, [r3]												//写入

1.页表映射初始化 :

       r8寄存器代表的是phys_offset,也就是实际的(物理地址-虚拟地址)的偏移,通过pgtbl r4 r8可以得到swapper_pg_dir 的物理地址。然后按4字节对其进行清0,直至大小超过#PG_DIR_SIZE为止。

2.恒等映射 :

       后面需要打开MMU,打开MMU后要使用的就是虚拟地址了,而之前的PC都是用的物理地址,可能会出现错误,所以在这一段代码段(__turn_mmu_on----__turn_mmu_on_end )需要做一个恒等映射(即物理地址与虚拟地址相同),这样打开MMU时也不会出现问题。首先需要了解在这一阶段里,内存采用的是段式管理,基地址(12bit)+偏移地址(20bit),也就是说4GB的空间分段管理,每段1MB,共4k段,所以页表大小就是4k*4 = 16k。

       通过system.map我们可以得到 __turn_mmu_on的虚拟地址0xc0100000  __turn_mmu_on_end 的虚拟地址 0xc0100020,假设物理偏移是0xc0000000   r5 = 0x00100000 , r6 = 0x00100020  偏移地址是20位所以右移20位可以得到r5 = 0x001,r6 = 0x001,r5,r6其实在一个段中,所以我们知道我们需要写到页表(0x00000400)的第1项,每项都是4字节,所以第一项地址就是 0x00000404,将对应物理地址以及mmu的flag写入到该页表项,如果大小超过1M,那么重复该过程,直至r5与r6相等。

3:代码段,数据段等映射(PAGE_OFFSET ------_end-1)

        这一步和第2步类似,根据虚拟地址找到对应的表项,再在表项中写入对应的地址即可。

4.DTB映射:

        DTB不超过1M,所以这里就固定进行2段映射区。

这样所有的临时映射就已经都完成了,接下来就可以打开mmu了。

__enable_mmu:
#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6
	orr	r0, r0, #CR_A
#else
	bic	r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
	bic	r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
	bic	r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
	bic	r0, r0, #CR_I
#endif
#ifdef CONFIG_ARM_LPAE
	mcrr	p15, 0, r4, r5, c2		@ load TTBR0
#else
	mov	r5, #DACR_INIT
	mcr	p15, 0, r5, c3, c0, 0		@ load domain access register
	mcr	p15, 0, r4, c2, c0, 0		@ load page table pointer			//页表物理地址的基地址
#endif
	b	__turn_mmu_on
ENDPROC(__enable_mmu)

ENTRY(__turn_mmu_on)
	mov	r0, r0
	instr_sync
	mcr	p15, 0, r0, c1, c0, 0		@ write control reg				//打开mmu
	mrc	p15, 0, r3, c0, c0, 0		@ read id reg
	instr_sync
	mov	r3, r3
	mov	r3, r13										//__mmap_switched
	ret	r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)

1.设置页表基地址 :

        CP15中的寄存器C2保存的是页表的基地址,即一级映射描述符表的基地址。代码中就是把r4 写到cp15的c2中,r4前面也分析过了,就是swapper_pg_dir 的物理地址。

2.打开MMU :

       CP15的寄存器C1中的M位,控制是否打开MMU,前面已经设置好了r0寄存器,所以只需要将r0写入cp15的C1中即可,这里省略了很多打开前的操作,如果有兴趣可以自行去分析源码。

__mmap_switched:
	adr	r3, __mmap_switched_data

	ldmia	r3!, {r4, r5, r6, r7}
	cmp	r4, r5				@ Copy data segment if needed		//从存储地址拷贝数据到数据段起始地址
1:	cmpne	r5, r6
	ldrne	fp, [r4], #4
	strne	fp, [r5], #4
	bne	1b

	mov	fp, #0				@ Clear BSS (and zero fp)			//bss段数据为0
1:	cmp	r6, r7
	strcc	fp, [r6],#4
	bcc	1b

 ARM(	ldmia	r3, {r4, r5, r6, r7, sp})
 THUMB(	ldmia	r3, {r4, r5, r6, r7}	)
 THUMB(	ldr	sp, [r3, #16]		)
	str	r9, [r4]			@ Save processor ID					//保存 一些信息 后续要用  cpu_id
	str	r1, [r5]			@ Save machine type					//machine id
	str	r2, [r6]			@ Save atags pointer				//dtb地址
	cmp	r7, #0
	strne	r0, [r7]			@ Save control register values
	b	start_kernel											//进入c语言启动kernel
ENDPROC(__mmap_switched)

1.拷贝data段数据:

          判断__data_loc和_sdata的地址是否相同,如果不相同则需要将__data_loc的数据拷贝到_sdata中来。如果相同,则表明已经拷贝过了,那就跳过。

2.bss段清0:

         将bss段数据清0。

3.保存一些信息到寄存器:

         将cpu_id machine_id 以及dtb地址写入对应的寄存器,在后续进入到start_kernel还会用到,所以需要保存。

4.进入start_kernel

         终于到了我们熟悉的函数了,这之后就是C语言的启动的kernel了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Arm Linux 内存管理(一)————开启MMU 的相关文章

  • 剑指 Offer 14- I. 剪绳子 数学法+动态规划

    剑指 Offer 14 I 剪绳子 难度 中等 题目描述 解题思路 1 数学方法 记不清在哪里好像做过这道题 在所有的分法里 每次以3为一段能得到最大的结果 比如9 分成33的时候是最大的 计算数字除以3的余数 如果余1 就和其中一个三组合
  • DETRs Beat YOLOs on Real-time Object Detection

    目录 1 模型架构 1 1高效混合编码器 1 1 1 尺度内特征交互模块AIFI 1 1 2 跨尺度特征融合CCFM 1 2IoU感知查询选择 总结 DETRs在实时目标检测中击败YOLO 问题 DETR的高计算成本 实时检测效果有待提高
  • ES6之map()方法

    map 方法 map 映射 即原数组映射成一个新的数组 map方法接受一个新参数 这个参数就是将原数组变成新数组的映射关系 function myfun 1 arr var array arr map item gt array push
  • unity2019导入leapmotion插件显示SpatialTracking在unity Engine中缺少相应的头文件

    unity2019导入leapmotion插件显示SpatialTracking在unity Engine中缺少相应的头文件 1 问题描述 2 问题解决方法 1 2 3 插入这个插件 4 问题就可以解决了 注 2019将很多插件需要自己选择
  • RabbitMQ--扩展--10--消息追踪

    RabbitMQ 扩展 10 消息追踪 1 介绍 1 1 RabbitMQ 消息异常丢失的情况 可能是生产者与Broker断开了连接并且也没有任何重试机制 可能是消费者在处理消息时发生了异常 不过却提前进行了ack 可能是交换机并没有与任何
  • js如何实现网站内容禁止复制和粘贴、另存为?

    1 使右键和复制失效 方法1 在网页中加入以下代码 代码如下 方法2 在中加入以下代码
  • 四个复制就能用的炫酷网页特效

    第一个 炫酷星空
  • springboot的基本配置

    server port 8086 spring profiles active dev application name my springsecurity plus datasource driver driver class name
  • 【鸿蒙】日志工具的使用

    鸿蒙 Harmony应用开发 目录导航 在前面 鸿蒙 创建你的第一个Harmony项目我们已经能够顺利的编译并运行我们的第一个程序hello world 为了今后能走的更远 我们现在需要点基础的装备 日志工具 学会了日志工具的使用方法 会对
  • kubectl proxy 命令使用

    kubectl proxy address 192 168 8 220 accept hosts localhost 127 0 0 1 1 accept paths
  • 【语义分割】8、Self-Regulation for Semantic Segmentation

    文章目录 一 背景 二 动机 三 方法 SR F Loss Shallow to Deep SR L Loss Deep to Shallow 四 效果 论文地址 https arxiv org pdf 2108 09702 pdf 代码地
  • Spingboot 多模块引入第三方jar包

    1 在需要的模块中引入jar包 2 在此模块中的pom xml 中引用 3 要想打包部署服务器 需要在启动模块中添加配置信息 ps 启动模块要引用此模块才能将此一起jar打包部署
  • 开源的虚拟化私有云及云管平台

    免费开源的私有云及云管平台来了 除虚拟化外 还支持纳管主流的 9 大公有云及私有云平台 欢迎大家安装体验 能解决哪些问题 将几台物理服务器虚拟化成一个私有云平台 需要一个紧凑而且功能相对完整的物理机全生命周期管理工具 将 VMware vS
  • bottle 文件服务器,python bottle 框架基础教程:文件上传

    文件上传 需要注意的是前端html的form表单中 要添加 enctype multipart form data 属性 否则无法上传文件 在后端 用request files方法 获取到表单传上来的文件 首先把对象赋值给一个变量名 如up
  • Android开发过程中的一些问题

    1 Can t toast on a thread that has not called Looper prepare 就是Android子线程中不能直接使用Toast显示提示信息的问题 加入Looper prepare 和Looper
  • java static 静态方法的使用 注意事项

    定义为 static function1 function1 中不能引用this或super 在android 中这样用的话 会出现以下的报错 W dalvikvm 2783 JNI WARNING instance fieldID 0x5
  • DC-6靶机渗透测试

    文章目录 DC 6靶机渗透测试 方式一 1 信息收集 1 1 主机扫描 1 2 端口扫描 1 3 目录扫描 2 暴力破解 3 漏洞利用 4 提权 方式二 1 漏洞利用 方式三 1 漏洞利用 方式四 1 漏洞利用 DC 6靶机渗透测试 方式一
  • MySQL 数据库————连接查询

    目录 一 多表连接查询 1 内连接 inner join 2 左连接 left join 3 右连接 right join 二 存储过程 1 简介 2 优点 3 语法 参数分类 带参数的存储过程 修改存储过程 删除存储过程 一 多表连接查询
  • VS2019安装和使用(C语言)

    VS2019视频 VS2022视频 博主看视频做的笔记 视频讲解比较详细更容易上手 一 下载 VS2019官网下载 分为三个版本 个人用户下载Community 2019足够了 登录微软账号免费 点击下载后跳转新页面可能要等一会才能弹出下载
  • 线性代数的深入理解

    线性代数笔记 关于矩阵理解 reference 矩阵理解 图片来源 b站上的教程 线性变换 所谓变换 其实就是空间里从一个点 元素 对象 到另一个点 元素 对象 的跃迁 矩阵是线性空间中的线性变换的一个描述 在一个线性空间中 只要我们选定一

随机推荐

  • 可正反插USB-C双口转HDMI/VGA带PD快充音视频产品设计方案AG9321MCQ设计参考电路

    ALGOLTEK AG9321MCQ系列为HDMI和VGA转换器提供USB C型 显示端口备用 的单片机解决方案 并提供电源传输 AG9321MCQ系列支持带片上Rp Rd的双USB C型插座 符合USB电源传输规范3 0 集成的10位AD
  • 【数据结构与算法】2、链表(简单模拟 Java 中的 LinkedList 集合,反转链表面试题)

    目录 一 链表基本概念和基本代码实现 二 链表 动态数组整合 面向接口编程 三 clear 四 add int index E element 1 找到 index 位置的节点 2 get int index 和 set int index
  • 前端入坑(四)--------react(如何从API获取数据)

    Hey Welcome to day 4 Hopefully you ve had time to follow along and do the exercises from the last few days And if I ve d
  • GIT : 记录IntelliJ IDEA 合并冲突时的一个bug(冲突解决后代码和本地仓库一样时导致merge失败)

    目录 目录 IntelliJ IDEA版本 问题描述 解决办法 IntelliJ IDEA版本 IntelliJ IDEA 2017 1 4 x64 问题描述 我们在用git开发是经常遇到冲突的情况 一般发生在协同开发时 一个文件被两个人同
  • Unity-Transform.eulerAngles

    Description The rotation as Euler angles in degrees The x y and z angles represent a rotation z degrees around the z axi
  • ReactJS之属性和状态的对比

    1 都是纯JS对象 2 都会触发render的执行 3 都有确定性 状态 state 是由组件本身进行维护和修改的 组件外部是无法进行修改的 属性 props 组件自身具有的特性 是无法被自身修改的 但是父组件是可以修改子组件的属性 子组件
  • 贝叶斯网络是神经网络吗,贝叶斯网络和神经网络

    深度信念网络与深度贝叶斯网络有什么区别 1 贝叶斯网络是 一种概率网络 它是基于概率推理的图形化网络 而贝叶斯公式则是这个概率网络的基础 贝叶斯网络是基于概率推理的数学模型 所谓概率推理就是通过一些变量的信息来获取其他的概率信息的过程 基于
  • 内网穿透NPS及NPC搭建(使用docker实现)

    客户端及服务端下载 NPS 1 启动NPS服务器容器 端口映射需要注意 docker run td rm p 10180 8080 p 10124 8024 p 10150 10179 10150 10179 name nps q01231
  • 遥感NDVI估算植被覆盖度

    遥感NDVI估算植被覆盖度 植被覆盖度是指植被 包括叶 茎 枝 在地面的垂直投影面积占统计区总面积的百分比 容易与植被覆盖度混淆的概念是植被盖度 植被盖度是指植被冠层或叶面在地面的垂直投影面积占植被区总面积的比例 两个概念主要区别就是分母不
  • php+redis实现对200w用户的即时推送服务

    欢迎加入 新群号码 99640845 怎么实现对200w用户的即时推送 这个推送可以理解为调用第三方的接口 push sms之类的东西 当时先写了一个demo 直接读取DB然后单个推送 结果 可想而知 于是设计一套基于redis php多进
  • EfficientNet的解读与Tensorflow 2.0实现

    EfficientNet论文解读 Efficient Net是Google在2019年发表的一篇论文 系统的研究了如何在给定资源的条件下 如何平衡扩展网络的深度 广度以及图像的分辨率这三者的关系 来取得最好的图像识别精度 作者提出了一种新的
  • part1:推荐一些适合练手、课程设计、毕业设计的python小项目源码,无任何下载门槛

    人生苦短 我用python 随着python这些年的流行 很多人开始使用python来实现各种功能 下面推荐一些适合用来练手 大学生课程设计作业 大学生毕业设计的python小项目 尤其适合新手 源码 说明文档 打包后的exe文件 都已经被
  • MCP2515独立控制器

    1 简介 MCP2515 是一款独立控制器局域网络 Controller Area Network CAN 协议控制器 完全支持 CAN V2 0B 技术规范 该器件能发送和接收标准和扩展数据帧以及远程帧 MCP2515 自带的两个验收屏蔽
  • GPU pytorch 1.4.0 cuda 10.1 安装

    安装版本 pytorch 1 4 0 torchvision 0 5 0 cudatoolkit 10 1 pytorch官网 第一步 安装 conda 镜像通道 conda config add channels https mirror
  • QT5开发

    摘要 Qt5主窗口是大部分Qt应用使用的基本界面 常见应用都会通过对主窗口进行界面布局来实现 一 QT5主窗口构成 1 基本元素 QMainWindow是一个为用户提供主窗口程序的类 包含一个菜单栏 menubar 多个工具栏 tool b
  • SpringMvc学习-2-Spring MVC 的核心组件

    Spring MVC 的核心组件 DispatcherServlet 核心处理器 也叫前端控制器 负责调度其他组件的执行 可降低不同组件之间的耦合性 是整个 Spring MVC 的核心模块 Handler 处理器 完成具体业务逻辑 相当于
  • Java 8: 从永久代(PermGen)到元空间(Metaspace)

    正如大家所知 JDK 8 Early Access版已经提供下载 这使开发者可以体验Java8的新特性 其中之一 是Oracle从JDK7发布以来就一直宣称的要完全移除永久代空间 例如 字符串内部池 已经在JDK7中从永久代中移除 JDK8
  • STM32题目项目汇总 - 100例

    文章目录 1前言 2 STM32 毕设课题 3 如何选题 3 1 不要给自己挖坑 3 2 难度把控 3 3 如何命名题目 1前言 更新单片机嵌入式选题后 不少学弟学妹催学长更新STM32和C51选题系列 感谢大家的认可 来啦 以下是学长亲手
  • 一点小记录

    看到一篇介绍vue plugin pages 很好的文章 懒癌福利 一种全新的路由组织方式 基于 vite 的插件介绍 他还有个github地址 try vite plugins 这里截图记录一下
  • Arm Linux 内存管理(一)————开启MMU

    首先我们根据vmlinux lds可以找到内核入口函数为 stext 我们就直接从stext开始 主要干了几件事情 1 safe svcmode maskall r9 设置CPU运行模式为SVC 并关中断 2 bl vet atags 验证