linux内核 noreturn,读《ARM Linux 内核源代码剖析》.......第13章 setup_processor()
setup_processor首先是查找保存相应处理器信息的结构体,然后根据结构体里的值,对处理器相关的各种变量进行设置。
setup_processor
static void __init setup_processor(void)
{
struct proc_info_list *list;
之前已讲过,proc_info_list是包含处理器信息的结构体。
list = lookup_processor_type(read_cpuid_id());
利用读出(通过read_cpuid_id,后面有进一步说明)的CPU
ID,在lookup_processor_type(后面有进一步说明)内查找与该CPU对应的proc_info_list结构体。
if (!list) {
printk("CPU configuration botched (ID x), unable "
"to continue.\n", read_cpuid_id());
while (1);
}
cpu_name = list->cpu_name;
下面几句根据proc_info_list中的值,设置cpu_name、processor、cpu_tlb、cpu_user、cpu_cache这几个全局变量。
#ifdef MULTI_CPU
processor = *list->proc;
#endif
#ifdef MULTI_TLB
cpu_tlb = *list->tlb;
#endif
#ifdef MULTI_USER
cpu_user = *list->user;
#endif
#ifdef MULTI_CACHE
cpu_cache = *list->cache;
#endif
printk("CPU: %s [x] revision %d (ARMv%s), cr=lx\n",
cpu_name, read_cpuid_id(), read_cpuid_id()
& 15,
proc_arch[cpu_architecture()], cr_alignment);
打印一些信息,这里通过cpu_architecture(后面有进一步说明)获取了CPU架构的信息
(这里输出的一个实例:CPU: ARMv7 Processor [411fc283] revision 3 (ARMv7),
cr=10c5387f )
sprintf(init_utsname()->machine, "%s%c",
list->arch_name, ENDIANNESS);
sprintf(elf_platform, "%s%c", list->elf_name,
ENDIANNESS);
elf_hwcap = list->elf_hwcap;
#ifndef CONFIG_ARM_THUMB
elf_hwcap &= ~HWCAP_THUMB;
#endif
cacheid_init();
获取处理器的缓存类型,并将其保存到全局变量cacheid。
cpu_proc_init();
调用proc_info_list
中的_proc_init函数
}
read_cpuid_id 查找CPU ID
#define CPUID_ID0
static inline unsigned int __attribute_const__
read_cpuid_id(void)
{
return read_cpuid(CPUID_ID);
调了下面这个函数
}
#ifdef CONFIG_CPU_CP15
#define read_cpuid(reg)\
({\
unsigned int __val;\
asm("mrcp15, 0, %0, c0, c0, " __stringify(reg)\
返回CP15协处理器的c0寄存器的值(c0是主ID寄存器,具有保存处理器信息的设备ID代码),这句话实际就是mrcp15,
0, %0, c0, c0,0
: "=r"
(__val)\
:\
:
"cc");\
__val;\
})
#else
extern unsigned int processor_id;
#define read_cpuid(reg) (processor_id)
如果没有协处理器,会直接用全局变量processor_id的值
#endif
lookup_processor_type 查找处理器信息
ENTRY(lookup_processor_type)
stmfdsp!, {r4 - r7, r9, lr}
为在C代码中调用而添加
movr9, r0
bl__lookup_processor_type
之前学过,会调用这个标签,获取proc_info_list的指针
movr0, r5
ldmfdsp!, {r4 - r7, r9, pc}
ENDPROC(lookup_processor_type)
cpu_architecture
查找CPU架构信息
在通过read_cpuid_id获取CPU
ID后,与能够区分CPU架构的值进行AND运算,以寻找CPU的结构
int cpu_architecture(void)
{
int cpu_arch;
if ((read_cpuid_id() & 0x0008f000) == 0) {
cpu_arch = CPU_ARCH_UNKNOWN;
} else if ((read_cpuid_id() & 0x0008f000) ==
0x00007000) {
cpu_arch = (read_cpuid_id() & (1
<< 23))
? CPU_ARCH_ARMv4T: CPU_ARCH_ARMv3;
} else if ((read_cpuid_id() & 0x00080000) ==
0x00000000) {
cpu_arch = (read_cpuid_id() >> 16)
& 7;
if (cpu_arch)
cpu_arch += CPU_ARCH_ARMv3;
} else if ((read_cpuid_id() & 0x000f0000) ==
0x000f0000) {
unsigned int mmfr0;
asm("mrcp15, 0, %0, c0, c1, 4"
: "=r"
(mmfr0));
if ((mmfr0 & 0x0000000f) == 0x00000003 ||
(mmfr0
& 0x000000f0) == 0x00000030)
cpu_arch = CPU_ARCH_ARMv7;
else if ((mmfr0 & 0x0000000f) == 0x00000002
||
(mmfr0 & 0x000000f0) ==
0x00000020)
cpu_arch = CPU_ARCH_ARMv6;
else
cpu_arch = CPU_ARCH_UNKNOWN;
} else
cpu_arch = CPU_ARCH_UNKNOWN;
return cpu_arch;
}
可能找到的CPU架构信息为
#define CPU_ARCH_UNKNOWN0
#define CPU_ARCH_ARMv31
#define CPU_ARCH_ARMv42
#define CPU_ARCH_ARMv4T3
#define CPU_ARCH_ARMv54
#define CPU_ARCH_ARMv5T5
#define CPU_ARCH_ARMv5TE6
#define CPU_ARCH_ARMv5TEJ7
#define CPU_ARCH_ARMv68
#define CPU_ARCH_ARMv79
结合第一个标题,这个架构值被用来索引proc_arch
数组(proc_arch[cpu_architecture()]),返回:
static const char *proc_arch[] = {
"undefined/unknown",
"3",
"4",
"4T",
"5",
"5T",
"5TE",
"5TEJ",
"6TEJ",
"7",
"?(11)",
"?(12)",
"?(13)",
"?(14)",
"?(15)",
"?(16)",
"?(17)",
};
被用来打印(ARMv%s),表示arm的版本。
utsname结构体
提供内核版本和发布信息等
比如
struct new_utsname {
char sysname[65];
一般是Linux
char nodename[65];
char release[65];
比如2.6.35-28-genetic
char version[65];
char machine[65];
比如armv7l
char domainname[65];
};
在应用程序中可以用uname函数访问内核中的utsname信息。平时用的uname -a,估计也是这么搞的。
cacheid_init 查找处理器的缓存类型
看起来有些复杂,其实作用很简单:查找处理器的缓存类型,并保存到全局变量cacheid
static void __init cacheid_init(void)
{
unsigned int cachetype = read_cpuid_cachetype();
其实读出来的还是CP15的c0寄存器。只是和CPU ID不同的是,下面将使用不同的位。
unsigned int arch = cpu_architecture();
if (arch >= CPU_ARCH_ARMv6) {
将区分v6版本以上和以下,v6以上的将比较cachetype的值,然后给cacheid赋不同的值。
if ((cachetype & (7
<< 29)) == 4
<< 29) {
cacheid = CACHEID_VIPT_NONALIASING;
if ((cachetype & (3
<< 14)) == 1
<< 14)
cacheid |= CACHEID_ASID_TAGGED;
} else if (cachetype & (1
<< 23))
cacheid = CACHEID_VIPT_ALIASING;
else
cacheid = CACHEID_VIPT_NONALIASING;
} else {
cacheid = CACHEID_VIVT;
v6以下一律CACHEID_VIVT
}
printk("CPU: %s data cache, %s instruction cache\n",
cache_is_vivt() ? "VIVT" :
cache_is_vipt_aliasing() ? "VIPT aliasing" :
cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" :
"unknown",
cache_is_vivt() ? "VIVT" :
icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" :
cache_is_vipt_aliasing() ? "VIPT aliasing" :
cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" :
"unknown");
}
以上cacheid可能的值是:
#define CACHEID_VIVT(1 << 0)
#define CACHEID_VIPT_NONALIASING(1
<< 1)
#define CACHEID_VIPT_ALIASING(1 <<
2)
#define
CACHEID_VIPT(CACHEID_VIPT_ALIASING|CACHEID_VIPT_NONALIASING)
#define CACHEID_ASID_TAGGED(1 <<
3)
缓存类型分类
缓存具有索引(index)和标签(tag),这俩是一一对应的
索引:含有处理器想要访问的地址
标签:含有指向主内存位置的值
我的理解是,当CPU要访问某个地址,发现索引中有,就直接在cache中修改或读取。当需要和内存同步时,就会用标签中含的地址去访问内存。
索引和标签中含有的都是地址,都可以分别是虚拟地址或者物理地址。这样就可以把cache的类型分为:
1. PIPT 物理索引,物理标签
CPU需要把虚拟地址通过TLB转换为物理地址后,才能使用缓存,效率较低。
2. VIVT 虚拟索引,虚拟标签
访问缓存时,不需要MMU的介入。但可能发生“不同虚拟地址指向相同物理地址”的假名问题,继而可能发生cache和内存的一致性问题。
3. VIPT 虚拟索引,物理标签
看起来用的比较多。不会发生VIVT的假名问题。
cpu_proc_init 调用处理器初始化函数
cpu_proc_init的定义如下:
arch/arm/include/asm/cpu-multi32.h
#define cpu_proc_init()processor._proc_init()
只是获取了processor结构体的函数指针_proc_init。
processor结构体又是从proc_info_list中获取的,其定义如下:
struct processor {
void (*_data_abort)(unsigned long pc);
获取触发数据abort的指令的信息,比如地址/flags
unsigned long (*_prefetch_abort)(unsigned long lr);
根据实际内容(pabort_noifar),其实啥都没干(只是把pc = lr)。
void (*_proc_init)(void);
执行初始化特定的处理器的操作(在arm v6/v7里,其实啥都没干,只是把pc = lr)
void (*_proc_fin)(void);
执行禁止特定的处理器时的操作(看起来是关闭处理器时做的操作,根据arm
v6实际使用的cpu_v6_proc_fin,内容是禁中断、刷新cache、禁cache)
void (*reset)(unsigned long addr) __attribute__((noreturn));
reset处理器的操作(根据arm v6使用的cpu_v6_reset,是pc = r0,这里r0是真正cpu
reset时将会跳转的地址。这句话相当于做了一次长跳转型的soft reset)
int (*_do_idle)(void);
将处理器设置为idle状态(比如在禁中断后又把cpu设置成等待中断的状态)
void (*dcache_clean_area)(void *addr, int size);
不刷新cache的情况下去clean D-cache中的一个虚拟地址range
void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *mm);
根据物理地址,设置一个新页表
void (*set_pte_ext)(pte_t *ptep, pte_t pte, unsigned int ext);
设置扩展(level
2)PTE(页表条目
(Page Table Entry)
)
} processor;
那么在本例中,cpu_proc_init其实啥都没干。
linux内核 noreturn,读《ARM Linux 内核源代码剖析》.......第13章 setup_processor()相关推荐
- linux中swi指令,Arm Linux系统调用流程详细解析SWI
Unix系统通过向内核发出系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口.系统调用是操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,系统调用的执行 ...
- arm linux 指纹识别,基于ARM—Linux指纹识别系统的设计.pdf
基于ARM-Linux指纹识别系统的设计.pdf 第 20卷 第 l5期 电子设计工程 2012年 8月 Vo1.20 No.15 ElectronicDesienEn~inee Aug.2012 基 ...
- arm linux 2.6下载,ARM+LINUX(montavista2.6.x)环境下SIMCOM5218使用
ARM+LINUX(montavista2.6.x)环境下 SIMCOM5218使用 http://blog.sina.com.cn/s/blog_4c796ed20100hpxg.html 一. 系 ...
- arm linux 进程调度,详解ARM Linux 2.4.x进程调度
Linux2.4.x是一个基于非抢占式的多任务的分时操作系统,虽然在用户进程的调度上采用抢占式策略,但是而在内核还是采用了轮转的方法,如果有个内核态的线程恶性占有CPU不释放,那系统无法从中解脱出来, ...
- 最新linux伺服控制,基于ARM Linux的三轴伺服控制系统人机界面设计 毕设
基于ARM Linux的三轴伺服控制系统人机界面设计 毕设 洛阳理工学院毕业设计(论文) 基于ARM+Linux的三轴伺服控制系统人机界面设计 摘要 现代计算机技术的产业革命,将世界经济从资本经济带入 ...
- cygwin 编译linux内核,Cygwin编译ARM Linux内核
--Cygwin配置 在d:\cygwin\cygwin.bat中加入set CYGWIN=title ntea cd /bin mv sh.exe sh-original.exe ln –s bas ...
- arm linux 进程页表,linux-kernel – ARM Linux页表项格式 – 未使用的位?
我需要使用两个PTE位来存储我的内核模块在拦截页面保护错误时将使用的自定义"状态"值. 我正在开发Galaxy Nexus,它有一个ARM Cortex A9(我相信ARM v7) ...
- linux运行欧陆风云,Arm linux启动分析(1)
下周准备做linux启动的技术讲座,在这里我慢慢整理下自己的材料,这次我写的是Image的启动过程,也即使zImage解压缩结束后的启动代码,这时候的代码开始地址仍然是0x30008000,下面我结合 ...
- linux忽略abort信号,ARM Linux Data Abort 异常处理流程
// 本文部分内容来自网络 //基于内核版本3.4 发生Data Abort异常后,ARM处理器首先根据向量表找到对应异常入口,向量表位于arch/arm/kernel/entry-armv.S: . ...
最新文章
- 倒计时 8 天 | 完整议程大揭秘!来 20 个 AI 论坛,与百名大咖携手玩转人工智能...
- vue结合Promise及async实现高效开发。
- 设计模式 - 创建型模式_ 单例模式 Singleton Pattern
- [转载] Knowledge Management and Enginnering——02 知识表示方法
- php ping主机名,PHP PING值函数
- linux 二进制差分工具,打造Android万能的软件更新库
- 针对云原生转型的6个关键数据策略
- 微信登录提示逻辑不正确_微信逻辑错误无法登录
- Pseudo-terminal will not be allocated because stdin is not a terminal
- 基于MATLAB的指纹对比识别系统
- 【BZOJ1061】【NOI2008】志愿者招募 费用流神题、单纯形裸题(代码费用流)
- 腾讯art-template4,即vue后又获一利器
- dmp标签_用户标签/用户分群在DMP(数据管理平台)中的应用
- eggs和egg是什么意思_egg是什么意思_egg的翻译_音标_读音_用法_例句_爱词霸在线词典...
- RPGMV修炼手册1——RPGMV的基本了解
- Picosmos 一键智能抠图
- Caching best practices max-age
- 视频文件(任意文件)二进制读写
- 五年后的深圳是天堂还是地狱?
- 广告位banner组件