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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值