Chinaunix首页 | 论坛 | 博客
  • 博客访问: 91197
  • 博文数量: 59
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 260
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-09 17:30
文章分类

全部博文(59)

文章存档

2016年(5)

2015年(23)

2014年(30)

2013年(1)

我的朋友

分类: LINUX

2014-12-28 22:49:10

原文地址:嵌入式MIPS linux 分析 作者:mailer3721

嵌入式MIPS linux 分析

——基于GC3210

linux的启动流程

11、嵌入式linux 简介

LINUX 是一个类似UNIX 的操作系统,其代码是完全重新开放的,内核功能强大,实现

简洁。它提供了类似UNIX 的编程接口和系统调用,可以方便的将UNIX系统上的应用程序,

移植到Linux上运行。Linux具有一下特点:

1.可移植性:

Linux内核源代码是用C语言编写的,可以运行到各种平台。

2.支持多种处理器体系结构:

Linux内核能够支持的处理器要求是32位处理器,有没有MMU都可以。没有MMU的处理器

只有uClinux支持。Linux2.6内核支持的绝大多数都是带MMU的。

3.开放源代码的优势:

Linux内核是开放源代码的,也就是说,用户可以免费获取,修改linux源码。庞大的linux

社区和内核源代码工程,有很多各种各样的驱动程序和应用程序可以利用。开发者可以免费

得到社区的贡献、支持。

众所周知,Linux在嵌入式系统中的应用已经非常普遍。为了进一步促进这方面的应用,

Linux 2.6中,引入了很多非常有利于嵌入式应用的功能。这些新功能包括实时性能的增

强、更方便的移植性、对大容量内存的支持、支持微控制器和I/O系统的改进等。

 

12 mips linux 内核引导过程

1)第一个文件 arch/mips/kernel/head.S   建立堆栈等,最后跳到start_kernel

2start_kernel : init/main.c

 

asmlinkage void __init start_kernel(void)

{      

        char * command_line;

        extern struct kernel_param __start___param[], __stop___param[];

       

       

        smp_setup_processor_id();

       

        /*

         * Need to run as early as possible, to initialize the

         * lockdep hash:

         */

        lockdep_init();

       

        local_irq_disable();

        early_boot_irqs_off();

        early_init_irq_lock_class();

/* 这时已关闭中断。作必要的设置。然后再开中断。*/

 

/*     

 * Interrupts are still disabled. Do necessary setups, then

 * enable them

 */

        lock_kernel();               //取得内核锁

        boot_cpu_init();

        page_address_init();

        printk(KERN_NOTICE);

        printk(linux_banner);

        setup_arch(&command_line);      

        setup_per_cpu_areas();

        smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

 

        /*

         * Set up the scheduler prior starting any interrupts (such as the

         * timer interrupt). Full topology setup happens at smp_init()

         * time - but meanwhile we still have a functioning scheduler.

         */

        sched_init();          //与进程相关的初始化

        /*

         * Disable preemption - early bootup scheduling is extremely

         * fragile until we cpu_idle() for the first time.

         */

        preempt_disable();

        build_all_zonelists();

        page_alloc_init();

        printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line);

parse_early_param();

        parse_args("Booting kernel", command_line, __start___param,

                   __stop___param - __start___param,

                   &unknown_bootoption);

        sort_main_extable();

        unwind_init();

        trap_init();             //设置陷阱

        rcu_init();

        init_IRQ();                   //设置外部中断

        pidhash_init();

        init_timers();         //timer初始化

        hrtimers_init();

        softirq_init();         //初始化软中断

        timekeeping_init();

        time_init();            //时钟初始化

        profile_init();

        if (!irqs_disabled())

                printk("start_kernel(): bug: interrupts were enabled early\n");

        early_boot_irqs_on();

         local_irq_enable();              //irq enable

 

        /*

         * HACK ALERT! This is early. We're enabling the console before

         * we've done PCI setups etc, and console_init() must be aware of

         * this. But we do want output early, in case something goes wrong.

         */

        console_init();

        if (panic_later)

                panic(panic_later, panic_param);

 

        lockdep_info();

 

        /*

         * Need to run this when irqs are enabled, because it wants

         * to self-test [hard/soft]-irqs on/off lock inversion bugs

         * too:

         */

        locking_selftest();

 

#ifdef CONFIG_BLK_DEV_INITRD

        if (initrd_start && !initrd_below_start_ok &&

                        initrd_start < min_low_pfn << PAGE_SHIFT) {

                printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "

                    "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);

                initrd_start = 0;

        }

#endif

        vfs_caches_init_early();

        cpuset_init_early();

        mem_init();                  //初始化物理内存管理器

        kmem_cache_init();              //内核内存管理器的初始化,也就是初始化cacheslab

        setup_per_cpu_pageset();

        numa_policy_init();

        if (late_time_init)

                late_time_init();

        calibrate_delay();

        pidmap_init();

        pgtable_cache_init();

        prio_tree_init();

        anon_vma_init();

#ifdef CONFIG_X86

        if (efi_enabled)

                efi_enter_virtual_mode();

#endif

        fork_init(num_physpages);

        proc_caches_init();        //proc文件系统创建高速缓冲

        buffer_init();

        unnamed_dev_init();

        key_init();

        security_init();

        vfs_caches_init(num_physpages);

        radix_tree_init();

        signals_init();

        /* rootfs populating might need page-writeback */

        page_writeback_init();

#ifdef CONFIG_PROC_FS

        proc_root_init();           //proc文件系统创建高速缓冲

#endif

        cpuset_init();

        taskstats_init_early();

        delayacct_init();

 

        check_bugs();               //检查与处理器相关的bug

 

        acpi_early_init(); /* before LAPIC and SMP init */

 

        /* Do the rest non-__init'ed, we're now alive */

        rest_init();             //调用kernel_thread,创建系统的第一号进程

}

 

 

3arch/mips/kernel/setup.c

void __init setup_arch(char **cmdline_p)

{

        cpu_probe();     读取cpuID ,探测cpu所属的架构及指令集范围,

        prom_init();          

//启动参数等  //arch/mips/soc3210/soc32101-boards/soc-soc/prom.c

        cpu_report();                //打印cpu信息

 

#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();             //资源分配,IO资源,内存资源等

#ifdef CONFIG_SMP

        plat_smp_setup();

#endif

}

 

我们来跟随内核启动运行的过程看看mips内核有什么特别之处.

 

加电后,mips kernel从系统固件程序(类似bios,可能烧在eprom,flash)得到控制之后(head.S),初始化内核栈,

调用setup_arch初始化硬件平台相关的代码.

setup_arch(setup.c)首先监测使用的CPU(通过MIPS CPUCP0控制寄存器PRID) 确定使用的指令集和一些CPU参数,TLB大小等.然后调用prom_init做一些底层参数初始化. prom_init是和具体的硬件相关的.

 

使用MIPS CPU的平台多如牛毛, 所以大家在arch/mips下面可以看到很多的子目录, 每个子目录是一个或者一系列相似的平台.这里的平台差不多可以理解成一块主板加上它的系统固件,其中很多还包括一些专用的显卡什么的硬件(比如一些工作站).

这些目录的主要任务是:

     1. 提供底层板子上的一些重要信息,包括系统固件传递的参数,io的映射基地址

       ,内存的大小的分布等.多数还包括提供早期的信息输入输出接口(通常是一个

       简单的串口驱动)以方便调试,因为pmon往往不提供键盘和显示卡的支持.

     2. 底层中断代码,包括中断控制器编程和中断的分派,应答等

     3. pci子系统底层代码. 实现pci配置空间的读写,以及pci设备的中断,IO/Mem

        空间的分配

     4. 其它,特定的硬件.常见的有实时时钟等

 

这里关键是要理解这些硬件平台和熟悉的x86不同之处.

 

 * item

 MIPS不象X86有很标准的硬件软件接口,而是五花八门,每个厂家有一套,因为它们很多是嵌入式系统或者专门的工作站.不象PC,有了BIOS后用同一套的程序,就可以使用很多不同的主板和CPU.

MIPS中的'bios'常用的有pmonyamon,都是开放源代码的软件。很多开发板带的固件功能和PC BIOS很不一样,它们多数支持串口显示,或者网络下载和启动,以及类DEBUG的调试界面,但可能根本不支持显卡和硬盘,没有一般的基本'输入输出'功能.

而在MIPS中就很不一样了,IO一般是memory map,map到哪里就倚赖具体平台了.PCI设备的地址空间和CPU所见的物理内存地址空间往往也不一样 (bus address & physical address).所以mips kerneliob/outb,以及bus_to_virt/virt_to_bus,phys_to_virt/virt_to_phys,

 ioremap等就要小心考虑.

PCI配置空间的读写和地址空间映射的处理通常都是每个平台不一样的.因为缺乏统一接口的BIOS,内核经常要自己做PCI设备的枚举,空间分配,中断分配.

 

* 中断系统.

PC中中断控制器先是有8259,后来是apic,cpu的中断处理386之后好像 也变化不大,相对统一. mips CPU的中断处理方式倒是比较一致,但是主板上的控制器就乱七八糟了怎么鉴别中断源,怎么编程控制器等任务就得各自实现了.总的说来,MIPS CPU的中断处理方式体现了RISC的特点:软件做事多,硬件尽量精简.

 

* 存储管理.

MIPS 是典型的RISC结构,它的存储管理单元做的事情比象x86这种机器少得多.例如,它的tlb是软件管理的,cache常常是需要系统程序干预的.而且,过多的CPU和主板变种使得这一部分非常复杂,容易出错.

存储管理的代码主要在include/asm-mipsarch/mips/mm/目录下.

 

* 其它.

如时间处理,r4k以上的MIPS CPU提供count/compare寄存器,每隔几拍count增加,到和compare相等时发生时钟中断,这可以用来提供系统的时钟中断. 但很多板子自己也提供其它的可编程时钟源.具体用什么就取决于开发者了.

init_arch后是loadmmu,初始化cache/tlb.代码在arch/mips/mm.有人可能会问,cachetlb之前CPU怎么工作的?

 x86里有实模式,MIPS没有,但它的地址空间是特殊的,分成几个不同的区域, 每个区域中的地址在CPU里的待遇是不一样的,系统刚上电时CPU从地址bfc00000开始,那里的地址既不用tlb也不用cache,所以CPU能工作而不管cachetlb是什么样子.

当然,这样子效率是很低的,所以CPU很快就开始进行loadmmu. 因为MIPSCPU变种繁多,所以代码又臭又长.

主要不外是检测cache大小,选择相应的ache/tlb flush过程,还有一些memcpy/memset等的高效实现.

这里还很容易出微妙的错误,软件管理tlb或者cache都不简单,要保证效率又要保证正确.在开发初期常常先关掉CPUcache以便排除cache问题.

 MMU初始化后,系统就直接跳转到init/main.c中的start_kernel,很快吧不过别高兴,start_kernel虚晃一枪,又回到arch/mips/kernel/setup.c,调用setup_arch,这回就是完成上面说的各平台相关的初始化了.

 平台相关的初始化完成之后,mips内核和其它平台的内核区别就不大了,但也还有不少问题需要关注.如许多驱动程序可能因为倚赖x86的特殊属性(IO端口,自动的cache一致性维护,显卡初始化等)而不能直接在MIPS下工作.

 

 

3Mips Linux 根文件系统加载过程

 

14 修改elf入口

Arch/mips/Makefile

core-$(CONFIG_SOC_SOC) +=arch/mips/soc3210/soc32101-boards/soc-soc/

load-$(CONFIG_SOC_SOC) +=0xffffffff80200000

cflags-$(CONFIG_SOC_SOC) += -Iinclude/asm-mips/mach-longmeng

 

0xffffffff80200000就是入口地址

 

 

 

 

 

阅读(348) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~