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

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 = &current_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 - 点滴积累

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值