1. 为vmware 添加新的硬盘 fdisk 用法就算了 mkfs -t ext3 -c /dev/sdb1 (我看就不用 -c 参数了吧, check bad block 就不用了, 俺的E4500真快阿,郁闷为啥叫sdx了呢) df 看看 vim /etc/fstab 添加到系统fstab /dev/sdb1 /cross ext3 default 1 2 (man fstab,mount) 不备份 fsck检查顺序 2. cross build principle -------------------------------------------------------------------------------- 1) 准备source code (host:linux targe:arm-linux) binutils-2.18 ftp://ftp.ntu.edu.tw/pub/gnu/gnu/binutils/binutils-2.18.tar.bz2 gcc-core-4.1.2 ftp://ftp.ntu.edu.tw/pub/gnu/gnu/gcc/gcc-4.1.2/gcc-core-4.1.2.tar.bz2 gcc-g++4.1.2 ftp://ftp.ntu.edu.tw/pub/gnu/gnu/gcc/gcc-4.1.2/gcc-g++-4.1.2.tar.bz2 glibc-2.6.1 ftp://ftp.ntu.edu.tw/pub/gnu//gnu/glibc/glibc-2.6.1.tar.gz linux-2.6.24.4 http://www.cn.kernel.org/pub/linux/kernel/v2.6/linux-2.6.24.4.tar.bz2 -------------------------------------------------------------------------------- 2) 定义环境变量 (然后解压缩...) export PREFIX=/cross/cross-arm cross gcc的bin会放到这个目录 export TARGET=arm-linux arm 上的linux cd /cross/src 假设你下载的gcc 和binutil在这个目录(你当然要解压缩了) mkdir build-binutils build-gcc build-glibc -------------------------------------------------------------------------------- 3) binutils (要分清host和target啊...) cd /cross/src/build-binutils ../binutils-x.xx/configure --target=$TARGET --prefix=$PREFIX --disable-nls make all make install --disable-nls :不要使用汉语吧, 用英语就行了. 这个选项让gcc不输出汉语的提示... -------------------------------------------------------------------------------- 4) bootstrap gcc cd /cross/src/build-gcc export PATH=$PATH:$PREFIX/bin ../gcc-x.x.x/configure --target=$TARGET --prefix=$PREFIX --disable-nls --enable-languages=c --without-headers --disable-shared --disable-threads --disable-libmudflap --disable-libssp make all-gcc make install-gcc -without-headers :这个选项使你编译出的GCC不能使用标准库.(host cpu和target cpu一样的话,基本不用cross toolchain系统通过编译选项也能实现这个功能). --with-newlib 这只是个bug的work around,和newlib没有关系的. This is only necessary if you are compiling GCC <= 3.3.x. That version has a known bug that keeps --without-headers from working correctly. Additionally setting --with-newlib is a workaround for that bug. --enable-languages :tell gcc需要哪些语言支持:font end, bootstrap gcc only surppot C tells GCC not to compile all the other language frontends it supports, but only C。 --disable-shared : 没有这个选项,会有 crti.o: No such file: No such file or directory collect2: ld returned 1 exit status --disable-thread : 没有这个选项的话会有, posix_thread.h can't not found 的问题 . --disable-libmudflap --disable-libssp :两个边界检查使用的库,有问题,禁止 http://www.mail-archive.com/gcc@gcc.gnu.org/msg31021.html 无libc的时候这几个库是不能编译通过的 :ssp,mudflap,gomp http://projects.linuxtogo.org/pipermail/openembedded-commits/2007-November/010465.html 这个线索是这个问题的补丁 还有官方的 bug :http://gcc.gnu.org/bugzilla/show_bug.cgi?id=25035 -------------------------------------------------------------------------------- 5) prepare linux kernel heads make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig 配置内核,记着选arm哦 make ARCH=arm CROSS_COMPILE=arm-linux- 随意运行下,就能产生version.h autoconf.h cd linux-2.4.21 cp -dR include/asm-arm $PREFIX/$TARGET/include/asm cp -dR include/linux $PREFIX/$TARGET/include/linux 在$TARGET/PREFIX/目录中建立下面的符号连接: cd $PREFIX/$TARGET ln -s include sys-linux 相当于configure gcc --with-heads= 在$TARGET_PREFIX/目录中建立下面的符号连接: cd $PREFIX/$TARGET/include/asm ln -s arch-s3c2410 arch 其实arch 已经建立了 (make了下,建立好了) ln -s proc-armv proc 这个版本没proc这个连接了 -------------------------------------------------------------------------------- 6) build glibc 搞了半天,原来glibc 2.6.1 是不支持arm的, 需要打上一系列的补丁 glibc-2.6.1-alpha_ioperm_fix-1.patch glibc-2.6.1-cross_hacks-1.patch glibc-2.6.1-hppa_nptl-1.patch glibc-2.6.1-libgcc_eh-1.patch glibc-2.6.1-localedef_segfault-1.patch glibc-2.6.1-mawk_fix-1.patch glibc-2.6.1-RTLD_SINGLE_THREAD_P-1.patch glibc-2.6.1-sysdep_cancel-1.patch 推荐补丁下载地址 : http://ftp.osuosl.org/pub/clfs/conglomeration/glibc/ 推荐文章: http://blog.chinaunix.net/u/26710/showart_394113.html RTLD_SINGLE_THREAD_P 的fix(上面的patch已经包含了,参考下): http://sources.redhat.com/ml/libc-ports/2006-10/msg00070.html 用 patch -Np1 -i 猛打把,少了那个都编译不过去 ,详细步骤如下 tar xvf glibc-2.6.1.tar.bz2 cd glibc-2.6.1/ tar xvf ../glibc-ports-2.6.1.tar.bz2 mv -v glibc-ports-2.6.1 ports patch -Np1 -i ../glibc-2.6.1-libgcc_eh-1.patch patch -Np1 -i ../glibc-2.6.1-localedef_segfault-1.patch patch -Np1 -i ../glibc-2.6.1-cross_hacks-1.patch patch -Np1 -i ../glibc-2.6.1-RTLD_SINGLE_THREAD_P-1.patch NPTL problem fix: patch for glibc2.4: http://www.devfiles.jlime.com/parted/glibc/nptl-crosscompile.patch The following lines need to be added to config.cache for Glibc to support NPTL: echo "libc_cv_forced_unwind=yes" > config.cache echo "libc_cv_c_cleanup=yes" >> config.cache 最后使用参数--cache-file=config.cache 还不行,需要把asm-generic copy 到 $PREFIX/$TARGET/include .... (为啥经验这么重要呢。。) $ tar -xvzf glibc-2.2.3.tar.gz $ tar -xzvf glibc-linuxthreads-2.2.3.tar.gz --directory=glibc-2.2.3 $ cd build-glibc $ CC=arm-linux-gcc ../glibc-2.2.3/configure --host=$TARGET --prefix=/usr --with-headers=$PREFIX/$TARGET/include --cache-file=config.cache CC=arm-linux-gcc 把 CC 变量设成你刚编译完的boostrap gcc, 用它来编译你的glibc. --host=$TARGET 告诉该链接库在目标系统上执行, 而非在本地主机. --prefix="/usr" 告诉配置脚本在目标板的根文件系统中glibc的位置. --with-headers 告诉glibc 我们的linux 内核头文件的目录位置. --enable-add-ons 告诉配置脚本使用我们下载的附加包. 已经将glibc-linuxthreads-2.2.3放入了glibc 源码目录中. 由于我们只添加了一个附加包, 这里--enable-add-ons等价于 --enable-add-ons=linuxthreads. (如果使用glibc-2.1.x, 需要使用glibc-crypt附加包, 就得使用: --enable-add-ons= linuxthreads, crypt选项). (NPTL 了 ) $ make $ make install_root=$PREFIX/$TARGET prefix="" install install_root 指定了安装链接库组件的目录, 将glibc安装到与我们项目相关的目录, 而非/usr目录.如果不指定prefix="", 那么glibc会被安装到$/PREFIX/$TARGET/usr/lib目录中. 指定prefix使glibc被安装到 $TARGET_PREFIX/lib目录. -------------------------------------------------------------------------------- 7.)修改$PREFIX$TARGET/lib目录中的libc.so $ cd $PREFIX/$TARGET/lib $ cat libc.so libc.so的内容: /* GNU ld script Use the shared library, but some functions are only in the static library, so try that secondarily. */ GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a ) 将/lib/绝对目录去掉, 既将"GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a )"改为: GROUP ( libc.so.6 libc_nonshared.a )
-------------------------------------------------------------------------------- 8) completed GCC cd build-4.1.2 ../gcc-4.1.2/configure --target=arm-linux --prefix=/cross/cross-arm --enable-languages=c make make install (出鬼的顺利,不支持c++,gcc 相关补丁也没有打, 凑合先用吧) -------------------------------------------------------------------------------- 9) simple check arm-linux-gcc --print-libgcc-file-name arm-linux-gcc -print-search-dirs arm-linux-gcc -o test test.c arm-linux-objdump -D test -------------------------------------------------------------------------------- 10 build kernel for arm 为了配置方便,copy 一个default config 到linux2.6.24.4 的根目录. 然后就剪辑吧能去的都去掉.嘿嘿. cp arch/arm/configs/s3c2410_defconfig . mv s3c2410_defconfig .config menuconfig: make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig zImage 出来了, download 到S3C2440 看看吧. 任意键进入boot menu. 选2 downfrom uart,用DNW下载代码,完成 选4,写入nand flash. (不同系统不一样,看monitor 咋写了) 选boot os............. 操蛋,啥都没看到, 只是在串口上看到monitor输出如下: Set boot params = root=/dev/ram init=/linuxrc initrd=0x00000000,0x00000000 console=ttyS1,115200 mem=65536K devfs=mount Load Kernel... Load Ramdisk... 遇到的问题请继续看下一节... -------------------------------------------------------------------------------- 11 调整解压缩参数 到底啥是最重要的啊, 如果连个printf都不能用,运气未免太差. 参数中的ttyS1 (第10节的输出),俺没有注意到(么经验啊). 于是上ADS 的AXD调试: 先连接上目标板,然后选run, 这样就可以reboot 系统,选5, boot os, 然后用ADX stop,就会看到一段汇编了. 仔细查看发现arm 确实在运行一段程序,经过查询system.map,发现是panic( 我猜想是root fs没有...,但是没有显示内容到我的串口啊,串口接在uart1上). 随意看看那个2440 mon程序, linux的加载方式如下: 将zImage从nand flash copy到 0x3040 0000 然后就jump过去. 内核参数放在0x3000 0100. goto_start = 0x3040 0000; (*goto_start)(0, 193); 不过对为啥不能启动还是没有线索啊... 有几个比较可疑的内核配置选项可能引起问题比如 boot option-> compressed rom boot loader base address #<a> default kernel command string #<b> 所以俺又试了几个选项, 把 #<a> 的地址改成0x3040 0000...... 不幸还是不可以, 因为没有信息出现也不知道运行到了哪里,不过根据调试的结果,应该是过了解压缩阶段的.... 事后俺查上面两个选项的含义( 就是按 ? 了), #<a> 是说ROM able 的zImage, 就是zImage直接在rom中运行时, 才有效所以犯了错误啊. <b> 也不行, 因为如果没有办法传递kernel 的command line时才有效, 而s32440下无效. 帮助里列举有几个平台需要这个选项. 先把参数地址为啥在0x3000 0100 和runaddr为啥在0x3040 0000跳过不谈,为啥没有任何信息? 连解压内核的信息都没有? 看了几遍config, 找到了如下的选项: system type-> [0] S3C UART to use for low-level messages 嘿嘿,进去把0改成1, 看看arm的相关启动代码,印证了通过这个选项选择内核解压缩信息的输出端口. 选成1, 再试,果然有内核解压缩信息打印出来,呵呵. 相关代码在内核的位置: decompress_kernel -> putstr("Uncompressing Linux..."); gunzip(); putstr(" done, booting the kernel."); -> include/asm-arm/plat-s3c static void putc(int ch) { if (uart_rd(S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) { ..... } uart_rd(unsigned int reg) { volatile unsigned int *ptr; ptr = (volatile unsigned int *)(reg + uart_base); return *ptr; } #define uart_base S3C24XX_PA_UART + (0x4000*CONFIG_S3C_LOWLEVEL_UART_PORT) -------------------------------------------------------------------------------- 12. 内核控制台 就是命令行里的console参数. 这里不打算讨论console到底是啥, 仅指出,俺没有看到内核正常的启动信息和这个选项大大的相关.我们分析下monitor给出的信息: Set boot params = root=/dev/ram init=/linuxrc initrd=0x00000000,0x00000000 console=ttyS1,115200 mem=65536K devfs=mount Load Kernel... Load Ramdisk... 这里console设置成了 ttyS1, 而linux2.6.24.4 要求(不知道具体从啥时候开始,2.6?) 名字是ttySAC1,指定串口1. 看monitor的代码就能知道了, monitor需要升级的. ...走了冤枉路了,明白了monitor和内核这里的一点纠缠. 升级monitor之后... 嘿嘿,终于有东西了...呵呵 Uncompressing Linux...................................................... done , booting the kernel. Linux version 2.6.24.4 (root@localhost.localdomain) (gcc version 4.1.2) #7 Tue Apr 15 07:50:25 CST 2008 CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177 Machine: SMDK2410 ATAG_INITRD is deprecated; please update your bootloader. Memory policy: ECC disabled, Data cache writeback CPU S3C2440A (id 0x32440001) S3C244X: core 400.000 MHz, memory 100.000 MHz, peripheral 50.000 MHz S3C24XX Clocks, (c) 2004 Simtec Electronics CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on CPU0: D VIVT write-back cache CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256 Kernel command line: root=/dev/ram init=/linuxrc initrd=0x00000000,0x00000000 console=ttySAC1,115200 mem=65536K devfs=mount irq: clearing pending ext status 000dff00 irq: clearing pending ext status 00001000 irq: clearing subpending status 000000ba irq: clearing subpending status 00000092 PID hash table entries: 256 (order: 8, 1024 bytes) timer tcon=00090000, tcnt a2c1, tcfg 00000200,00000000, usec 00001eb8 Console: colour dummy device 80x30 console [ttySAC1] enabled Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) Memory: 64MB = 64MB total Memory: 63128KB available (1428K code, 223K data, 92K init) Mount-cache hash table entries: 512 CPU: Testing write buffer coherency: ok S3C2440: Initialising architecture S3C2440: IRQ Support S3C2440: Clock Support, DVS off S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics DMA channel 0 at c4800000, irq 33 DMA channel 1 at c4800040, irq 34 DMA channel 2 at c4800080, irq 35 DMA channel 3 at c48000c0, irq 36 JFFS2 version 2.2. (NAND) 漏 2001-2006 Red Hat, Inc. io scheduler noop registered io scheduler anticipatory registered (default) io scheduler deadline registered io scheduler cfq registered Serial: 8250/16550 driver $Revision: 1.90 $ 4 ports, IRQ sharing enabled s3c2440-uart.0: s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2440 s3c2440-uart.1: s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2440 s3c2440-uart.2: s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2440 RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize loop: module loaded S3C24XX NAND Driver, (c) 2004 Simtec Electronics s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8- bit) Scanning device for bad blocks Creating 8 MTD partitions on "NAND 64MiB 3,3V 8-bit": 0x00000000-0x00004000 : "Boot Agent" 0x00000000-0x00200000 : "S3C2410 flash partition 1" 0x00400000-0x00800000 : "S3C2410 flash partition 2" 0x00800000-0x00a00000 : "S3C2410 flash partition 3" 0x00a00000-0x00e00000 : "S3C2410 flash partition 4" 0x00e00000-0x01800000 : "S3C2410 flash partition 5" 0x01800000-0x03000000 : "S3C2410 flash partition 6" 0x03000000-0x04000000 : "S3C2410 flash partition 7" mice: PS/2 mouse device common for all mice S3C24XX RTC, (c) 2004,2006 Simtec Electronics s3c2440-i2c s3c2440-i2c: slave address 0x10 s3c2440-i2c s3c2440-i2c: bus frequency set to 390 KHz s3c2440-i2c s3c2440-i2c: i2c-0: S3C I2C adapter S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled drivers/rtc/hctosys.c: unable to open rtc device (rtc0) List of all partitions: 1f00 16 mtdblock0 (driver?) 1f01 2048 mtdblock1 (driver?) 1f02 4096 mtdblock2 (driver?) 1f03 2048 mtdblock3 (driver?) 1f04 4096 mtdblock4 (driver?) 1f05 10240 mtdblock5 (driver?) 1f06 24576 mtdblock6 (driver?) 1f07 16384 mtdblock7 (driver?) No filesystem could mount root, tried: cramfs romfs Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0) -------------------------------------------------------------------------------- 13. 内核参数的传递方式 前边说过monitor吧内核参数放到了地址 0x3000 0100 这个地址. 这个道理何在呢. 先看内核打印内核命令行的地方: init/main.c asmlinkage void __init start_kernel(void) -->printk(KERN_NOTICE "Kernel command line: %s", boot_command_line); 是个全局变量,搜索下,在arch/arm/kernel/setup.c: void __init setup_arch(char **cmdline_p) { char *from = default_command_line; /*.config 中指定的命令行*/ ... mdesc = setup_machine(machine_arch_type); /*从上面串口输出知道是 "Machine: SMDK2410", 由.config决定*/ /*里边引用的lookup_machine_type 在arch/arm/kernel/head_common.S*/ ...... if (__atags_pointer) tags = phys_to_virt(__atags_pointer); else if (mdesc->boot_params) tags = phys_to_virt(mdesc->boot_params); ... /* * If we have the old style parameters, convert them to * a tag list. */ if (tags->hdr.tag != ATAG_CORE) convert_to_tag_list(tags); /*把boot loader传递的参数, arch/arm/kernel/compat.c struct param_struct { ..} 转换成tag list, 其中包括 ATAG_CMDLINE */ if (tags->hdr.tag != ATAG_CORE) tags = (struct tag *)&init_tags; if (mdesc->fixup) /*s3c2410 是NULL*/ mdesc->fixup(mdesc, tags, &from, &meminfo); if (tags->hdr.tag == ATAG_CORE) { if (meminfo.nr_banks != 0) squash_mem_tags(tags); parse_tags(tags); /*ATAG_CMDLINE 的parser 把相应命令行copy 到default_command_line, 也在这个文件内*/ } .... memcpy(boot_command_line, from, COMMAND_LINE_SIZE); boot_command_line[COMMAND_LINE_SIZE-1] = '/0'; parse_cmdline(cmdline_p, from); } 这里先忽略下mdesc是怎么被找到的,先直接搜索 mdesc的类型,可以找到 #define MACHINE_START(_type,_name) 这个宏,然后console的"SMDK2410"这个宏定义, 可以在arch/arm/mach-s3c2410下找到: MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch * to SMDK2410 */ /* Maintainer: Jonas Dietsche */ .phys_io = S3C2410_PA_UART, .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, .map_io = smdk2410_map_io, .init_irq = s3c24xx_init_irq, .init_machine = smdk2410_init, .timer = &s3c24xx_timer, MACHINE_END #define S3C2410_SDRAM_PA (S3C2410_CS6) #define S3C2410_CS6 (0x30000000) 这下子就知道内核参数传递的方式了: (arm) 把 arch/arm/kernel/compat.c 中定义的struct param_struct { } 放到0x3000 0100 处即可.... 至于去求证 monitor 的 runAddr 和参数地址, 请看 : (这个是monitor用到的参数,决定了runAddrs) BootParams boot_params = { {"bootpara", 1}, //0=boot without parameters,1=boot with parameters {"cpuclk", 2}, //0=200M, 1=300M, 2=400M, 3=440M {"rundelay", 0}, //0 seconds {"serial", 1}, //0=serial port 0, 1=serial port 1 {"baudrate", 115200}, {"machine", 193}, {"runAddr", 0x30400000}, {"rootfs", 0}, {"tty", 1}, {"initrdA", 0}, {"initrdL", 0}, {"memsize", 0x04000000}, {"devfs", 1}, {"ostore", 0}, //0=nand, 1=nor {"userpara", sizeof(DEFAULT_USER_PARAMS)}, DEFAULT_USER_PARAMS }; 从这个参数加载linux的代码在nand.c LoadRun . -------------------------------------------------------------------------------- 14. 内核加载地址和start参数问题 从上面的分析, 知道内核被加载到0x30400 0000处, 这个其实是zImage的加载地址, 就是说内核解压缩程序的运行地址. 还有一个问题,我们的内核一直是little endian 的. arm-linux-objdump下就知道. 一个问题是Big-endian的内核如何run, 另一个是解压缩程序的运行地址是随意的吗? 从monitor看, 这个环境一直在little endian运行... (big endian 实验也另作研究吧) 能否加载到任意合理地址(至少有ram吧,呵呵), 试验一下即可. 结果证明是可以的, 当然应该行,因为zImage已经支持PIC代码,并且可以配置成在纯ROM环境下运行(那就得烧到到固定地址了). 研究下zImage都包含什么东西,这个先从arch/arm/boot/Makefile看看吧: $(obj)/compressed/vmlinux: $(obj)/Image FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed $@ $(obj)/zImage: $(obj)/compressed/vmlinux FORCE #zImage 包含解压缩头的Image $(call if_changed,objcopy) @echo ' Kernel: $@ is ready' ....... $(obj)/uImage: $(obj)/zImage FORCE # U-boot image $(call if_changed,uimage) @echo ' Image $@ is ready' $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE 包含bootp目录的image bootpImage, 如要initrd,这个平台不支持,所以加载 $(Q)$(MAKE) $(build)=$(obj)/bootp $@ RamDisk 是monitor的事情了 @: $(obj)/bootpImage: $(obj)/bootp/bootp FORCE #包含bootp目录的image bootpImage $(call if_changed,objcopy) @echo ' Kernel: $@ is ready' 内核解压缩和PIC (position independent code) arch/arm/boot/compressed/Makefile # # We now have a PIC decompressor implementation. Decompressors running # from RAM should not define ZTEXTADDR. Decompressors running directly # from ROM or Flash must define ZTEXTADDR (preferably via the config) # FIXME: Previous assignment to ztextaddr-y is lost here. See SHARK ifeq ($(CONFIG_ZBOOT_ROM),y) #从boot ROM运行时需要配置一个固定地址,还有BSS的地址 ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT) ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS) else ZTEXTADDR := 0 #一般情况下,就是0, 是pic代码加上'手工'重定位,加载到任意地址 ZBSSADDR := ALIGN(4) endif SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ #把vmlinux.lds.in 中TEXT_START换成配置的地址(主要针对ZBOOT_ROM) targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o font.c head.o misc.o $(OBJS) EXTRA_CFLAGS := -fpic -fno-builtin EXTRA_AFLAGS := ............. # Don't allow any static data in misc.o, which # would otherwise mess up our GOT table CFLAGS_misc.o := -Dstatic= $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o $(addprefix $(obj)/, $(OBJS)) FORCE $(call if_changed,ld) @: $(obj)/piggy.gz: $(obj)/../Image FORCE #piggy.gz 是压缩后的内核,见piggy.S $(call if_changed,gzip) $(obj)/piggy.o: $(obj)/piggy.gz FORCE CFLAGS_font.o := -Dstatic= $(obj)/font.c: $(FONTC) $(call cmd,shipped) $(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config @sed "$(SEDFLAGS)" < $< > $@ $(obj)/misc.o: $(obj)/misc.c include/asm/arch/uncompress.h lib/inflate.c 解压缩的pic技术以后再研究吧,挺多的.这里就是增强下信心吧. 知道可以加载到任意地址,呵呵. 下面看看decompress的入口函数start的参数问题: arch/arm/boot/compressed/head.S /* * sort out different calling conventions */ .align start: .type start,#function .rept 8 mov r0, r0 .endr b 1f .word 0x016f2818 @ Magic numbers to help the loader .word start @ absolute load/run zImage address .word _edata @ zImage end address 1: mov r7, r1 @ save architecture ID mov r8, r2 @ save atags pointer 从这里看出, r1 存放的是architectureID, r2存放 atags 指针. 寄存器传递参数, 加上没有用的r0, 应该是这样一个函数: void start(0, archID, *atags) 其实,在arch/arm/kernel/head.S中有关于参数的一段详细的注释,看看就明白了: /* * Kernel startup entry point. * --------------------------- * * This is normally called from the decompressor code. The requirements * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0, * r1 = machine nr, r2 = atags pointer. * * This code is mostly position independent, so if you link the kernel at * 0xc0008000, you call this at __pa(0xc0008000). * * See linux/arch/arm/tools/mach-types for the complete list of machine * numbers for r1. * * We're trying to keep crap to a minimum; DO NOT add any machine specific * crap here - that's what the boot loader (or in extreme, well justified * circumstances, zImage) is for. */ 找到mach-type是: s3c2440 ARCH_S3C2440 S3C2440 362 不过通过实验, 我们的机器看来是SMDK兼容了,machine 参数必须传递193: SMDK2410 这个才行,而cpu类型则是自动侦测的,呵呵. 对应kernel的参数和decompressed一样: void (*theKernel)(int zero, int arch, uint params); 另:试了试big endian,发现现在linux kernel对s3c的系统还不支持big模式.make config也无此选项. -------------------------------------------------------------------------------- 15. initrd : initial ram disk load process 让我们从新审视所得到的内核console的输出(见上文),看看需要做的东西, 先来关注最后几行的输出信息,内核相关代码是: start_kernel->rest_init->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);-> static int __init kernel_init(void * unused) { .......... /* * check if there is an early userspace init. If yes, let it do all * the work */ if (!ramdisk_execute_command) /*由内核命令行参数 rdinit= 来控制,我们没有指定 (搜索就知道是rdinit=来控制了...)*/ ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); } ......... } 内核中有各种__setup宏定义的内核参数, 其前面的字符串是内核命令行, 其后的函数是这个命令行的处理函数,相关的宏定义在init.h中.grep下,很快有结果. 详细讨论先放一放. void __init prepare_namespace(void) { ............. if (saved_root_name[0]) { root_device_name = saved_root_name; /*这个就是由 root=/dev/ram传递的内核参数,稍作搜索即知*/ if (!strncmp(root_device_name, "mtd", 3)) { /*我们当然不是这个*/ ............. } ROOT_DEV = name_to_dev_t(root_device_name); /*/dev/ram 解析出来的设备是 ROOT_DEV= root_RAM0(1,0)*/ if (strncmp(root_device_name, "/dev/", 5) == 0) root_device_name += 5; /* root_dev_name = "ram" */ } if (initrd_load()) /* CONFIG_BLK_DEV_INITRD 之后才能使用initrd,我们的.config是有的*/ goto out; ...... mount_root(); out: sys_mount(".", "/", NULL, MS_MOVE, NULL); /* /root 如何成为根, 何以叫mount root,原来并不是加载 "/" */ sys_chroot("."); security_sb_post_mountroot(); } int __init initrd_load(void) { if (mount_initrd) { /*只有配置了内核命令行: noinitrd才为0, 我们当然没有'自杀'了*/ create_dev("/dev/ram", Root_RAM0); /*创建设备先...*/ /* * Load the initrd data into /dev/ram0. Execute it as initrd * unless /dev/ram0 is supposed to be our actual root device, * in that case the ram disk is just set up here, and gets * mounted in the normal path. */ if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) { /*下面看看/initrd.image啥时候创建的*/ sys_unlink("/initrd.image"); handle_initrd(); return 1; } } sys_unlink("/initrd.image"); return 0; } 从我们配置的参数是Root_RAM0, 最后 void __init prepare_namespace(void)会调用mount_root: void __init mount_root(void) { #ifdef CONFIG_ROOT_NFS ... #endif #ifdef CONFIG_BLK_DEV_FD .... #endif #ifdef CONFIG_BLOCK create_dev("/dev/root", ROOT_DEV); mount_block_root("/dev/root", root_mountflags); /*有默认值 MS_RDONLY | MS_SILENT*/ #endif } void __init mount_block_root(char *name, int flags) { get_fs_names(fs_names); /*所有已经安装的文件系统的名字列表*/ retry: for (p = fs_names; *p; p += strlen(p)+1) { /*p代表fs type, 这里name 是/dev/root,就是Root_RAM0*/ int err = do_mount_root(name, p, flags, root_mount_data); /*(dev,type,mntflags,(rootflags=,给具体文件系统的参数) )*/ ....... /*如果失败了,下面的信息倒是没有显示出来....*/ printk("VFS: Cannot open root device "%s" or %s", root_device_name, b); printk("Please append a correct "root=" boot option; here are the available partitions:"); printk_all_partitions(); panic("VFS: Unable to mount root fs on %s", b); } printk("List of all partitions:"); printk_all_partitions(); printk("No filesystem could mount root, tried: "); /*列出曾经尝试的文件系统类型*/ for (p = fs_names; *p; p += strlen(p)+1) printk(" %s", p); printk(""); ....... panic("VFS: Unable to mount root fs on %s", b); /*知名panic*/ ... } 好了上面的函数就是知名的panic. 这个过程就是将文件/initrd.image 拷贝到Root_RAM0设备内, 然后创建设备文件/dev/root(这个个文件就是为了fs的接口函数准备的),然后将 /dev/root 安装到/root, 最后升级/root到文件系统根目录 '/'. 遇到这个panic从代码上看,是do_mount_root在尝试用各种文件系统来解析/dev/root后竭尽失败.无奈之下,panic的.原因可就多了,比如config的时候没有选上ramdisk支持(设备层), 或者ramdisk中的文件系统内核不支持(文件系统层), 再或者initrd的加载出了问题(无论是boot loader 还是内核创建"/initrd.image", 最后参数传递错误也不成. 经过仔细检查,内核配置和参数传递应该没有啥问题. 这里好多内核的信息没有打印出来,详细的错误也就被隐蔽了. 为了验证ramdisk是否正确加载, 在rd_load_image("/initrd.image") 里面加了不少调试信息,结果发现, 根本没有/initrd.image这个文件,在这个函数打开这个文件的时候出错了, 这证明这个文件创建失败了. 原因也很多,不过还是先扫一眼这个文件在什么地方创建.一搜,在这个函数里呢: static int __init populate_rootfs(void) 这是一个init函数,内核悄悄的运行了他.... (运行的地方好找,不提). static int __init populate_rootfs(void) { ... /*解压缩先略过不看...*/ if (initrd_start) { /*这个就是内核命令行传递进来的值...., 汗...monitor里这个值是0, 显然是不对的啊*/ /* ...创建 initrd.image 这个文件*/ fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700); ....... } 好了一个问题出来了, 改吧, 去monitor的参数里修改一番,也犯了不少错误: 1) 第一个就是initrd的初始地址不能为0..., 2) 再有就是有个地方出错比如我设置initrd初始地址0x1000(随意值), 大小是0x1000(随意值,不设置长度monitor不加载ramdisk,只对此monitor有效). 这个地址有傻问题? 看看输出: Memory policy: ECC disabled, Data cache writeback initrd (0x00000000 - 0x000003e8) extends beyond physical memory - disabling initrd CPU S3C2440A (id 0x32440001) 这里又跳出个地方和initrd有关, 位置在bootmem初始化中:(ft...) arch/arm/mm/init.c static int __init check_initrd(struct meminfo *mi) { ....... if (phys_initrd_size) {/*最初传递的initrd start 和size都是0, 我ft.... */ for (i = 0; i < mi->nr_banks; i++) { ........... } if (initrd_node == -1) { printk(KERN_ERR "initrd (0x%08lx - 0x%08lx) extends beyond " "physical memory - disabling initrd", phys_initrd_start, end); phys_initrd_start = phys_initrd_size = 0; } .......... } 没有仔细探究这个bank是个什么意思(估计就是S3C2440 cpu里对内存bank的划分吧),但是这个警告信息提醒了我.超出物理内存? 不一定是地址太大,呵呵,因为啊, ram物理地址并不是从0开始的,启动的时候是S3C2440 自己吧nand读到了地址0开始的一段内部ram中.....所以,吧initrd start地址和size设置成一个在RAM地址范围内的一段地址里. 重启,这下,果然不同了........ 出现的信息还是不能加载root文件系统,但是initrd加载成功了, 即ramdisk已经包含了initrd.image, "/initrd.image"文件也已经创建成功了. 但是出现RAMDISK: image too big! (2078xx/4096) ,相关函数是 int __init rd_load_image(char *from) /*从/initrd.image 写入/dev/ram设备*/ { ...... /*nblocks是从ramdisk image中根据文件系统magic猜测的block数(identify_ramdisk_image), *rd_blocks是从/dev/ram设备中读出的数值,ramdisk大小是4096K size ,1024 blocksize,可以配置 */ if (nblocks > rd_blocks) { /*这个信息是发现加载的initrd image大小大于ram disk的大小,加载失败*/ printk("RAMDISK: image too big! (%dKiB/%ldKiB)", nblocks, rd_blocks); goto done; } .... } 另外,建议调试阶段把kernel consolelog 设置成verbose,就是在setup_arch的最前端,调用下面函数: console_verbose(); 这样一些提示性和continue性质的信息能够显示出来方便调试. 这个原因是什么? 根据调试信息打印出来信息, identify_ramdisk_image:RAMDISK: cramfs filesystem found at block 0 显然是发现了一个cramfs,但是不知到是什么原因. 呵呵,秘密是我还没有烧一个initrd进去呢,用的是现有的, 其大小和格式都是未知的.这样当然累了,要知道原因就太难(?), 所以要自己制作一个.... -------------------------------------------------------------------------------- 16. 制作ramfs 为了测试,仅制作文件系统..步骤如下: RDSIZE=1024 BLKSIZE=1024 # 为了测试,越小越好,创建一个1M 的ram disk,这样下载快(现在我只有串口) dd if=/dev/zero of=ramdisk.img bs=$BLKSIZE count=$RDSIZE #在其上制作ext2文件系统 /sbin/mke2fs -F -m 0 -b $BLKSIZE ramdisk.img $RDSIZE -F 强制运行,不管是否是块设备/或者已经安装 -m 0 设置预留块数量的百分比, 我们可不能浪费,所以一个都不保留....呵呵. -b 1024 一个block 1k 下载,运行,呵呵又出错了: try load ram disk /*这是自己加的调试信息*/ RAMDISK: ext2 filesystem found at block 0 /*我们制作的是ext2文件系统,这里也认出了*/ RAMDISK: Loading 4000KiB [1 disk] into ram disk... done. /*这里看出initrd真是加载成功了,到了ramdisk0中*/ load image ret 1 /*这是自己加的调试信息, mount失败*/ 原因很明显,我们根本没有编译ext2文件系统支持, 重新配置编译,运行.呵呵根文件系统安装真的成功了.不过,问题还没有完: VFS: Mounted root (ext2 filesystem). Freeing init memory: 92K Warning: unable to open an initial console. Failed to execute /linuxrc. Attempting defaults... Kernel panic - not syncing: No init found. Try passing init= option to kernel. 这个嘛,嘿嘿,我们的文件系统上啥都没有, 得搞个什么东西上去才行啊. -------------------------------------------------------------------------------- 16.1 initrd "hello world" 也试了直接编译busybox,可是看起来busybox启动了,但是没有任何输出.查busybox的说明,busybox建议最好自己用cross toolchain搞个hello world试试,看看你能不能运行,不行的话就别搞busybox了,肯定不行啦. 故而,写hello world一个(好久不hello world了): arm-linux-gcc -static -o test test.c arm-linux-objcopy -S test test_strip [这个去掉大部分没有用的段,大大减小bin的大小] 然后用arm-linux-readelf -A test 看看. (aeabi) 在rootfs上(ramdisk.img)建立/dev和必要的设备文件: mount -t ext2 ramdisk.img /mnt/initrd -o loop mkdir dev cp -a /dev/null dev cp -a /dev/console dev cp /cross/src/linux2.6.24.4/test_strip linuxrc [我们的hello world就是linuxrc,呵呵] sync umount /mnt/initrd gzip -9 ramdisk.img 下载, 启动.... 没有看到我熟悉的hello world VFS: Mounted root (ext2 filesystem). Freeing init memory: 92K selected clock c01adf24 (pclk) quot 26, calc 115740 google下,发现自己几乎犯过了所有的毛病,先是内核并不支持eabi串口无输出,就编译成支持eabi的内核,结果一样不幸.再google,发现float point emulation必须选一,我没选,选上重来. 最终float point终于解决了这个问题.内核中只能使用浮点模拟,运行大部分的 binaries 都需要浮点支持. 最后hello world终于搞定了. (经测试,没有eabi支持的内核照样可以hello world,以后再研究吧] VFS: Mounted root (ext2 filesystem). Freeing init memory: 92K selected clock c01adf24 (pclk) quot 26, calc 115740 hello world [这就是我们期待的输出...] -------------------------------------------------------------------------------- 16.2 busy box 先编译一个busybox用用,到 http://www.busybox.net/downloads/ 下载最新的1.9.2玩玩. 配置的时候选上 don't use /usr,不然make install 就完蛋了 make ARCH=arm CROSS_COMPILE=arm-linux- allnoconfig make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig /*只要ash mount echo ls cat ps dmsg sysctl, static 连接*/ make ARCH=arm CFLAGS="-I/cross/cross-arm/arm-linux/sys-include/" CROSS_COMPILE=arm-linux- make ARCH=arm CFLAGS="-I/cross/cross-arm/arm-linux/sys-include/" CROSS_COMPILE=arm-linux- install (需要把applets.c的警告去掉, glibc静态连接不好用,有bug,我们就试试,玩玩, 建议用ulibc,呵呵)然后就开始制作了: # 把我们先前制作的ramdisk.img加载到临时目录 mount ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0 # busy box 为我们建立好了各种符号连接,copy即可 cp -rf /cross/src/busybox-1.9.2/_install/* . 我们的rootfs就有了 bin sbin 和linuxrc # 然后在ramdisk上建立如下目录 mkdir dev # 创建相应的设备文件 cp -a /dev/console dev cp -a /dev/null dev #cp -a /dev/ttyS0 dev #没用s3c2410上串口设备是s3c2410_serial1 #cp -a /dev/ttyS1 dev # # 解除安装, 可以压缩的(俺的很小,不压缩也罢!) umount /mnt/initrdgzip -9 ramdisk.img #先搞这么多,先玩一把,下面是输出,爽啊 FS: Mounted root (ext2 filesystem). Freeing init memory: 92K selected clock c01a9df0 (pclk) quot 26, calc 115740 selected clock c01a9df0 (pclk) quot 26, calc 115740 init started: BusyBox v1.9.2 (2008-04-18 22:44:50 CST) selected clock c01a9df0 (pclk) quot 26, calc 115740 command='reboot' action=32 tty='' command='umount -a -r' actselected clock c01a9df0 (pclk) quot 26, calc 115740ion=64 tty='' command='init' action=128 tty='' command='-/bin/sh' action=4 tty='' command='-/bin/sh' action=4 tty='/dev/tty2' command='-/bin/sh' action=4 tty='/dev/tty3' command='-/bin/sh' action=4 tty='/dev/tty4' command='/etc/init.d/rcS' action=1 tty='' starting pid 720, tty '': '/etc/init.d/rcS' Cannot run '/etc/init.d/rcS': No such file or directory waiting for enter to start '/bin/sh'(pid 721, tty '') Please press Enter to activate this console. Can't open /dev/tty3: No such file or directory Can't open /dev/tty4: No such file or directory process '-/bin/sh' (pid 724) exited. Scheduling it for restart. process '-/bin/sh' (pid 726) exited. Scheduling it for restart. Can't open /dev/tty3: No such file or directory Can't open /dev/tty4: No such file or directory(不断重复中,看看需要做的东西还多着呢,不过ash已经启动了...ls可以工作的..呵呵) 这个事情有人报告是个bug: http://groups.google.com/group/linux.debian.bugs 关于这个问题的报告 既然如此,先升级的1.10.1看看, 测试结果: 1. applets.c的警告没有了 2. make ARCH=arm CROSS_COMPILE=arm-linux- CFLAGS=-I/cross/cross-arm/arm-linux/sys-linux这次busybox选的东西比较多,弄了个2M的ramdisk, 压缩后399k. ... 问题一样地...,好了自己写linuxrc吧. -------------------------------------------------------------------------------- 16.3 为busybox 配置脚本 1) 先简单看看各种文件系统的作用吧: tmpfs :/dev/shm #share mem对应的文件系统 devpts :/dev/pts #目前最常见的 pseudo 终端(PTYs)实现方式 sysfs :/sys #sysfs 包含进程相关的proc fs,设备相关的devfs以及为终端相关的devpty fs的信息,是当前趋势, 由内核内的kobj形成,udev严重依赖于sysf,能够在/dev目录下产生系统真正存在的设备,而不是一锅粥的方式 sysfs和udev配合,势必要击败devfs+devfsd(?) udev : # 需要/etc/udev/rules.d 目录和一堆的配置规则 devfs :/dev #和devfsd配合使用, devfsd将会负责创建“旧类型”兼容性设备节点;在注册/注销设备时执行自动化操作; 负责备份对根文件系统上某个目录的设备许可权和所有权的更改,以及其它更多功能 proc :proc # proc文件系统,不必多说
2) 简单的inuxrc: #!/bin/she cho echo "Open ARM AKAE www.linuxforum.net " echo mount -t proc /proc /proc mount -t sysfs sysfs /sys mount -t devpts devpts /dev/pts echo "Start mdev..." mdev -s /*mdev -s 把设备从sysfs 同步到/dev目录,需要要/etc/mdev.conf*/ init /*传递给脚本inittab...*/
3) /etc/inittab s3c2410_serial1::respawn:-/bin/sh #/*注意s3c2410上串口的设备名称是/dev/s3c2410_serial[0..3]*/
4) /etc/mdev.conf #空文件,有就行,用了在说吧
5) 启动信息 Open ARM www.linuxforum.net akae Start mdev... selected clock c0239b70 (pclk) quot 26, calc 115740 init started: Busyselected clock c0239b70 (pclk) quot 26, calc 115740 Box v1.10.1 (2008-04-19 15:27:01 CST) command='-/bin/sh ' action=2 tty='/dev/s3c2410_serial1' starting pid 229, tty '/dev/s3c2410_serial1': '-/bin/sh ' BusyBox v1.10.1 (2008-04-19 15:27:01 CST) built-in shell (ash) Enter 'help' for a list of built-in commands. #
6) Trouble shoot (1) /bin/sh: can't access tty; job control turned off busybox 通过系统控制台来做些工作, 如果我们的shell启动在 /dev/console (5,1)上, tty_io.c 函数open中noctty就被设置成1,导致这个问题,具体原因还不知道.总之要启动在一个真实的串口就没有关系. 解决方法可以通过busybox的init指定shell的设备, 注意, S3C2410上, 串口1是s3c2410_serial1.最终就是在etc/inittab中加入下面一句话就可以了: s3c2410_serial1::respawn:-/bin/sh #/*注意s3c2410上串口的设备名称是/dev/s3c2410_serial[0..3]*/ (2)busybox的init脚本问题 no tty3 tty4 之类 可以先启动mdev, (mdev -s), 然后运行init就可以了. 就是让/dev/tty0-4 都存在即可.... (具体原因未知) (3) 加载mtdblock3 的jffs2文件系统出现下述信息: jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x0001001c: 0x3c04 建议:将此分区数据清除,更近一步,加载一个image到这个分区. (4)下载了新内核,ramdisk找不到了 runaddr 和 ramdisk距离太近, 内核解压缩后覆盖了ramdisk... ft -------------------------------------------------------------------------------- 16.4 busy box的init 和各种脚本的分工 看到上面的脚本...写的不咋样,甚至都不符合各种配置文件的具体分工,很不规整. 下面来研究一个比较规整的方案. 下面是google结果(看busybox init的代码?,人生苦短,以后把) 1)在串口上启动 shell 为了避开在busybox "job control"的那个问题, 把/dev/console 直接改成 s3c2410_serial1的设备号,就是: mknod -m 666 console c 204 65 #204, 64,65,66 然后直接用busybox _install目录下的linuxrc,到busybox的一个软连接,也是启动init.
2)busybox init的流程 简单说,就是执行inittab里指定的命令.具体流程如下: a)如果没有/etc/inittab,则默认执行/etc/init.d/rcS (见下面说明) b)执行inittab中动作类型为wait的命令 c)执行inittab中动作类型是once的命令 d)然后进入等待和循环执过程: .如果一动作为respawn类型的进程退出了,就重新启动它. .如果动作为askfirst类型的程序退出了,也要重新启动它,但是先询问用户. busybox的inittab的语法格式是: (examples/inittab) id:runlevels:action:process id: busybox 用id域指定程序运行的控制台. 如果是串口控制台,id域 runlevels: busybox根本不使用它 action: 字段定义了该进程应该运行在何种状态下: sysinit :boot time时执行 (第一此运行init的时候?) respawn :init 监视其启动的进程,如果退出了,这种类型的进程需要重新启动 askfirst:和respawn差不多,就是要按下回车键才运行 wait : 要等待其运行完成 once : 只运行一次 ctrlaltdel : 这个组合键按下时启动.
busybox init的默认动作: ::sysinit:/etc/init.d/rcS ::askfirst:/bin/sh 如果/dev/console 不是串口终端则继续运行: tty2::askfirst:/bin/sh
3) inittab #在pc机上这个inittab本来是定义运行级别来着,这里就简单这么写吧 ::sysinit:/etc/init.d/rcS console::askfirst:-/bin/sh #我们把console换成了第一串口
4)etc/init.d/rcS #!/bin/sh echo echo "Open ARM AKAE-www.linuxforum.net " echo mount -t proc /proc /proc mount -t devpts devpts /dev/pts echo "Start mdev..." # read the busybox docs: mdev.txt, you need config your kernerl with hotplug surport mount -t sysfs sysfs /sys echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s
5)一个空的/etc/mdev.conf文件先对付着
6)/etc/fstab也没有做
-------------------------------------------------------------------------------- 17.内核启动信息分析 Uncompressing Linux...................................................... done , booting the kernel. [archarmootcompressedmisc.c: decompress_kernel()] Linux version 2.6.24.4 (root@localhost.localdomain) (gcc version 4.1.2) #7 Tue Apr 15 07:50:25 CST 2008 [init/main.c :start_kernel] CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177 [start_kernel->setup_arch->setup_processor] Machine: SMDK2410 [setup_arch->setup_machine] Converting old-style param struct to taglist [setup_arch->convert_to_tag_list->..->build_tag_list] ATAG_INITRD is deprecated; please update your bootloader. [setup_arch->parse_tags->parse_tag_initrd ] Memory policy: ECC disabled, Data cache writeback [setup_arch->paging_init->build_mem_type_table] On node 0 totalpages: 16384 [setup_arch->paging_init->bootmem_init_node->free_area_init_node-> calculate_node_totalpages] DMA zone: 128 pages used for memmap [setup_arch->..->free_area_init_node->free_area_init_core] DMA zone: 0 pages reserved DMA zone: 16256 pages, LIFO batch:3 Normal zone: 0 pages used for memmap Movable zone: 0 pages used for memmap CPU S3C2440A (id 0x32440001) [setup_arch->paging_init->devicemaps_init->mdesc->smdk2410_map_io->s3c24xx_init_io] S3C244X: core 400.000 MHz, memory 100.000 MHz, peripheral 50.000 MHz [..smdk2410_map_io->cpu_ids->s3c244x_init_clocks] S3C24XX Clocks, (c) 2004 Simtec Electronics [...s3c244x_init_clocks->s3c24xx_setup_clocks] CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on [...s3c244x_init_clocks->s3c2410_baseclk_add] CPU0: D VIVT write-back cache [setup_arch->cpu_init->dump_cpu_info] CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256 [start_kernel->build_all_zonelists] Kernel command line: root=/dev/ram init=/linuxrc initrd=0x30a00000,0x00100000 [start_kernel, print cmd line ] console=ttySAC1,115200 mem=65536K devfs=mount irq: clearing pending ext status 000dff00 [start_kernel->init_IRQ->init_arch_irq(mdesc->init_irq)->s3c24xx_init_irq] irq: clearing pending ext status 00001000 [init_arch_irq 在arm体系下是一个函数指针,setup_arch初始化其为mdesc->init_irq] irq: clearing subpending status 0000009a irq: clearing subpending status 00000092 PID hash table entries: 256 (order: 8, 1024 bytes) [start_kernel->pidhash_init] timer tcon=00090000, tcnt a2c1, tcfg 00000200,00000000, usec 00001eb8 [start_kernel->time_init->system_timer.init-> s3c2410_timer_init->s3c2410_timer_setup] [setup_arch中初始化全局函数指针 system_timer = mdesc->timer ,既s3c24xx_timer, arch/arm/plat-s3c24xx/timer.c] Console: colour dummy device 80x30 [start_kernel->console_init-> via lds __con_initcall_start ->vt:con_init] selected clock c0239b70 (pclk) quot 26, calc 115740 [console_initcall(s3c24xx_serial_initconsole)->register_console->console.setup-> s3c24xx_serial_console.setup(s3c24xx_serial_console_setup)->uart_set_options->s3c24xx_serial_set_termios->s3c24xx_serial_getclk]console [ttySAC1] enabled [console_initcall(s3c24xx_serial_initconsole)->register_console] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) [start_kernel->vfs_caches_init_early] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) Memory: 64MB = 64MB total [start_kernel->mem_init] Memory: 61440KB available (2060K code, 248K data, 108K init) Calibrating delay loop... 199.47 BogoMIPS (lpj=498688) [start_kernel->calibrate_delay] Mount-cache hash table entries: 512 [start_kernel-> vfs_caches_init->mnt_init] CPU: Testing write buffer coherency: ok [start_kernel->check_bugs->check_writebuffer_bugs] net_namespace: 64 bytes [start_kernel->rest_init->kernel_init->do_basic_setup->pure_initcall(net_ns_init)] NET: Registered protocol family 16 [core_initcall(netlink_proto_init)->sock_register] S3C2440: Initialising architecture [arch_initcall(s3c_arch_init)->(cpu->init())->cpu_ids->s3c2410_init] S3C2440: IRQ Support [arch_initcall(s3c2440_irq_init)->sysdev_driver_register->drv.add()->s3c2440_irq_add] S3C2440: Clock Support, DVS off [arch_initcall(s3c24xx_clk_driver)->s3c2440_clk_driver.s3c2440_clk_add] S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics [arch_initcall(s3c2410_dma_drvinit)->sysdev_driver_register-> DMA channel 0 at c4800000, irq 33 c2410_dma_driver.s3c2410_dma_add->s3c2410_dma_init->s3c24xx_dma_init] DMA channel 1 at c4800040, irq 34 DMA channel 2 at c4800080, irq 35 DMA channel 3 at c48000c0, irq 36 usbcore: registered new interface driver usbfs [subsys_initcall(usb_init)->usb_register(&usbfs_driver)->usb_register_driver] usbcore: registered new interface driver hub [subsys_initcall(usb_init)->usb_hub_init(&hub_driver)->usb_register_driver] usbcore: registered new device driver usb [subsys_initcall(usb_init)->usb_register_device_driver(&usb_generic_driver)] NET: Registered protocol family 2 [fs_initcall(inet_init)->sock_register] IP route cache hash table entries: 1024 (order: 0, 4096 bytes) [fs_initcall(inet_init)->ip_init->ip_rt_init ] TCP established hash table entries: 2048 (order: 2, 16384 bytes) [fs_initcall(inet_init)->ip_init->tcp_init] TCP bind hash table entries: 2048 (order: 1, 8192 bytes) [fs_initcall(inet_init)->ip_init->tcp_init] TCP: Hash tables configured (established 2048 bind 2048) [fs_initcall(inet_init)->ip_init->tcp_init] TCP reno registered [fs_initcall(inet_init)->ip_init->tcp_init->tcp_register_congestion_control] checking if image is initramfs...<5>it isn't (no cpio magic); looks like an initrd [rootfs_initcall(populate_rootfs)] Freeing initrd memory: 1024K [rootfs_initcall(populate_rootfs)->free_initrd->free_initrd_mem->free_area] NetWinder Floating Point Emulator V0.97 (extended precision) [module_init(fpe_init);] JFFS2 version 2.2. (NAND) 漏 2001-2006 Red Hat, Inc. [module_init(init_jffs2_fs);] io scheduler noop registered [module_init(noop_init)->elv_register] io scheduler anticipatory registered (default) [module_init(as_init)->elv_register] io scheduler deadline registered [module_init(deadline_init)->elv_register] io scheduler cfq registered [module_init(cfq_init)->elv_register] Serial: 8250/16550 driver $Revision: 1.90 $ 4 ports, IRQ sharing enabled [ module_init(serial8250_init)] s3c2440-uart.0: s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2440 [module_init(s3c24xx_serial_modinit)->s3c2440_serial_init-> s3c2440-uart.1: s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2440 s3c2440_serial_probe->uart_add_one_port-> s3c2440-uart.2: s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2440 uart_configure_port->uart_report_port] RAMDISK driver initialized: 2 RAM disks of 4096K size 1024 blocksize [module_init(rd_init);] loop: module loaded [module_init(loop_init);] S3C24XX NAND Driver, (c) 2004 Simtec Electronics [module_init(s3c2410_nand_init);] s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns [s3c2440_nand_probes->3c2410_nand_inithw] NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit) [ form above...->nand_scan_ident nand_get_flash_type] Scanning device for bad blocks [s3c24xx_nand_probe->nand_scan ->nand_scan_tail->chip->scan_bbt:..->create_bbt] Creating 4 MTD partitions on "NAND 64MiB 3,3V 8-bit": [s3c2410_nand_add_partition->add_mtd_partitions] 0x00000000-0x00030000 : "Boot Agent" 0x00030000-0x00200000 : "kernel" 0x00200000-0x02000000 : "ramfs" 0x02000000-0x04000000 : "extfs" ohci_hcd: 2006 August 04 USB 1.1 'Open' Host Controller (OHCI) Driver ohci_hcd: block sizes: ed 64 td 64 s3c2410-ohci s3c2410-ohci: s3c2410_start_hc: s3c2410-ohci s3c2410-ohci: S3C24XX OHCI s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1 s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000 s3c2410-ohci s3c2410-ohci: created debug files s3c2410-ohci s3c2410-ohci: OHCI controller state s3c2410-ohci s3c2410-ohci: OHCI 1.0, NO legacy support registers s3c2410-ohci s3c2410-ohci: control 0x083 HCFS=operational CBSR=3 s3c2410-ohci s3c2410-ohci: cmdstatus 0x00000 SOC=0 s3c2410-ohci s3c2410-ohci: intrstatus 0x00000004 SF s3c2410-ohci s3c2410-ohci: intrenable 0x8000005a MIE RHSC UE RD WDH s3c2410-ohci s3c2410-ohci: hcca frame #001e s3c2410-ohci s3c2410-ohci: roothub.a 02001202 POTPGT=2 NOCP NPS NDP=2(2) s3c2410-ohci s3c2410-ohci: roothub.b 00000000 PPCM=0000 DR=0000 s3c2410-ohci s3c2410-ohci: roothub.status 00008000 DRWE s3c2410-ohci s3c2410-ohci: roothub.portstatus [0] 0x00000100 PPS s3c2410-ohci s3c2410-ohci: roothub.portstatus [1] 0x00000100 PPSu sb usb1: default language 0x0409 usb usb1: uevent usb usb1: usb_probe_device usb usb1: configuration #1 chosen from 1 choice usb usb1: adding 1-0:1.0 (config #1, interface 0) usb 1-0:1.0: uevent hub 1-0:1.0: usb_probe_interface hub 1-0:1.0: usb_probe_interface - got id hub 1-0:1.0: USB hub found s3c2410-ohci s3c2410-ohci: s3c2410_hub_control(c3c2b400,0xa006,0x2900,0x0000,c3c19a84,000f) hub 1-0:1.0: 2 ports detected hub 1-0:1.0: standalone hub hub 1-0:1.0: no power switching (usb 1.0) hub 1-0:1.0: no over-current protection hub 1-0:1.0: power on to power good time: 4ms s3c2410-ohci s3c2410-ohci: s3c2410_hub_control(c3c2b400,0xa000,0x0000,0x0000,c3c19a40,0004) hub 1-0:1.0: local power source is good hub 1-0:1.0: trying to enable port power on non-switchable hub s3c2410-ohci s3c2410-ohci: s3c2410_hub_control(c3c2b400,0x2303,0x0008,0x0001,c3c19a40,0000) s3c2410-ohci s3c2410-ohci: s3c2410_hub_control(c3c2b400,0x2303,0x0008,0x0002,c3c19a40,0000) hub 1-0:1.0: state 7 ports 2 chg 0000 evt 0000 usb usb1: new device strings: Mfr=3, Product=2, SerialNumber=1 usb usb1: Product: S3C24XX OHCI usb usb1: Manufacturer: Linux 2.6.24.4 ohci_hcd usb usb1: SerialNumber: s3c24xx s3c2410_udc: debugfs dir creation failed -19 s3c2410-ohci s3c2410-ohci: s3c2410_hub_control(c3c2b400,0xa300,0x0000,0x0001,c3c6fe30,0004) s3c2410-ohci s3c2410-ohci: s3c2410_hub_control(c3c2b400,0xa300,0x0000,0x0002,c3c6fe30,0004) mice: PS/2 mouse device common for all mice [module_init(mousedev_init);] S3C24XX RTC, (c) 2004,2006 Simtec Electronics [module_init(s3c_rtc_init);] s3c2440-i2c s3c2440-i2c: slave address 0x10 [s3c2440_i2c_driver.s3c24xx_i2c_probe->s3c24xx_i2c_init] s3c2440-i2c s3c2440-i2c: bus frequency set to 390 KHz [同上] s3c2440-i2c s3c2440-i2c: i2c-0: S3C I2C adapter [s3c2440_i2c_driver.s3c24xx_i2c_probe] S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics [module_init(watchdog_init);] s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled TCP cubic registered [module_init(cubictcp_register)->tcp_register_congestion_control] drivers/rtc/hctosys.c: unable to open rtc device (rtc0) [late_initcall(rtc_hctosys)] RAMDISK: Compressed image found at block 0 [kernel_init->prepare_namespace->initrd_load->rd_load_image->identify_ramdisk_image] VFS: Mounted root (ext2 filesystem). [kernel_init->prepare_namespace->mount_root->mount_block_root->do_mount_root] Freeing init memory: 108K selected clock c0239b70 (pclk) quot 26, calc 115740 /*以下内容为busybox产生*/ selected clock c0239b70 (pclk) quot 26, calc 115740 init started: BusyBox v1.10.1 (2008-04-19 15:27:01 CST) selected clock c0239b70 (pclk) quot 26, calc 115740 starting pid 225, tty '': '/etc/init.d/rcS' Open ARM AKAE-www.linuxforum.net Start mdev... mount: mounting sysfs on /sys failed: Device or resource busy selected clock c0239b70 (pclk) quot 26, calc 115740 starting pid 236, tty '/dev/console': '-/bin/sh ' BusyBox v1.10.1 (2008-04-19 15:27:01 CST) built-in shell (msh) Enter 'help' for a list of built-in commands. -------------------------------------------------------------------------------- 18 .Uart devices register and driver select
1.machine和cpu的检测与uart设备名的设置 CPU S3C2440A (id 0x32440001) [setup_arch->paging_init->devicemaps_init->mdesc->smdk2410_map_io->s3c24xx_init_io] mdesc是SMDK2410,smdk2410_map_io->s3c24xx_init_io根据cpuid设置arm/plat-s3c24xx的全局变量cpu.然后mdk2410_map_io 调用s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));通过(cpu->init_uarts)(cfg, no);注册cpu特的定的uart资源. cpu_ids在arch/arm/plat-s3c24xx/cpu.c中,其中2440a的uart初始化函数是:
void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no) { s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); } void __init s3c24xx_init_uartdevs(char *name, struct s3c24xx_uart_resources *res, struct s3c2410_uartcfg *cfg, int no) { struct platform_device *platdev; struct s3c2410_uartcfg *cfgptr = uart_cfgs; struct s3c24xx_uart_resources *resp; int uart; memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { platdev = s3c24xx_uart_src[cfgptr->hwport]; resp = res + cfgptr->hwport; s3c24xx_uart_devs[uart] = platdev; /*uart的platform设备*/ platdev->name = name; /*platform 设备名称初始化为 "s3c2440-uart" */ platdev->resource = resp->resources; platdev->num_resources = resp->nr_resources; platdev->dev.platform_data = cfgptr; } nr_uarts = no; } 然后arch_initcall(s3c_arch_init);-> platform_add_devices(s3c24xx_uart_devs, nr_uarts);注册设备到 platform_bus_type. 2.console 设备初始化 selected clock c0239b70 (pclk) quot 26, calc 115740 [console_initcall(s3c24xx_serial_initconsole)->register_console->console.setup->s3c24xx_serial_console.setup(s3c24xx_serial_console_setup)->uart_set_options->s3c24xx_serial_set_termios->s3c24xx_serial_getclk]console [ttySAC1] enabled [console_initcall(s3c24xx_serial_initconsole)->register_console] 先是根据内核命令行选择并配置一个uart的驱动:static int s3c24xx_serial_initconsole(void){ struct s3c24xx_uart_info *info; struct platform_device *dev = s3c24xx_uart_devs[0]; /*上面已经初始化了*/ ...... } else if (strcmp(dev->name, "s3c2440-uart") == 0) { info = s3c2440_uart_inf_at; /*根据设备名称(通过cpu设这的,见上面第一节分析*/ } ........ s3c24xx_serial_console.data = &s3c24xx_uart_drv; /*选择uart驱动: ttySACx, 204,64-66*/ s3c24xx_serial_init_ports(info); register_console(&s3c24xx_serial_console); return 0; } 然后再根据命令行参数选择uart端口和波特率: void register_console(struct console *console) /*s3c24xx_serial_console*/ { ...... for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) { if (strcmp(console_cmdline[i].name, console->name) != 0) continue; if (console->index >= 0 && console->index != console_cmdline[i].index) continue; if (console->index < 0) console->index = console_cmdline[i].index; if (console->setup && console->setup(console, console_cmdline[i].options) != 0) /*options中存的是波特率*/ break; console->flags |= CON_ENABLED; console->index = console_cmdline[i].index; /*我们的选择是ttySAC1*/ if (i == selected_console) { console->flags |= CON_CONSDEV; preferred_console = selected_console; } break; } ............. } 3.uart设备驱动初始化 Serial: 8250/16550 driver $Revision: 1.90 $ 4 ports, IRQ sharing enabled [ module_init(serial8250_init)] s3c2440-uart.0: s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2440 s3c2440-uart.1: s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2440 s3c2440-uart.2: s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2440 上面三行信息就是uart初始化的信息,具体是在uart_report_port这个函数内打印的.但是通过什么路径确时费了番周折.串口初始化就是 module_init(s3c24xx_serial_modinit) static int __init s3c24xx_serial_modinit(void) { int ret; ret = uart_register_driver(&s3c24xx_uart_drv); /*选择uart驱动: ttySACx, 204,64-66,设备名称s3c2410_serial*/ if (ret < 0) { printk(KERN_ERR "failed to register UART driver"); return -1; } s3c2400_serial_init(); /*这里有4个cpu的串口初始化函数,到底那个可以成功?*/ s3c2410_serial_init(); /*这个在配置里选上了*/ s3c2412_serial_init(); s3c2440_serial_init(); /*这个也在配置里选上了*/ return 0; } 只从打印信息看,当然是选择了2440那个,下面从代码里找这个根据: static inline int s3c2410_serial_init(void) { return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf); } static inline int s3c2440_serial_init(void) { return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf); } static struct platform_driver s3c2410_serial_drv = { .probe = s3c2410_serial_probe, .remove = s3c24xx_serial_remove, .suspend = s3c24xx_serial_suspend, .resume = s3c24xx_serial_resume, .driver = { .name = "s3c2410-uart", .owner = THIS_MODULE, }, }; static struct platform_driver s3c2440_serial_drv = { .probe = s3c2440_serial_probe, .remove = s3c24xx_serial_remove, .suspend = s3c24xx_serial_suspend, .resume = s3c24xx_serial_resume, .driver = { .name = "s3c2440-uart", .owner = THIS_MODULE, }, }; 上面两个初始化函数其实都是调用: static int s3c24xx_serial_init-> int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; ...... return driver_register(&drv->driver); } driver_register->bus_add_driver-> int bus_add_driver(struct device_driver *drv) { ...... if (drv->bus->drivers_autoprobe) { /*默认是autoprobe*/ error = driver_attach(drv); if (error) goto out_unregister; } ...... } driver_attach->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);-> static int __driver_attach(struct device * dev, void * data) { struct device_driver * drv = data; ....... if (!dev->driver) /*这里就能找到在第一节中注册的uart dev*/ driver_probe_device(drv, dev); } ..... } int driver_probe_device(struct device_driver * drv, struct device * dev) { int ret = 0; if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; ret = really_probe(dev, drv); /* -> dev->bus->probe(dev)或者drv->probe(dev),这个bus没有probe*/ .... } 这里终于找到判定一个设备的驱动的条件:platform_bus_type ->platform_match->就是一个字符创的比较,看设备和驱动的名字是否一致: strncmp(pdev->name, drv->name, BUS_ID_SIZE) 翻翻上面注册的名字,当然是2440的驱动胜出. 所以probe就调用了: s3c2440_serial_probe->uart_add_one_port->uart_report_port. 4. uart设备注册后对console设备的影响(register_console只是早期注册给内核printk的) 第二节谈到console的注册: static int s3c24xx_serial_initconsole(void) { struct platform_device *dev = s3c24xx_uart_devs[0]; /*上面已经初始化了*/ ...... s3c24xx_serial_console.data = &s3c24xx_uart_drv; /*选择uart驱动: ttySACx, 204,64-66*/ register_console(&s3c24xx_serial_console); return 0; } static struct console s3c24xx_serial_console = { .name = S3C24XX_SERIAL_NAME, .device = uart_console_device, /*找到此console对应的tty*/ .flags = CON_PRINTBUFFER, .index = -1, .write = s3c24xx_serial_console_write, .setup = s3c24xx_serial_console_setup .data = &s3c24xx_uart_drv; }; static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .dev_name = "s3c2410_serial", .nr = 3, .cons = S3C24XX_SERIAL_CONSOLE, .driver_name = S3C24XX_SERIAL_NAME, .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR, }; 这个console只提供了最少的接口供printk使用.见register_console->release_console_sem. 然后,在第三节谈到的uart设备初始化中: static int __init s3c24xx_serial_modinit(void) { int ret; ret = uart_register_driver(&s3c24xx_uart_drv); /*初始化s3c24xx_uart_drv->tty_driver*/ /*初始化tty_driver后uart_console_device就能找到对应tty设备了*/ ... } static inline int s3c2440_serial_init->s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf)-> __driver_attach ->...->really_probe内,将注册的uart platform_device设备进行进一步初始化:dev->driver = drv;要注意s3c2440_serial_drv是platform_driver,而不是uart_driver:s3c24xx_uart_drv().
|