九层之台,起于垒土 千里之行,始于足下 - 老子
从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 下面的链接!
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 - 点滴积累