<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    mkimage工具 加載地址和入口地址 內核啟動分析

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接


    第三章第二節 mkimage工具
    制作Linux內核的壓縮鏡像文件,需要使用到mkimage工具。mkimage這個工具位于u-boot-2013. 04中的tools目錄下,它可以用來制作不壓縮或者壓縮的多種可啟動鏡像文件。mkimage在制作鏡像文件的時候,是在原來的可執行鏡像文件的前面加上一個16個byte(0x40)的頭,用來記錄參數所指定的信息,這樣u-boot才能識別出制作出來的這個鏡像是針對哪一個CPU體系結構、哪一種OS、哪種類型、加載到內存中的哪個位置、入口點在內存的哪個位置以及鏡像名是什么等信息。在/u-boot-2013.04/tools目錄下執行./mkimage,輸出信息如下所示:
    zhuzhaoqi@zhuzhaoqi-desktop:~/u-boot/u-boot-2013.04/u-boot-2013.04/tools$./mkimage
    Usage: ./mkimage -l image
    -l==> list image header information
    ./mkimage[-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
    -A==> set architecture to 'arch'
    -O==> set operating system to 'os'
    -T==> set image type to 'type'
    -C==> set compression type 'comp'
    -a==> set load address to 'addr' (hex)
    -e==> set entry point to 'ep' (hex)
    -n==> set image name to 'name'
    -d==> use image data from 'datafile'
    -x==> set XIP (execute in place)
    ./mkimage[-D dtc_options] -f fit-image.its fit-image
    ./mkimage-V ==> print version information and exit


    表3. 1 CPU體系結構



    取值

    表示的體系結構
    取值
    表示的體系結構
    alpha
    Alpha
    arm
    ARM
    x86
    Intel x86
    ia64
    IA64
    mips
    MIPS
    mips64
    MIPS 64 Bit
    ppc
    PowerPC
    s390
    IBM S390
    sh
    SuperH
    sparc
    SPARC
    sparc64
    SPARC 64 Bit
    m68k
    MC68000




    針對上面的輸出信息,-A 指定CPU的體系結構,也就是說,arch的取值可以是如表3. 1所示。
    -O 指定操作系統類型,os可以取:openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos。
    -T 指定鏡像類型,type可以是:standalone、kernel、ramdisk、multi、firmware、script、filesystem。
    -C 指定鏡像壓縮方式,comp可以是:none(不壓縮)、gzip( 用gzip的壓縮方式)、bzip2 (用bzip2的壓縮方式)。
    -a 指定鏡像在內存中的加載地址,鏡像下載到內存中時,要按照用mkimage制作鏡像時,這個參數所指定的地址值來下載。
    -e 指定鏡像運行的入口點地址,這個地址就是-a參數指定的值加上0x40(因為前面有個mkimage添加的0x40個字節的頭)。
    -n 指定鏡像名。
    -d 指定制作鏡像的源文件。
    將u-boot-2013.04下的tools這個文件夾下中的mkimage工具復制到ubuntu系統的/user/bin下,這樣可以直接當作操作命令使用。


    第三章第三節 加載地址和入口地址

    在上一節中,無法啟動內核,導致的原因可能是加載地址、入口地址等導致的。執行./mkimage之后如下:
    zhuzhaoqi@zhuzhaoqi-desktop:~/u-boot/u-boot-2013.04/u-boot-2013.04/tools$ ./mkimage
    ./mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
    其中-a addr指的就是鏡像在內存中的加載地址,鏡像下載到內存中時,要按照用mkimage制作鏡像時,這個參數所指定的地址值來下載。
    而-e ep 是指定鏡像運行的入口點地址。
    還有兩個概念需要明白,即是bootm address和kernel運行地址。bootm address:通過uboot的bootm命令,從address啟動kernel。kernel運行地址:在具體mach目錄中的Makefile.boot中指定,是kernel啟動后實際運行的物理地址。
    如果bootm address和Load Address相等,在這種情況下,bootm不會對uImage header后的zImage進行memory move的動作,而會直接go到Entry Point開始執行。因此此時的Entry Point必須設置為Load Address+ 0x40。如果kernel boot過程沒有到uncompressing the kernel,就可能是這里設置不對。它們之間的關系為:boom address == Load Address == Entry Point - 0x40。
    如果bootm address和Load Address不相等(但需要避免出現memory move時出現覆蓋導致zImage被破壞的情況)。此種情況下,bootm會把uImage header后的zImage文件move到Load Address,然后go到entry point開始執行。這段代碼在common/cmd_bootm.c中bootm_load_os函數中,如下程序所示。由此知道此時的Load Address必須等于Entry Point。它們之間的關系則為:boom address != Load Address == Entry Point。
    case IH_COMP_NONE:
    if (load == blob_start || load == image_start)
    {
    printf(" XIP %s ... ", type_name);
    no_overlap = 1;
    }
    else
    {
    printf(" Loading %s ... ", type_name);
    memmove_wd((void *)load, (void *)image_start,
    image_len, CHUNKSZ);
    }
    *load_end = load + image_len;
    puts("OK\n");
    break;
    zImage的頭部有地址無關的自解壓程序,因此剛開始執行的時候,zImage所在的內存地址(Entry Point)不需要同編譯kernel的地址相同。自解壓程序會把kernel解壓到編譯時指定的物理地址,然后開始地址相關代碼的執行。在開啟MMU之前,kernel都是直接使用物理地址(可參看內核符號映射表System.map)。
    通過上面的分析,大概找出了問題的根源,由于bootm address和Load Address都為50008000,屬于相等情況,也就是說Entry Point: 50008000,這個地址需要修改,替換成50008040。
    找到Load Address和Entry Point這兩個地址的定義,存在于scripts/makefile.lib中,
    zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/scripts$ gedit Makefile.lib
    打開之后可以找到如下:
    318 UIMAGE_LOADADDR ?= arch_must_set_this
    319 UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
    這里就是說Entry Point等于Load Address,那么應該修改成為Entry Point=Load Address+0x40,在GNU make中,有sed –e替換操作,如sed -e "s/..$$/40/",就是把輸出的字符串的最后兩個字符刪掉,并且用40來補充,也就是說把字符串最后兩個字符用40來替換。
    那么作如下修改:
    318 UIMAGE_LOADADDR ?= arch_must_set_this
    319 #UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
    320 UIMAGE_ENTRYADDR ?=$(shell echo $(UIMAGE_LOADADDR) |
    sed -e "s/..$$/40/")
    修改完成之后,回到linux-3.8.3根目錄下進行編譯,如下操作:
    zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ make uImage
    如果有如下報錯:
    zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ make uImage
    scripts/kconfig/conf --silentoldconfig Kconfig

    *** Error during update of the configuration.

    make[2]: *** [silentoldconfig] 錯誤 1
    make[1]: *** [silentoldconfig] 錯誤 2
    make: *** 沒有規則可以創建“include/config/kernel.release”需要的目標“include/config/auto.conf”。停止。
    那就是權限的問題,要么修改文件權限,要么在root下編譯。這樣即可:
    zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ sudo make uImage
    編譯成功之后輸出如下信息:
    ······
    Image Name: Linux-3.8.3
    Created: Sat Mar 16 10:38:47 2013
    Image Type: ARM Linux Kernel Image (uncompressed)
    Data Size: 1664080 Bytes = 1625.08 kB = 1.59 MB
    Load Address: 50008000
    Entry Point: 50008040
    Image arch/arm/boot/uImage is ready
    從串口輸出可知,Entry Point=Load Address+0x40,依舊按照SD燒寫方式進行測試,如果bootdelay延時過長,可以修改bootdelay時間,如下操作:
    Hit any key to stop autoboot: 0
    zzq6410 >>> set bootdelay 3
    zzq6410 >>> sav
    Saving Environment to NAND...
    Erasing Nand...
    Erasing at 0x80000 -- 100% complete.
    Writing to Nand... done
    zzq6410 >>>
    重啟OK6410開發平臺,測試結果如下:
    NAND read: device 0 offset 0x100000, size 0x500000
    5242880 bytes read: OK
    ## Booting kernel from Legacy Image at 50008000 ...
    Image Name: Linux-3.8.3
    Image Type: ARM Linux Kernel Image (uncompressed)
    Data Size: 1664080 Bytes = 1.6 MiB
    Load Address: 50008000
    Entry Point: 50008040
    Verifying Checksum ... OK
    XIP Kernel Image ... OK
    OK

    Starting kernel ...

    Starting kernel ...

    Uncompressing Linux... done, booting the kernel.
    Booting Linux on physical CPU 0x0
    Linux version 3.8.3 (zhuzhaoqi@zhuzhaoqi-desktop) (gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67) ) #1 Fri Mar 15 12:56:52 CST 2013
    CPU: ARMv6-compatible processor [410fb766] revision 6 (ARMv7), cr=00c5387d
    CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
    Machine: OK6410
    Memory policy: ECC disabled, Data cache writeback
    CPU S3C6410 (id 0x36410101)
    S3C24XX Clocks, Copyright 2004 Simtec Electronics
    S3C64XX: PLL settings, A=533000000, M=533000000, E=24000000
    S3C64XX: HCLK2=266500000, HCLK=133250000, PCLK=66625000
    mout_apll: source is fout_apll (1), rate is 533000000
    mout_epll: source is epll (1), rate is 24000000
    mout_mpll: source is mpll (1), rate is 533000000
    usb-bus-host: source is clk_48m (0), rate is 48000000
    irda-bus: source is mout_epll (0), rate is 24000000
    CPU: found DTCM0 8k @ 00000000, not enabled
    CPU: moved DTCM0 8k to fffe8000, enabled
    CPU: found DTCM1 8k @ 00000000, not enabled
    CPU: moved DTCM1 8k to fffea000, enabled
    CPU: found ITCM0 8k @ 00000000, not enabled
    CPU: moved ITCM0 8k to fffe0000, enabled
    CPU: found ITCM1 8k @ 00000000, not enabled
    CPU: moved ITCM1 8k to fffe2000, enabled
    Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024
    Kernel command line: root=/dev/mtdblock2 rootfstype=cramfs console=ttySAC0,115200
    PID hash table entries: 1024 (order: 0, 4096 bytes)
    Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
    Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
    __ex_table already sorted, skipping sort
    Memory: 256MB = 256MB total
    Memory: 256532k/256532k available, 5612k reserved, 0K highmem
    Virtual kernel memory layout:
    vector : 0xffff0000 - 0xffff1000 ( 4 kB)
    DTCM : 0xfffe8000 - 0xfffec000 ( 16 kB)
    ITCM : 0xfffe0000 - 0xfffe4000 ( 16 kB)
    fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
    vmalloc : 0xd0800000 - 0xff000000 ( 744 MB)
    lowmem : 0xc0000000 - 0xd0000000 ( 256 MB)
    modules : 0xbf000000 - 0xc0000000 ( 16 MB)
    .text : 0xc0008000 - 0xc02bed88 (2780 kB)
    .init : 0xc02bf000 - 0xc02da7a4 ( 110 kB)
    .data : 0xc02dc000 - 0xc03076a0 ( 174 kB)
    .bss : 0xc0308000 - 0xc0338ef8 ( 196 kB)
    SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
    NR_IRQS:246
    VIC @f6000000: id 0x00041192, vendor 0x41
    VIC @f6010000: id 0x00041192, vendor 0x41
    sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 4294967286ms
    Console: colour dummy device 80x30
    Calibrating delay loop... 353.89 BogoMIPS (lpj=1769472)
    pid_max: default: 32768 minimum: 301
    Mount-cache hash table entries: 512
    CPU: Testing write buffer coherency: ok
    Setting up static identity map for 0x502149a8 - 0x50214a04
    DMA: preallocated 256 KiB pool for atomic coherent allocations
    OK6410: Option string ok6410=0
    OK6410: selected LCD display is 480x272
    s3c64xx_dma_init: Registering DMA channels
    PL080: IRQ 73, at d0846000, channels 0..8
    PL080: IRQ 74, at d0848000, channels 8..16
    S3C6410: Initialising architecture
    bio: create slab <bio-0> at 0
    usbcore: registered new interface driver usbfs
    usbcore: registered new interface driver hub
    usbcore: registered new device driver usb
    ROMFS MTD (C) 2007 Red Hat, Inc.
    io scheduler noop registered
    io scheduler deadline registered
    io scheduler cfq registered (default)
    s3c-fb s3c-fb: window 0: fb
    Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
    s3c6400-uart.0: ttySAC0 at MMIO 0x7f005000 (irq = 69) is a S3C6400/10
    console [ttySAC0] enabled
    s3c6400-uart.1: ttySAC1 at MMIO 0x7f005400 (irq = 70) is a S3C6400/10
    s3c6400-uart.2: ttySAC2 at MMIO 0x7f005800 (irq = 71) is a S3C6400/10
    s3c6400-uart.3: ttySAC3 at MMIO 0x7f005c00 (irq = 72) is a S3C6400/10
    brd: module loaded
    loop: module loaded
    s3c24xx-nand s3c6400-nand: Tacls=4, 30ns Twrph0=8 60ns, Twrph1=6 45ns
    s3c24xx-nand s3c6400-nand: System booted from NAND
    s3c24xx-nand s3c6400-nand: NAND soft ECC
    NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit), 2048MiB, page size: 4096, OOB size: 218
    No oob scheme defined for oobsize 218
    ……
    Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
    中間作者省去了很多信息,因為這些信息暫時對我們是沒有太大關系,但是也給出了很多信息,因為可以很好地和接下來的每一步移植作對比。從串口的輸出,可以得知內核是啟動了。也就是說,此時u-boot已經成功將相關參數傳遞給linux3.8.3內核,完成了u-boot到內核的交接。并且內核已經識別了是OK6410開發平臺,控制CPU是s3c6410等信息。
    當然,讀者不僅僅可以通過修改Entry Point使得內核啟動,還可以修改啟動內核的地址使得bootm address和Load Address不相等,也就是修改U-Boot源碼中include/configs/目錄下的s3c6410.h文件中:
    #ifdef CONFIG_ENABLE_MMU
    #define CONFIG_SYS_MAPPED_RAM_BASE 0xc0000000
    #define CONFIG_BOOTCOMMAND"nand read 0xc0018000 0x600000x1c0000;\"bootm 0xc0018000"
    #else

    #define CONFIG_SYS_MAPPED_RAM_BASE CONFIG_SYS_SDRAM_BASE
    #define CONFIG_BOOTCOMMAND"nand read 0x50018000 0x100000 0x500000;"\"bootm 0x50018000"
    #endif


    第三章第四節 內核啟動分析
    對于ARM處理器,內核啟動大體上可以分為兩個階段:與處理器相關的匯編啟動階段和與處理器無關的C代碼啟動階段。匯編啟動階段從head.S(arch/arm/kernel/head.S)文件開始,C代碼啟動階段從start_kernel函數(init/main.c)開始。當然,經過壓縮的內核鏡像文件zImage,在進入匯編啟動階段前還要運行一段自解壓代碼(arch/arm/boot/compressed/head.S)。
    省略一些無關緊要的過程和編譯后不運行的代碼,該過程的啟動流程如圖3. 7所示。相對早期linux-2.6.38的版本,linux-3.8.3在匯編啟動階段并沒有出現__lookup_machine_type,但這并不意味著內核不再檢查bootloader傳入的machine_arch_type參數(R1),只是將檢查機制推遲到了C代碼階段。

    1) __lookup_processor_type
    __lookup_processor_type函數的具體實現如程序清單3. 1。
    程序清單3. 1查找處理器類型函數
    __lookup_processor_type:
    adr r3, __lookup_processor_type_data
    ldmia r3, {r4 - r6}
    sub r3, r3, r4 @ get offset between virt&phys
    add r5, r5, r3 @ convert virt addresses to
    add r6, r6, r3 @ physical address space
    1: ldmia r5, {r3, r4} @ value, mask
    and r4, r4, r9 @ mask wanted bits
    teq r3, r4
    beq 2f
    add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
    cmp r5, r6
    blo 1b
    mov r5, #0 @ unknown processor
    2: mov pc, lr
    ENDPROC(__lookup_processor_type)
    .align 2
    .type __lookup_processor_type_data, %object
    __lookup_processor_type_data:
    .long .
    .long __proc_info_begin
    .long __proc_info_end
    .size __lookup_processor_type_data, . - __lookup_processor_type_data
    __lookup_processor_type函數的主要功能是將內核支持的所有CPU類型與通過程序實際讀取的cpu id進行查表匹配。如果匹配成功,將匹配到的proc_info_list的基地址存到r5,否則,r5為0,程序將會進入一個死循環。函數傳入參數r9為程序實際讀取的cpu id,傳出參數r5為匹配到的proc_info_list指針的地址。同時為了使C語言能夠調用這個函數,根據APCS(ARM 過程調用標準)規則,簡單使用以下代碼就能包裝成一個C語言版本__lookup_processor_type的API函數,函數的原型為struct proc_info_list *lookup_processor_type(unsigned int)。
    ENTRY(lookup_processor_type)
    stmfd sp!, {r4 - r6, r9, lr}
    mov r9, r0
    bl __lookup_processor_type
    mov r0, r5
    ldmfd sp!, {r4 - r6, r9, pc}
    ENDPROC(lookup_processor_type)
    ENTRY和ENDPROC宏的定義如下:
    #define ENTRY(name) \
    .globl name; \
    name:
    #define ENDPROC(name)
    內核利用一個結構體proc_info_list來記錄處理器相關的信息,在文件arch/arm/include/asm/procinfo.h聲明了該結構體的類型,如下所示。
    struct proc_info_list {
    unsigned int cpu_val;
    unsigned int cpu_mask;
    unsigned long __cpu_mm_mmu_flags; /* used by head.S */
    unsigned long __cpu_io_mmu_flags; /* used by head.S */
    unsigned long __cpu_flush; /* used by head.S */
    const char *arch_name;
    const char *elf_name;
    unsigned int elf_hwcap;
    const char *cpu_name;
    struct processor *proc;
    struct cpu_tlb_fns *tlb;
    struct cpu_user_fns *user;
    struct cpu_cache_fns *cache;
    };
    事實上,在arch/arm/mm/proc-*.S這類文件中,程序才真正給內核所支持的arm處理器的proc_info_list分配了內存空間,例如linux/arch/arm/mm/proc-v6.S文件用匯編語言定義的__v6_proc_info結構體。.section指示符來指定這些結構體編譯到.proc.info段。.proc.info的起始地址為 __proc_info_begin,終止位置為__proc_info_end,把它們作為全局變量保存在內存中,鏈接腳本arch/arm/kernel/vmlinux.lds部分內容參考如下:
    .init.proc.info : {
    . = ALIGN(4);
    __proc_info_begin = .;
    *(.proc.info.init)
    __proc_info_end = .;
    }
    2) __vet_atags
    在啟動內核時, bootloader會向內核傳遞一些參數。通常,bootloader 有兩種方法傳遞參數給內核:一種是舊的參數結構方式(parameter_struct)——主要是2.6 之前的內核使用的方式;另外一種是現在的內核在用的參數列表(tagged list) 的方式。這些參數主要包括,系統的根設備標志、頁面大小、內存的起始地址和大小、當前內核命令參數等。而這些參數是通過struct tag結構體組織,利用指針鏈接成一個按順序排放的參數列表。bootloader引導內核啟動時,就會把這個列表的首地址放入R2中,傳給內核,內核通過這個地址就分析出傳入的所有參數。
    內核要求參數列表必須存放在RAM物理地址的頭16k位置,并且ATAG_CORE類型的參數需要放置在參數的列表的首位。__vet_atags的功能就是初步分析傳入的參數列表,判斷的方法也很簡單。如果這個列表起始參數是ATAG_CORE類型,則表示這是一個有效的參數列表。如果起始參數不是ATAG_CORE,就認為bootloader沒有傳遞參數給內核或傳入的參數不正確。
    1) __create_page_tables
    圖3. 8實際內存分布圖

    linux內核使用頁式內存管理,應用程序給出的內存地址是虛擬地址,它需要經過若干級頁表一級一級的變換,才變成真正的物理地址。32位CPU的虛擬地址大小從0x0000_0000到0xFFFF_FFFF共4G。以段(1 MB)的方式建立一級頁表,可以將虛擬地址空間分割成4096個段條目(section entry)。條目也稱為“描述符”(Descriptor),每一個段描述符32位,因此一級頁表占用16K(0x4000)內存空間。

     

    s3c6410處理器DRAM的地址空間從0x5000_0000開始,上文提到bootloader傳遞給內核的參數列表存放在RAM物理地址的頭16K位置,頁表放置在內核的前16K,因此內核的偏移地址為32K(0x8000),由此構成了如圖3. 8所示的實際內存分布圖。
    圖3. 9初步頁表建立流程

    __create_page_tables函數初始化了一個非常簡單頁表,僅映射了使內核能夠正常啟動的代碼空間,更加細致的工作將會在后續階段完善。流程如所示,獲取頁表物理地址、清空頁表區和建立啟動參數頁表通過閱讀源碼很容易理解,不加分析。
    __enable_mmu函數使能mmu后,CPU發出的地址是虛擬地址,程序正常運行需要映射得到物理地址,為了保障正常地配置mmu,需要對這段代碼1:1的絕對映射,映射范圍__turn_mmu_on至__turn_mmu_on_end。正常使能mmu后,不需要這段特定的映射了,在后續C代碼啟動階段時被paging_init()函數刪除。建立__enable_mmu函數區域的頁表代碼如程序清單3. 2所示。
    程序清單3. 2 __enable_mmu頁表的建立
    //r4 =頁表物理地址
    //獲取段描述符的默認配置flags
    ldr r7, [r10, #PROCINFO_MM_MMUFLAGS]
    adr r0, __turn_mmu_on_loc //得到__turn_mmu_on_loc的物理地址
    ldmia r0, {r3, r5, r6}
    sub r0, r0, r3 //計算得到物理地址與虛擬地址的偏差
    add r5, r5, r0 //修正得到__turn_mmu_on的物理地址
    add r6, r6, r0 //修正得到__turn_mmu_on_end的物理地址
    mov r5, r5, lsr #SECTION_SHIFT //1M對齊
    mov r6, r6, lsr #SECTION_SHIFT //1M對齊
    1: orr r3, r7, r5, lsl #SECTION_SHIFT //生成段描述符:flags + 段基址
    str r3, [r4, r5, lsl #PMD_ORDER] //設置段描述絕對映射,物理地址等于虛擬地址。每個段描述符占4字節,PMD_ORDER = 2
    cmp r5, r6
    addlo r5, r5, #1 //下一段,實際上__turn_mmu_on_end - __turn_mmu_on< 1M
    blo 1b
    ............................
    __turn_mmu_on_loc:
    .long . //__turn_mmu_on_loc當前位置的虛擬地址
    .long __turn_mmu_on //__turn_mmu_on的虛擬地址
    .long __turn_mmu_on_end //__turn_mmu_on_end的虛擬地址
    建立內核的映射區頁表,分析見程序清單3. 3。
    程序清單3. 3內核的映射區頁表的建立
    //r4 =頁表物理地址
    mov r3, pc //r3 = 當前物理地址
    mov r3, r3, lsr #SECTION_SHIFT //物理地址轉化段基址
    orr r3, r7, r3, lsl #SECTION_SHIFT //段基址 + flags = 段描述符
    //KERNEL_START = 0xC000_8000 SECTION_SHIFT = 20 PMD_ORDER = 2
    //由于arm 的立即數只能是8位表示,所有用兩條指令實現了將r3存儲到對應的頁表項中
    add r0, r4, #(KERNEL_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)
    str r3, [r0, #((KERNEL_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!
    ldr r6, =(KERNEL_END - 1)
    add r0, r0, #1 << PMD_ORDER
    add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) //內核映射頁表結束的段基址
    1: cmp r0, r6
    add r3, r3, #1 << SECTION_SHIFT //得到段描述符
    strls r3, [r0], #1 << PMD_ORDER //設置段描述符
    bls 1b
    1) __v6_setup
    __v6_setup 函數在 proc-v6.S 文件中,在頁表建立起來之后,此函數進行一些使能 MMU 之前的初始化操作。
    2) __enable_mmu
    __v6_setup已經為使能 MMU做好了必要的準備,為了保證MMU啟動后程序順利返回,在進入__enable_mmu函數之前,已經將__mmap_switched的虛擬地址(鏈接地址)存儲在R13中。
    3) __mmap_switched
    程序運行到這里,MMU已經啟動,__mmap_switched函數為內核進入C代碼階段做了一些準備工作:復制數據段,清楚BSS段,設置堆棧指針,保存processor ID、machine type(bootloader中傳入的)、atags pointer等。最后,終于跳轉到start_kernel函數,進入C代碼啟動階段。

     

    from:http://bbs.eeworld.com.cn/thread-419923-1-1.html

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全