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其实啥都没干。