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()相关推荐

  1. linux中swi指令,Arm Linux系统调用流程详细解析SWI

    Unix系统通过向内核发出系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口.系统调用是操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,系统调用的执行 ...

  2. arm linux 指纹识别,基于ARM—Linux指纹识别系统的设计.pdf

    基于ARM-Linux指纹识别系统的设计.pdf 第 20卷 第 l5期 电子设计工程 2012年 8月 Vo1.20 No.15 ElectronicDesienEn~inee Aug.2012 基 ...

  3. 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 一. 系 ...

  4. arm linux 进程调度,详解ARM Linux 2.4.x进程调度

    Linux2.4.x是一个基于非抢占式的多任务的分时操作系统,虽然在用户进程的调度上采用抢占式策略,但是而在内核还是采用了轮转的方法,如果有个内核态的线程恶性占有CPU不释放,那系统无法从中解脱出来, ...

  5. 最新linux伺服控制,基于ARM Linux的三轴伺服控制系统人机界面设计 毕设

    基于ARM Linux的三轴伺服控制系统人机界面设计 毕设 洛阳理工学院毕业设计(论文) 基于ARM+Linux的三轴伺服控制系统人机界面设计 摘要 现代计算机技术的产业革命,将世界经济从资本经济带入 ...

  6. 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 ...

  7. arm linux 进程页表,linux-kernel – ARM Linux页表项格式 – 未使用的位?

    我需要使用两个PTE位来存储我的内核模块在拦截页面保护错误时将使用的自定义"状态"值. 我正在开发Galaxy Nexus,它有一个ARM Cortex A9(我相信ARM v7) ...

  8. linux运行欧陆风云,Arm linux启动分析(1)

    下周准备做linux启动的技术讲座,在这里我慢慢整理下自己的材料,这次我写的是Image的启动过程,也即使zImage解压缩结束后的启动代码,这时候的代码开始地址仍然是0x30008000,下面我结合 ...

  9. linux忽略abort信号,ARM Linux Data Abort 异常处理流程

    // 本文部分内容来自网络 //基于内核版本3.4 发生Data Abort异常后,ARM处理器首先根据向量表找到对应异常入口,向量表位于arch/arm/kernel/entry-armv.S: . ...

最新文章

  1. 倒计时 8 天 | 完整议程大揭秘!来 20 个 AI 论坛,与百名大咖携手玩转人工智能...
  2. vue结合Promise及async实现高效开发。
  3. 设计模式 - 创建型模式_ 单例模式 Singleton Pattern
  4. [转载] Knowledge Management and Enginnering——02 知识表示方法
  5. php ping主机名,PHP PING值函数
  6. linux 二进制差分工具,打造Android万能的软件更新库
  7. 针对云原生转型的6个关键数据策略
  8. 微信登录提示逻辑不正确_微信逻辑错误无法登录
  9. Pseudo-terminal will not be allocated because stdin is not a terminal
  10. 基于MATLAB的指纹对比识别系统
  11. 【BZOJ1061】【NOI2008】志愿者招募 费用流神题、单纯形裸题(代码费用流)
  12. 腾讯art-template4,即vue后又获一利器
  13. dmp标签_用户标签/用户分群在DMP(数据管理平台)中的应用
  14. eggs和egg是什么意思_egg是什么意思_egg的翻译_音标_读音_用法_例句_爱词霸在线词典...
  15. RPGMV修炼手册1——RPGMV的基本了解
  16. Picosmos 一键智能抠图
  17. Caching best practices max-age
  18. 视频文件(任意文件)二进制读写
  19. 五年后的深圳是天堂还是地狱?
  20. 广告位banner组件

热门文章

  1. MQL5画多种颜色空心K线的方法
  2. 开发创造性思维的基本方法
  3. 使用PHP搭建自己的MVC框架
  4. Python线性规划实践
  5. 2021-05-27 WMS系统中的二维码技术应用
  6. 亚马逊运营如何优化Listing和广告词?
  7. js正则中文英文判断
  8. C语言——查验身份证
  9. 集腋成裘-06-angularJS -angular_02
  10. 儿子如何继承父亲的遗产