Stage2源码分析
start_armboot函数的路径:/lib_arm/board.c
void start_armboot (void) { init_fnc_t **init_fnc_ptr; char *s; #if defined(CONFIG_VFD) || defined(CONFIG_LCD) unsigned long addr; #endif #if defined(CONFIG_MINI2440_LED) struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio(); #endif /* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t)); gd->flags |= GD_FLG_RELOC; monitor_flash_len = _bss_start - _armboot_start; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } /* armboot_start is defined in the board-specific linker script */ mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN); #ifndef CONFIG_SYS_NO_FLASH /* configure available FLASH banks */ display_flash_config (flash_init ()); #endif /* CONFIG_SYS_NO_FLASH */ #ifdef CONFIG_VFD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif /* * reserve memory for VFD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); vfd_setmem (addr); gd->fb_base = addr; #endif /* CONFIG_VFD */ #ifdef CONFIG_LCD /* board init may have inited fb_base */ if (!gd->fb_base) { # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif /* * reserve memory for LCD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); lcd_setmem (addr); gd->fb_base = addr; } #endif /* CONFIG_LCD */ #if defined(CONFIG_CMD_NAND) puts ("NAND: "); nand_init(); /* go init the NAND */ #endif #if defined(CONFIG_CMD_ONENAND) onenand_init(); #endif #ifdef CONFIG_HAS_DATAFLASH AT91F_DataflashInit(); dataflash_print_info(); #endif /* initialize environment */ env_relocate (); #ifdef CONFIG_VFD /* must do this after the framebuffer is allocated */ drv_vfd_init(); #endif /* CONFIG_VFD */ #ifdef CONFIG_SERIAL_MULTI serial_initialize(); #endif /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); stdio_init (); /* get the devices list going. */ jumptable_init (); #if defined(CONFIG_API) /* Initialize API */ api_init (); #endif console_init_r (); /* fully init console as a device */ #if defined(CONFIG_ARCH_MISC_INIT) /* miscellaneous arch dependent initialisations */ arch_misc_init (); #endif #if defined(CONFIG_MISC_INIT_R) /* miscellaneous platform dependent initialisations */ misc_init_r (); #endif /* enable exceptions */ enable_interrupts (); #ifdef CONFIG_USB_DEVICE usb_init_slave(); #endif /* Perform network card initialisation if necessary */ #ifdef CONFIG_DRIVER_TI_EMAC /* XXX: this needs to be moved to board init */ extern void davinci_eth_set_mac_addr (const u_int8_t *addr); if (getenv ("ethaddr")) { uchar enetaddr[6]; eth_getenv_enetaddr("ethaddr", enetaddr); davinci_eth_set_mac_addr(enetaddr); } #endif #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96) /* XXX: this needs to be moved to board init */ if (getenv ("ethaddr")) { uchar enetaddr[6]; eth_getenv_enetaddr("ethaddr", enetaddr); smc_set_mac_addr(enetaddr); } #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ /* Initialize from environment */ if ((s = getenv ("loadaddr")) != NULL) { load_addr = simple_strtoul (s, NULL, 16); } #if defined(CONFIG_CMD_NET) if ((s = getenv ("bootfile")) != NULL) { copy_filename (BootFile, s, sizeof (BootFile)); } #endif #ifdef BOARD_LATE_INIT board_late_init (); #endif #ifdef CONFIG_GENERIC_MMC puts ("MMC: "); mmc_initialize (gd->bd); #endif #ifdef CONFIG_BITBANGMII bb_miiphy_init(); #endif #if defined(CONFIG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts ("Net: "); #endif eth_initialize(gd->bd); #if defined(CONFIG_RESET_PHY_R) debug ("Reset Ethernet PHY\n"); reset_phy(); #endif #endif #if defined(CONFIG_MINI2440_LED) gpio->GPBDAT = 0x0; //tekkamanninja #endif #if defined(CONFIG_CFB_CONSOLE) printf ("%s\n", version_string); printf ("modified by kangear\n(kangear@163.com)\n"); printf ("Love Linux forever!!\n"); #endif /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop (); } /* NOTREACHED - no way out of command loop except booting */ }
下面来分析代码:
struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
又有
static inline struct s3c24x0_gpio *s3c24x0_get_base_gpio(void) { return (struct s3c24x0_gpio *)S3C24X0_GPIO_BASE; }
<span style="line-height: 25.98958396911621px; font-size: 12pt; font-family: 宋体;">#define S3C24X0_GPIO_BASE</span><span style="line-height: 25.98958396911621px; font-size: 12pt; font-family: 宋体;"> </span><span style="line-height: 25.98958396911621px; font-size: 12pt; font-family: 宋体;">0x56000000</span>
返回返回0x56000000,赋给gpio指针。
/* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
看一下gd_t的定义,在include/asm-arm/global_data.h文件中:
#ifndef __ASM_GBL_DATA_H #define __ASM_GBL_DATA_H /* * The following data structure is placed in some memory wich is * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or * some locked parts of the data cache) to allow for a minimum set of * global variables during system initialization (until we have set * up the memory controller so that we can use RAM). * * Keep it *SMALL* and remember to set CONFIG_SYS_GBL_DATA_SIZE > sizeof(gd_t) */ typedef struct global_data { bd_t *bd; unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long fb_base; /* base address of frame buffer */ #ifdef CONFIG_VFD unsigned char vfd_type; /* display type */ #endif #if 0 unsigned long cpu_clk; /* CPU clock in Hz! */ unsigned long bus_clk; phys_size_t ram_size; /* RAM size */ unsigned long reset_status; /* reset status register at boot */ #endif void **jt; /* jump table */ } gd_t;
sizeof(gd_t)就是gd_t结构体的大小,所以_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)就相当于减去malloc area再减去gd_t结构体的大小,把此处地址赋值给指针gd。
需要注意的一点是
bd_t *bd;
bd是一个结构体指针,指针大小为4字节,并不是该结构体嵌套在gd_t结构体中。
/* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory");
memory 强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu将registers,cache中的数据用于去优化指令,而避免去访问内存。
memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t)); gd->flags |= GD_FLG_RELOC; monitor_flash_len = _bss_start - _armboot_start; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
对gd_t结构体进行初始化,首先清0;
确定bd_t结构体的位置,并把该位置赋值给指针gd->bd;
将bd_t结构体清0;
gd->flags应该是表示是否要relocate code
#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */
monitor_flash_len = _bss_start - _armboot_start;
Uboot镜像的大小,以下是一个内存的示意图
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
在board.c中,定义init_sequence
typedef int (init_fnc_t) (void);
又有:
init_fnc_t *init_sequence[] = { #if defined(CONFIG_ARCH_CPU_INIT) arch_cpu_init, /* basic arch cpu dependent setup */ #endif board_init, /* basic board dependent setup */ #if defined(CONFIG_USE_IRQ) interrupt_init, /* set up exceptions */ #endif timer_init, /* initialize timer */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c, #endif dram_init, /* configure available RAM banks */ #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI) arm_pci_init, #endif display_dram_config, NULL, };
先是定义一个函数类型typedef int (init_fnc_t) (void);
该函数的返回值int,形参为void。
init_sequence为函数指针数组,遍历该数组即执行该数组中所有元素各自指向的函数。
下面来看一下指针数组中的各个函数:
arch_cpu_init
CONFIG_ARCH_CPU_INIT没有找到该定义,则不会执行arch_cpu_init;
board_init
在/board/tekkamanninja/mini2440/mini2440.c中:
/* * Miscellaneous platform dependent initialisations */ int board_init (void) { struct s3c24x0_clock_power * const clk_power = s3c24x0_get_base_clock_power(); struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio(); /* to reduce PLL lock time, adjust the LOCKTIME register */ clk_power->LOCKTIME = 0xFFFFFF; /* configure MPLL */ clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV); /* some delay between MPLL and UPLL */ delay (4000); /* configure UPLL */ clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV); /* some delay between MPLL and UPLL */ delay (8000); /* set up the I/O ports */ gpio->GPACON = 0x007FFFFF; #if defined(CONFIG_MINI2440) gpio->GPBCON = 0x00295551; #else gpio->GPBCON = 0x00044556; #endif gpio->GPBUP = 0x000007FF; #if defined(CONFIG_MINI2440) gpio->GPCCON = 0xAAAAA6AA; gpio->GPCDAT &= ~(1<<5); #else gpio->GPCCON = 0xAAAAAAAA; #endif gpio->GPCUP = 0xFFFFFFFF; gpio->GPDCON = 0xAAAAAAAA; gpio->GPDUP = 0xFFFFFFFF; gpio->GPECON = 0xAAAAAAAA; gpio->GPEUP = 0x0000FFFF; gpio->GPFCON = 0x000055AA; gpio->GPFUP = 0x000000FF; gpio->GPGCON = 0xFF95FF3A; gpio->GPGUP = 0x0000FFFF; gpio->GPHCON = 0x0016FAAA; gpio->GPHUP = 0x000007FF; gpio->EXTINT0=0x22222222; gpio->EXTINT1=0x22222222; gpio->EXTINT2=0x22222222; #if defined(CONFIG_S3C2440) /* arch number of S3C2440-Board */ gd->bd->bi_arch_number = MACH_TYPE_MINI2440 ; #endif /* adress of boot parameters */ gd->bd->bi_boot_params = 0x30000100; icache_enable(); dcache_enable(); #if defined(CONFIG_MINI2440_LED) gpio->GPBDAT = 0x00000180; #endif return 0; }<span style="font-family: 宋体; font-size: 12pt; line-height: 19.5pt;"> </span>
设置时钟,引脚初始化,arch number。
gd->bd->bi_boot_params存放参数的地址,这些参数用来启动Linux内核。
I/D Cache 使能。
interrupt_init
#if defined(CONFIG_USE_IRQ) interrupt_init, /* set up exceptions */ #endif<span style="font-family: 宋体; font-size: 12pt; line-height: 19.5pt;"> </span>
在mini2440.h中
//#undef CONFIG_USE_IRQ /* we don't need IRQ/FIQ stuff */ #define CONFIG_USB_DEVICE 1 #ifdef CONFIG_USB_DEVICE #define CONFIG_USE_IRQ 1 #endif
所以interrupt_init是执行的:
在/lib_arm/interrupts.c中
#ifdef CONFIG_USE_IRQ DECLARE_GLOBAL_DATA_PTR; int interrupt_init (void) { /* * setup up stacks if necessary */ IRQ_STACK_START = _armboot_start - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_GBL_DATA_SIZE - 4; FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; return arch_interrupt_init(); }
在include/configs/mini2440.h文件中:
#ifdef CONFIG_USE_IRQ #define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */ #define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */ #endif
在cpu/arm920t/s3c24x0interrupts.c文件中:
int arch_interrupt_init (void) { return 0; }
所以这里只是对IRQ_STACK_START和FIQ_STACK_START这两个宏进行赋值,这两个宏是在哪定义的,看start.S。
#ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START IRQ_STACK_START: .word 0x0badc0de /* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif
之前只是随便赋了一个值0x0badc0de,现在把他们重新赋值而已。
timer_init
对定时器的一些初始化,定时器了解的不多,先放这边以后补。。。。
env_init:
/common/env_nand.c /* this is called before nand_init() * so we can't read Nand to validate env data. * Mark it OK for now. env_relocate() in env_common.c * will call our relocate function which does the real * validation. * * When using a NAND boot image (like sequoia_nand), the environment * can be embedded or attached to the U-Boot image in NAND flash. This way * the SPL loads not only the U-Boot image from NAND but also the * environment. */ int env_init(void) { #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST) int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1; #ifdef CONFIG_ENV_OFFSET_REDUND env_t *tmp_env2; tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE); crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); #endif tmp_env1 = env_ptr; crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); if (!crc1_ok && !crc2_ok) { gd->env_addr = 0; gd->env_valid = 0; return 0; } else if (crc1_ok && !crc2_ok) { gd->env_valid = 1; } #ifdef CONFIG_ENV_OFFSET_REDUND else if (!crc1_ok && crc2_ok) { gd->env_valid = 2; } else { /* both ok - check serial */ if(tmp_env1->flags == 255 && tmp_env2->flags == 0) gd->env_valid = 2; else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) gd->env_valid = 1; else if(tmp_env1->flags > tmp_env2->flags) gd->env_valid = 1; else if(tmp_env2->flags > tmp_env1->flags) gd->env_valid = 2; else /* flags are equal - almost impossible */ gd->env_valid = 1; } if (gd->env_valid == 2) env_ptr = tmp_env2; else #endif if (gd->env_valid == 1) env_ptr = tmp_env1; gd->env_addr = (ulong)env_ptr->data; #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ return (0); }
本意上是对环境变量进行初始化,但是根据注释可以发现,这个函数是在nand_init之前调用的,所以不可能把Nand中的环境变量Copy到SDRAM中,这里要做的工作只是对gd结构体的一些变量进行初始化,方便之后的relocation。
ENV_IS_EMBEDDED和CONFIG_NAND_ENV_DST都没有被定义。
注意注释:大意是当uboot从nand启动时,环境变量有两种存在方式embedded or attached,显然这里不是embedded(ENV_IS_EMBEDDED没有被定义),属于attached的形式。
什么是embedded和attached呢?有待研究......
gd->env_addr指向的是环境变量的首地址。
gd->env_valid被赋值为1,在relocate的时候会用到。
来看一下default_environment:
/commom/env_common.c
uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif #ifdef CONFIG_RAMBOOTCOMMAND "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" #endif #ifdef CONFIG_NFSBOOTCOMMAND "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0" #endif #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0" #endif #ifdef CONFIG_LOADS_ECHO "loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0" #endif #ifdef CONFIG_ETHADDR "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0" #endif #ifdef CONFIG_ETH1ADDR "eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0" #endif #ifdef CONFIG_ETH2ADDR "eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0" #endif #ifdef CONFIG_ETH3ADDR "eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0" #endif #ifdef CONFIG_ETH4ADDR "eth4addr=" MK_STR(CONFIG_ETH4ADDR) "\0" #endif #ifdef CONFIG_ETH5ADDR "eth5addr=" MK_STR(CONFIG_ETH5ADDR) "\0" #endif #ifdef CONFIG_IPADDR "ipaddr=" MK_STR(CONFIG_IPADDR) "\0" #endif #ifdef CONFIG_SERVERIP "serverip=" MK_STR(CONFIG_SERVERIP) "\0" #endif #ifdef CONFIG_SYS_AUTOLOAD "autoload=" CONFIG_SYS_AUTOLOAD "\0" #endif #ifdef CONFIG_PREBOOT "preboot=" CONFIG_PREBOOT "\0" #endif #ifdef CONFIG_ROOTPATH "rootpath=" MK_STR(CONFIG_ROOTPATH) "\0" #endif #ifdef CONFIG_GATEWAYIP "gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0" #endif #ifdef CONFIG_NETMASK "netmask=" MK_STR(CONFIG_NETMASK) "\0" #endif #ifdef CONFIG_HOSTNAME "hostname=" MK_STR(CONFIG_HOSTNAME) "\0" #endif #ifdef CONFIG_BOOTFILE "bootfile=" MK_STR(CONFIG_BOOTFILE) "\0" #endif #ifdef CONFIG_LOADADDR "loadaddr=" MK_STR(CONFIG_LOADADDR) "\0" #endif #ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=1\0" #endif #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0" #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" };
default_environment结构体中保存的是默认环境变量,每个环境变量之间以”\0”隔开,整个结构体以”\0\0”结尾。
init_baudrate
/lib_arm/board.c文件中: static int init_baudrate (void) { char tmp[64]; /* long enough for environment variables */ int i = getenv_r ("baudrate", tmp, sizeof (tmp)); gd->bd->bi_baudrate = gd->baudrate = (i > 0) ? (int) simple_strtoul (tmp, NULL, 10) : CONFIG_BAUDRATE; return (0); }
在环境变量中查找baudrate字符串,看看有没有设置正确的波特率,有的话就设置上。
serial_init
在drivers/serial/serial_s3c 24x0.c文件中:
#if !defined(CONFIG_SERIAL_MULTI) /* Initialise the serial port. The settings are always 8 data bits, no parity, * 1 stop bit, no start bits. */ int serial_init(void) { return serial_init_dev(UART_NR); } #endif
同样在这个文件中还有:
/* Initialise the serial port. The settings are always 8 data bits, no parity, * 1 stop bit, no start bits. */ static int serial_init_dev(const int dev_index) { struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); #ifdef CONFIG_HWFLOW hwflow = 0; /* turned off by default */ #endif /* FIFO enable, Tx/Rx FIFO clear */ writel(0x07, &uart->UFCON); writel(0x0, &uart->UMCON); /* Normal,No parity,1 stop,8 bit */ writel(0x3, &uart->ULCON); /* * tx=level,rx=edge,disable timeout int.,enable rx error int., * normal,interrupt or polling */ writel(0x245, &uart->UCON); #ifdef CONFIG_HWFLOW writel(0x1, &uart->UMCON); /* RTS up */ #endif /* FIXME: This is sooooooooooooooooooo ugly */ #if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2) /* we need auto hw flow control on the gsm and gps port */ if (dev_index == 0 || dev_index == 1) writel(0x10, &uart->UMCON); #endif _serial_setbrg(dev_index); return (0); }
下面分析代码:
serial_init_dev(UART_NR);中的UART_NR是什么?
找到:
#ifdef CONFIG_SERIAL1 #define UART_NR S3C24X0_UART0
在/include/s3c2410.h有:
enum s3c24x0_uarts_nr { S3C24X0_UART0, S3C24X0_UART1, S3C24X0_UART2 };
枚举类型的第一个成员的默认值是0,所以这里UART_NR的值应该是0,表示对标号为0的串口进行操作。
下面看serial_init_dev,他的注释说明了这个函数是用于串口的初始化,数据位为8位,没有奇偶校验(parity