您的位置:首页 > 运维架构 > Linux

全面解析Linux 内核 3.10.x - 板级初始化 - setup_arch

2017-06-29 08:13 477 查看
From: 全面解析Linux 内核 3.10.x - 本文章完全基于MIPS架构


九层之台,起于垒土 千里之行,始于足下 - 老子


从dmesg的第一条打印信息说起 - Linux banner

且看我的ubuntu 12.04的第一条打印语句
Linux version 3.11.0-15-generic (buildd@allspice) (gcc version 4.6.3 (Ubuntu/
Linaro 4.6.3-1ubuntu5) ) #25~precise1-Ubuntu SMP Thu Jan 30 17:39:31 UTC 2014
(Ubuntu 3.11.0-15.25~precise1-generic 3.11.10)


内核代码:
printk(KERN_NOTICE "%s", linux_banner);


linux_banner 是个变长buff位于init/version.c文件
const char linux_banner[] =
"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";


宏的内容位于 include/generated/compile.h

Note: compile.h文件是编译生成的,并不属于内核源码!


我叫Mips,可他们都是x86 - setup_arch

不管是移动端,桌面还是服务器市场上,x86独秀一枝花,MIPS系列的CPU都很孤单!曾今的耀眼到如今的孤单,像是看破了人世间的种种! 网络关于这部分基本都是x86 或者 ARM为主的内容,那么我就来说说MIPS吧!

setup_arch 

函数做的事情(MIPS,x86,ARM)根据体系架构的不同,初始化也有所不同! 

Mips:
void __init setup_arch(char **cmdline_p)
{
cpu_probe();
prom_init();

#ifdef CONFIG_EARLY_PRINTK
setup_early_printk();
#endif
cpu_report();
check_bugs_early();

#if defined(CONFIG_VT)
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
arch_mem_init(cmdline_p);

resource_init();
plat_smp_setup();

cpu_cache_init();
}


总结,setup_arch做了以下几件事情:


1.CPU配置初始化 - cpu_probe()

__cpuinit void cpu_probe(void)
{
struct cpuinfo_mips *c = ¤t_cpu_data;
unsigned int cpu = smp_processor_id();  #1.读取CP0(协处理器0的PRId($15)寄存器获取版本号

c->processor_id = PRID_IMP_UNKNOWN;
c->fpu_id   = FPIR_IMP_NONE;
c->cputype  = CPU_UNKNOWN;

c->processor_id = read_c0_prid();
switch (c->processor_id & 0xff0000) {
case PRID_COMP_LEGACY:
cpu_probe_legacy(c, cpu);
break;
case PRID_COMP_MIPS:
cpu_probe_mips(c, cpu);
break;
case PRID_COMP_ALCHEMY:
cpu_probe_alchemy(c, cpu);
break;
case PRID_COMP_SIBYTE:
cpu_probe_sibyte(c, cpu);
break;
case PRID_COMP_BROADCOM:
cpu_probe_broadcom(c, cpu);
break;
case PRID_COMP_SANDCRAFT:
cpu_probe_sandcraft(c, cpu);
break;
case PRID_COMP_NXP:
cpu_probe_nxp(c, cpu);
break;
case PRID_COMP_CAVIUM:
cpu_probe_cavium(c, cpu);
break;
case PRID_COMP_INGENIC:
cpu_probe_ingenic(c, cpu);
break;
case PRID_COMP_NETLOGIC:
cpu_probe_netlogic(c, cpu);
break;
}

BUG_ON(!__cpu_name[cpu]);
BUG_ON(c->cputype == CPU_UNKNOWN);

/*
* Platform code can force the cpu type to optimize code
* generation. In that case be sure the cpu type is correctly
* manually setup otherwise it could trigger some nasty bugs.
*/
BUG_ON(current_cpu_type() != c->cputype);

if (mips_fpu_disabled)
c->options &= ~MIPS_CPU_FPU;

if (mips_dsp_disabled)
c->ases &= ~(MIPS_ASE_DSP | MIPS_ASE_DSP2P);

if (c->options & MIPS_CPU_FPU) {
c->fpu_id = cpu_get_fpu_id();

if (c->isa_level == MIPS_CPU_ISA_M32R1 ||
c->isa_level == MIPS_CPU_ISA_M32R2 ||
c->isa_level == MIPS_CPU_ISA_M64R1 ||
c->isa_level == MIPS_CPU_ISA_M64R2) {
if (c->fpu_id & MIPS_FPIR_3D)
c->ases |= MIPS_ASE_MIPS3D;
}
}

if (cpu_has_mips_r2) {
c->srsets = ((read_c0_srsctl() >> 26) & 0x0f) + 1;
/* R2 has Performance Counter Interrupt indicator */
c->options |= MIPS_CPU_PCI;
}
else
c->srsets = 1;

cpu_probe_vmbits(c);

#ifdef CONFIG_64BIT
if (cpu == 0)
__ua_limit = ~((1ull << cpu_vmbits) - 1);
#endif
}


通过上面代码,可得知: 

1>此函数先是读取协处理器0中处理器ID(PRId)寄存器获取版本后,进行对应CPU的处理!
+----------------+----------------+----------------+----------------+
| Company Options| Company ID     | Processor ID   | Revision       |
+----------------+----------------+----------------+----------------+
31            24 23            16 15             8 7
图1  协处理器0 PRId寄存器


2>获取之后,根据厂商信息进行cpu_probe_xxxx()函数 

此函数分两部分: 

第一主要通过decode_configs()函数通过协处理器0的16(Config),18(WatchLo),$19(WatchHi)寄存器对CPU进行,如图2,图3!
+----+------+------+----+----+----+----+----+----+----+----+----+---+-----+----+----+----+----+----+
| CM | EC   | EP   | SB | SS | SW | EW | SC | SM | BE | EM | EB | 0 | IC  | DC | IB | DB | CU | KO |
+----+------+------+----+----+----+----+----+----+----+----+----+---+-----+----+----+----+----+----+
31  30   28 27  24  23 22  21 20   19 18  17 16   15   14   13   12 11  9 8   6  5   4    3  2    0
图2  Config 寄存器
+----------------+-------+-----+---------+
| MatchAddr      | 0     | R   | W       |
+----------------+-------+-----+---------+
31            3    2       1       0
图3 WatchLo/Hi 寄存器
Config寄存器主要设置的Bit是
WatchLo/Hi寄存器主要去设置的Bit是


第二根据厂商信息确定CPU_TYPE,确定指令level,确定tlbsize大小! 

对于此部分并不是每个人都需要去了解的,有兴趣的可以See 下面的链接!

1.协处理器0,MIPS控制处理器详解


2.简单配置MMU以及获取基地址初始化 - [prom_init]()

void __init prom_init(void)
{
nlm_io_base = CKSEG1ADDR(XLP_DEFAULT_IO_BASE);
xlp_mmu_init();
nlm_node_init(0);

#ifdef CONFIG_SMP
cpumask_setall(&nlm_cpumask);
nlm_wakeup_secondary_cpus();

/* update TLB size after waking up threads */
current_cpu_data.tlbsize = ((read_c0_config6() >> 16) & 0xffff) + 1;

register_smp_ops(&nlm_smp_ops);
#endif
}


上述xlp_mmu_init函数开启了外部TLB,通过已知的页表大小配置得到页掩码,来而新刷新TLB! 

之后紧接着又获取了一些基地址信息! 

关于MMU以及TLB,请戳这里MIPS- TLB介绍


3.printk console - setup_early_printk

printk console注册流程
void __init setup_early_printk(void)
{
if (early_console)
return;
early_console = &early_console_prom;

register_console(&early_console_prom);
}


上述代码就是要printk 可以将信息打印早当前的console上,如果有兴趣研究console的工作流程,推荐查看Documentation/coonsole/console.txt


4.打印CPU版本以及FPU版本信息 - cpu_report

因为之前在cpu_probe的时候就已经将相关信息保存到cpuinfo_mips结构体中,在此处将CPU/FPU ID以及name打印出来!


5. 检查多线程 - cpu_chechbugs

此处一般检查


6. VGA初始化

因为当前没有配置CONFIG_VGA_CONSOLE此宏,故而此处跳过!


7. 板级内存初始化 -arch_mem_init

其实这里叫板级内存初始化有点牵强,此处其实做了好几件事情! 

在当前版本中,此函数主要做了4件事! 
a.打印内存映射 - print_memory_map 
b.引导内存分配器进行初始化- bootmem_init 

要弄清楚上述两个问题需要结合架构进行分析,先来了解下MIPS的地址空间以及映射关系,请分别戳 
c.Device Tree 结构初始化 - device_tree_init 

何谓DTS,我这里总结了一篇总结,请戳 全面解析Linux 内核 3.10.x -
Device Tree 详解! 
d.页表初始化 

关于页表以及页框等内容我页将其专门总结到一起!戳


8. CPU Cache初始化 - cpu_cache_init


此函数将对cpu的缓存进行初始化配置,这里暂时不去管它!

By: Keven - 点滴积累
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: