linux 3.5 lookup_processor_type函数
来源:互联网 发布:wifi有网络看不了视频 编辑:程序博客网 时间:2024/05/29 16:37
以下所讲的内容以 ARM 处理器展开:
每一个CPU都会有一个对应的处理器类型,它通过读取CP15中的寄存器C0主标识符来确定。
使用下面这条汇编语句进行读取:
MRC P15, 0, R0, C0, C0, 0
在linux 内核中,会使用对读取到的主标志符通过lookup_processor_type函数进行选择返回一个类型为struct proc_info_list 的结构体。
该结构体定义在:
/arch/arm/include/asm/procinfo.h
struct proc_info_list {
unsigned int cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char *arch_name;
const char *elf_name;
unsigned int elf_hwcap;
const char *cpu_name;
struct processor*proc;
struct cpu_tlb_fns*tlb;
struct cpu_user_fns*user;
struct cpu_cache_fns*cache;
};
我们需要关注的是第一个域和第二个域:
unsigned int cpu_val;
unsigned int cpu_mask;
判断的过程是:对读取到的主标注符(假如存储在R9寄存器),进行掩码与之后再跟cpu_val来比较如果相等,则找到对应的类型结构体。
汇编代码如下:
1: ldmia r5, {r3, r4} @ value, mask
and r4, r4, r9 @ mask wanted bits
teq r3, r4
注释:r5为指向一个结构体的起始地址。
比如对于一个arm 920类型的CPU来说它的定义如下:
/arch/arm/mm/proc-arm920.s
.section ".proc.info.init", #alloc, #execinstr
.type __arm920_proc_info,#object
__arm920_proc_info:
.long 0x41009200 //val
.long 0xff00fff0 //mask
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm920_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
.long cpu_arm920_name
.long arm920_processor_functions
.long v4wbi_tlb_fns
.long v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
.long arm920_cache_fns
#else
.long v4wt_cache_fns
#endif
.size __arm920_proc_info, . - __arm920_proc_info
我们现在知道了该结构体的定义,也知道了如何进行判断。现在问题就是如何在linux镜像中找到该结构体的起始地址。
其实内核中是把所有CPU类型的struct proc_info_list放在一个段(section)中,它们之间的连续的,之间的间距则为sizeof(struct proc_info_list)。
我们看一下lds是怎么描述的:
/arch/arm/kernel/vmlinux.lds.s
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
很明显,__arch_info_begin则为起始地址,__arch_info_end为结束地址。
既然现在也知道了如何存放这么一堆CPU类型结构体,那么看看具体的代码是如何来实现的。
/arch/arm/kernel/head-common.s
由ENTRY(lookup_processor_type)宏来导出该符号,让C代码可以调用该函数。
/*
* This provides a C-API version of __lookup_processor_type
*/
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r6, r9, lr} //保存一些寄存器,以免在后面的__lookup_processor_type中被破坏。
mov r9, r0 //r0->r9, ATPCS规定传递参数r0-r3,调用该函数传递一个参数,保存在r0.
bl __lookup_processor_type //之所以传递给r9,是因为__lookup_processor_type假设r9存放就是CP15寄存器C0的值。
mov r0, r5 //r5就是找到的结构体的起始地址或为0(没有找到),传递给r0作为返回参数。
ldmfd sp!, {r4 - r6, r9, pc} //恢复原先寄存器。
ENDPROC(lookup_processor_type)
/*
* Read processor ID register (CP#15, CR0), and look up in the linker-built
* supported processor list. Note that we can't use the absolute addresses
* for the __proc_info lists since we aren't running with the MMU on
* (and therefore, we are not in the correct address space). We have to
* calculate the offset.
*
* r9 = cpuid //假设就是这样,验证之前代码r0->r9。
* Returns:
* r3, r4, r6 corrupted //这几个寄存器会被使用到,所以之前需要保存寄存器的值。
* r5 = proc_info pointer in physical address space //r5指向所找到的proc_info结构体的物理地址
* r9 = cpuid (preserved)
*/
__CPUINIT
__lookup_processor_type:
adr r3, __lookup_processor_type_data //r3存放运行时地址
ldmia r3, {r4 - r6} //r4 存放__lookup_processor_type_data加载地址
//r5 存放__proc_info_begin
//r6 存放__proc_info_end
//r3 = r3 - r4 -> r3 != 0 时候代表加载时地址和运行时地址不一致
//需要对r5 r6加上一个偏移量r3
sub r3, r3, r4@ get offset between virt&phys
add r5, r5, r3@ convert virt addresses to // r5 指向__proc_info_begin lds里面的一个段
add r6, r6, r3@ physical address space // r6指向__proc_info_end
1: ldmia r5, {r3, r4} @ value, mask 之前提到过的判断的依据:
and r4, r4, r9 @ mask wanted bits 与上掩码,同val比较。
teq r3, r4 //测试是否相等,是否找到对应的proc_info
beq 2f //如果相等,则退出循环
add r5, r5, #PROC_INFO_SZ@ sizeof(proc_info_list)
cmp r5, r6 //不等条件下,r5 + #PROC_INFO_SZ指向下一个结构体
blo 1b //判断是否到结尾了
mov r5, #0@ unknown processor //最后没有找到proc_info -> r5 = 0 退出后则会发生错误
2: mov pc, lr //返回
ENDPROC(__lookup_processor_type)
/*
* Look in <asm/procinfo.h> for information about the __proc_info structure.
*/
.align 2
.type __lookup_processor_type_data, %object
__lookup_processor_type_data:
.long .
.long __proc_info_begin
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data
上面的所有总结就是:C代码中调用loopup_processor_type函数,传递1个参数(CP15寄存器C0),该函数会返回一个参数。如果返回参数为0,
就是没有找到对应的struct proc_info,否则就是该结构体所在的物理地址。
我们现在看看linux内核中是如何使用该函数的。
/arch/arm/kernel/setup.c
static void __init setup_processor(void)
{
struct proc_info_list *list;
list = lookup_processor_type(read_cpuid_id());
if (!list) {
printk("CPU configuration botched (ID %08x), unable "
"to continue.\n", read_cpuid_id());
while (1);
}
...
...
}
返回值list, 进行判断是否为空则可以知道是否找到对应的结构体。。。。
再者,可以推测read_cpuid_id就是读取CP15中寄存器C0,实际代码为:
static inline unsigned int __attribute_const__ read_cpuid_id(void)
{
return read_cpuid(CPUID_ID);
}
//加入reg = 0
//mrc p15, 0, %0, c0, c0, 0
// 读入c0 -> %0 这里"=r"(__val) 输出为__val
//作用读入CP15 主标志寄存器
#define read_cpuid(reg) \
({ \
unsigned int __val;\
asm("mrc p15, 0, %0, c0, c0, " __stringify(reg) \
: "=r" (__val)\
: \
: "cc"); \//会引起改变CPU状态寄存器
__val; \
})
#define CPUID_CACHETYPE 1
#define CPUID_TCM 2
#define CPUID_TLBTYPE 3
#define CPUID_MPIDR 5
顺便提一下,read_cpuid,根据传递的参数不同所读取的也不一样。
传递0: MRC P15, 0, R0, C0, C0, 0 读取主标识符
传递1: MRC P15, 0, R0, C0, C0, 1 读取cache类型
、、、
至于寄存器里内容的分布和定义则需要取查看相应手册了解。。。
- linux 3.5 lookup_processor_type函数
- linux 3.5 insert_resource函数解析
- LINUX函数
- linux函数
- Linux 函数
- linux 函数
- Linux 函数--access函数
- strdup函数 -- linux函数
- umask()函数 -- linux函数
- ftok() Linux Linux函数
- Linux fopen函数 stat函数
- Linux多线程函数pthread_create()函数
- Linux系统函数write()函数
- 嵌入式linux:Linux时间函数
- linux的系统函数
- Linux C 函数介绍
- Linux系统函数
- Linux进程函数集合
- AJAX——Introduction和最简单的AJAX例子
- maven打包时跳过测试
- 实现dedecms织梦目录式伪静态的方法
- Android UI详解之View绘制原理
- 【ms-dos】 常用命令(记录)
- linux 3.5 lookup_processor_type函数
- 安卓给按钮添加监听事件的四种方法2.
- 网易游戏面试面经搜集
- The steps to integrate with Gerrit with eclipse
- iOS APP 审核拒绝理由
- golang中io包用法(二)
- android 之 Hnadler 、Message 、Looper
- hello linux
- Android中的meminfo