Mini2440之uboot移植之源码分析board_init_r(四)
board_init_r和board_init_f差不多,都是执行一个循环。这里是循环执行init_sequence_r[]里的函数指针。
一、board_init_r(common/board_r.c)
void board_init_r(gd_t *new_gd, ulong dest_addr) { #ifdef CONFIG_NEEDS_MANUAL_RELOC int i; #endif #ifdef CONFIG_AVR32 mmu_init_r(dest_addr); #endif #if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) gd = new_gd; #endif #ifdef CONFIG_NEEDS_MANUAL_RELOC for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++) init_sequence_r[i] += gd->reloc_off; #endif if (initcall_run_list(init_sequence_r)) hang(); /* NOTREACHED - run_main_loop() does not return */ hang(); }
在上一篇博客中我们已经介绍了该函数调用表时会传入两个参数:
- new_gd:为u-boot重定位后的新地址的gd结构;
- dest_addr:u-boot重定位后的地址;
然后循环遍历init_sequence_r中的每一个函数,并执行。
二、init_sequence_r
/* * Over time we hope to remove these functions with code fragments and * stub funtcions, and instead call the relevant function directly. * * We also hope to remove most of the driver-related init and do it if/when * the driver is later used. * * TODO: perhaps reset the watchdog in the initcall function after each call? */ init_fnc_t init_sequence_r[] = { initr_trace, initr_reloc, /* TODO: could x86/PPC have this also perhaps? */ #ifdef CONFIG_ARM initr_caches, /* Note: For Freescale LS2 SoCs, new MMU table is created in DDR. * A temporary mapping of IFC high region is since removed, * so environmental variables in NOR flash is not availble * until board_init() is called below to remap IFC to high * region. */ #endif initr_reloc_global_data, #if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500) initr_unlock_ram_in_cache, #endif initr_barrier, initr_malloc, initr_console_record, #ifdef CONFIG_SYS_NONCACHED_MEMORY initr_noncached, #endif bootstage_relocate, #ifdef CONFIG_DM initr_dm, #endif initr_bootstage, #if defined(CONFIG_ARM) || defined(CONFIG_NDS32) board_init, /* Setup chipselects */ #endif /* * TODO: printing of the clock inforamtion of the board is now * implemented as part of bdinfo command. Currently only support for * davinci SOC's is added. Remove this check once all the board * implement this. */ #ifdef CONFIG_CLOCKS set_cpu_clk_info, /* Setup clock information */ #endif #ifdef CONFIG_EFI_LOADER efi_memory_init, #endif stdio_init_tables, initr_serial, initr_announce, INIT_FUNC_WATCHDOG_RESET #ifdef CONFIG_NEEDS_MANUAL_RELOC initr_manual_reloc_cmdtable, #endif #if defined(CONFIG_PPC) || defined(CONFIG_M68K) initr_trap, #endif #ifdef CONFIG_ADDR_MAP initr_addr_map, #endif #if defined(CONFIG_BOARD_EARLY_INIT_R) board_early_init_r, #endif INIT_FUNC_WATCHDOG_RESET #ifdef CONFIG_LOGBUFFER initr_logbuffer, #endif #ifdef CONFIG_POST initr_post_backlog, #endif INIT_FUNC_WATCHDOG_RESET #ifdef CONFIG_SYS_DELAYED_ICACHE initr_icache_enable, #endif #if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT) /* * Do early PCI configuration _before_ the flash gets initialised, * because PCU ressources are crucial for flash access on some boards. */ initr_pci, #endif #ifdef CONFIG_WINBOND_83C553 initr_w83c553f, #endif #ifdef CONFIG_ARCH_EARLY_INIT_R arch_early_init_r, #endif power_init_board, #ifndef CONFIG_SYS_NO_FLASH initr_flash, #endif INIT_FUNC_WATCHDOG_RESET #if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) || \ defined(CONFIG_SPARC) /* initialize higher level parts of CPU like time base and timers */ cpu_init_r, #endif #ifdef CONFIG_PPC initr_spi, #endif #ifdef CONFIG_CMD_NAND initr_nand, #endif #ifdef CONFIG_CMD_ONENAND initr_onenand, #endif #ifdef CONFIG_GENERIC_MMC initr_mmc, #endif #ifdef CONFIG_HAS_DATAFLASH initr_dataflash, #endif initr_env, #ifdef CONFIG_SYS_BOOTPARAMS_LEN initr_malloc_bootparams, #endif INIT_FUNC_WATCHDOG_RESET initr_secondary_cpu, #if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET) mac_read_from_eeprom, #endif INIT_FUNC_WATCHDOG_RESET #if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT) /* * Do pci configuration */ initr_pci, #endif stdio_add_devices, initr_jumptable, #ifdef CONFIG_API initr_api, #endif console_init_r, /* fully init console as a device */ #ifdef CONFIG_DISPLAY_BOARDINFO_LATE show_board_info, #endif #ifdef CONFIG_ARCH_MISC_INIT arch_misc_init, /* miscellaneous arch-dependent init */ #endif #ifdef CONFIG_MISC_INIT_R misc_init_r, /* miscellaneous platform-dependent init */ #endif INIT_FUNC_WATCHDOG_RESET #ifdef CONFIG_CMD_KGDB initr_kgdb, #endif interrupt_init, #if defined(CONFIG_ARM) || defined(CONFIG_AVR32) initr_enable_interrupts, #endif #if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K) timer_init, /* initialize timer */ #endif #if defined(CONFIG_STATUS_LED) initr_status_led, #endif /* PPC has a udelay(20) here dating from 2002. Why? */ #ifdef CONFIG_CMD_NET initr_ethaddr, #endif #ifdef CONFIG_BOARD_LATE_INIT board_late_init, #endif #if defined(CONFIG_CMD_AMBAPP) ambapp_init_reloc, #if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP) initr_ambapp_print, #endif #endif #ifdef CONFIG_CMD_SCSI INIT_FUNC_WATCHDOG_RESET initr_scsi, #endif #ifdef CONFIG_CMD_DOC INIT_FUNC_WATCHDOG_RESET initr_doc, #endif #ifdef CONFIG_BITBANGMII initr_bbmii, #endif #ifdef CONFIG_CMD_NET INIT_FUNC_WATCHDOG_RESET initr_net, #endif #ifdef CONFIG_POST initr_post, #endif #if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE) initr_pcmcia, #endif #if defined(CONFIG_CMD_IDE) initr_ide, #endif #ifdef CONFIG_LAST_STAGE_INIT INIT_FUNC_WATCHDOG_RESET /* * Some parts can be only initialized if all others (like * Interrupts) are up and running (i.e. the PC-style ISA * keyboard). */ last_stage_init, #endif #ifdef CONFIG_CMD_BEDBUG INIT_FUNC_WATCHDOG_RESET initr_bedbug, #endif #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER) initr_mem, #endif #ifdef CONFIG_PS2KBD initr_kbd, #endif #if defined(CONFIG_SPARC) prom_init, #endif run_main_loop, };
通过反汇编代码我们可以确定实际定义了哪些函数:
0007220c <init_sequence_r>: 7220c: 0000eab0 .word 0x0000eab0 initr_trace 72210: 0000eab8 .word 0x0000eab8 initr_reloc 72214: 0000ec88 .word 0x0000ec88 initr_caches 72218: 0000ead0 .word 0x0000ead0 initr_reloc_global_data 7221c: 0000eaf8 .word 0x0000eaf8 initr_barrier 72220: 0000ec6c .word 0x0000ec6c initr_malloc 72224: 0000eb00 .word 0x0000eb00 initr_console_record 72228: 0000ea8c .word 0x0000ea8c bootstage_relocate 7222c: 0000ec58 .word 0x0000ec58 initr_bootstage 72230: 0000111c .word 0x0000111c board_init 72234: 000163c8 .word 0x000163c8 stdio_init_tables 72238: 0000ec48 .word 0x0000ec48 initr_serial 7223c: 0000eb10 .word 0x0000eb10 initr_announce 72240: 0000eb08 .word 0x0000eb08 power_init_board 72244: 0000ebcc .word 0x0000ebcc initr_flash 72248: 0000ebb0 .word 0x0000ebb0 initr_nand 7224c: 0000eb80 .word 0x0000eb80 initr_env 72250: 0000eaa0 .word 0x0000eaa0 initr_secondary_cpu 72254: 00016488 .word 0x00016488 stdio_add_devices 72258: 0000eb70 .word 0x0000eb70 initr_jumptable 7225c: 000146e0 .word 0x000146e0 console_init_r 72260: 00000ae0 .word 0x00000ae0 interrupt_init 72264: 0000eb60 .word 0x0000eb60 initr_enable_interrupts 72268: 0000eb40 .word 0x0000eb40 initr_ethaddr 7226c: 0000eb24 .word 0x0000eb24 initr_net 72270: 0000eb18 .word 0x0000eb18 run_main_loop
三、各个函数指针
3.1 initr_trace(common/board_r.c)
static int initr_trace(void) { #ifdef CONFIG_TRACE trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE); #endif return 0; }
3.2 initr_reloc(common/board_r.c)
static int initr_reloc(void) { /* tell others: relocation done */ gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT; return 0; }
常量GD_FLG_RELOC 和GD_FLG_FULL_MALLOC_INIT定义如下:
/* * Global Data Flags - the top 16 bits are reserved for arch-specific flags */ #define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */ #define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */ #define GD_FLG_SILENT 0x00004 /* Silent mode */ #define GD_FLG_POSTFAIL 0x00008 /* Critical POST test failed */ #define GD_FLG_POSTSTOP 0x00010 /* POST seqeunce aborted */ #define GD_FLG_LOGINIT 0x00020 /* Log Buffer has been initialized */ #define GD_FLG_DISABLE_CONSOLE 0x00040 /* Disable console (in & out) */ #define GD_FLG_ENV_READY 0x00080 /* Env. imported into hash table */ #define GD_FLG_SERIAL_READY 0x00100 /* Pre-reloc serial console ready */ #define GD_FLG_FULL_MALLOC_INIT 0x00200 /* Full malloc() is ready */ #define GD_FLG_SPL_INIT 0x00400 /* spl_init() has been called */ #define GD_FLG_SKIP_RELOC 0x00800 /* Don't relocate */ #define GD_FLG_RECORD 0x01000 /* Record console */
设置gd->flags标志位第1位、第10位为1。
3.3 initr_caches(common/board_r.c)
static int initr_caches(void) { /* Enable caches */ enable_caches(); return 0; }
enable_caches()使能缓存,定义在arch/arm/lib/cache.c:
/* * Default implementation of enable_caches() * Real implementation should be in platform code */ __weak void enable_caches(void) { puts("WARNING: Caches not enabled\n"); }
3.4 initr_reloc_global_data(common/board_r.c)
static int initr_reloc_global_data(void) { #ifdef __ARM__ monitor_flash_len = _end - __image_copy_start; #elif defined(CONFIG_NDS32) monitor_flash_len = (ulong)&_end - (ulong)&_start; #elif !defined(CONFIG_SANDBOX) && !defined(CONFIG_NIOS2) monitor_flash_len = (ulong)&__init_end - gd->relocaddr; #endif #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) /* * The gd->cpu pointer is set to an address in flash before relocation. * We need to update it to point to the same CPU entry in RAM. * TODO: why not just add gd->reloc_ofs? */ gd->arch.cpu += gd->relocaddr - CONFIG_SYS_MONITOR_BASE; /* * If we didn't know the cpu mask & # cores, we can save them of * now rather than 'computing' them constantly */ fixup_cpu(); #endif #ifdef CONFIG_SYS_EXTRA_ENV_RELOC /* * Some systems need to relocate the env_addr pointer early because the * location it points to will get invalidated before env_relocate is * called. One example is on systems that might use a L2 or L3 cache * in SRAM mode and initialize that cache from SRAM mode back to being * a cache in cpu_init_r. */ gd->env_addr += gd->relocaddr - CONFIG_SYS_MONITOR_BASE; #endif #ifdef CONFIG_OF_EMBED /* * The fdt_blob needs to be moved to new relocation address * incase of FDT blob is embedded with in image */ gd->fdt_blob += gd->reloc_off; #endif #ifdef CONFIG_EFI_LOADER efi_runtime_relocate(gd->relocaddr, NULL); #endif return 0; }
全局变量monitor_flash_len定义:
ulong monitor_flash_len;
其中_end和rel_dyn_end 值是一样的。
3.5 initr_barrier(common/board_r.c)
static int initr_barrier(void) { #ifdef CONFIG_PPC /* TODO: Can we not use dmb() macros for this? */ asm("sync ; isync"); #endif return 0; }
3.6 initr_malloc(common/board_r.c)
static int initr_malloc(void) { ulong malloc_start; #ifdef CONFIG_SYS_MALLOC_F_LEN debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr, gd->malloc_ptr / 1024); #endif /* The malloc area is immediately below the monitor copy in DRAM */ malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN; mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN), TOTAL_MALLOC_LEN); return 0; }
从board_init_f画出的内存图可知,u-boot重定位地址的下面就是预留的堆区。
设置malloc的起始地址,malloc_start设置为u-boot重定位后的地址-TOTAL_MALLOC_LEN,TOTAL_MALLOC_LEN之前有介绍4MB。
然后填充了三个全局变量并将堆区全部清零:
void mem_malloc_init(ulong start, ulong size) { mem_malloc_start = start; mem_malloc_end = start + size; mem_malloc_brk = start; debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start, mem_malloc_end); #ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT /* 定义这个宏则堆区初始化为0,否则不初始化 */ memset((void *)mem_malloc_start, 0x0, size); #endif malloc_bin_reloc(); }
3.7 initr_console_record(common/board_r.c)
static int initr_console_record(void) { #if defined(CONFIG_CONSOLE_RECORD) return console_record_init(); #else return 0; #endif }
3.8 bootstage_relocate(common/bootstage.c)
int bootstage_relocate(void) { int i; /* * Duplicate all strings. They may point to an old location in the * program .text section that can eventually get trashed. */ for (i = 0; i < BOOTSTAGE_ID_COUNT; i++) if (record[i].name) record[i].name = strdup(record[i].name); return 0; }
3.9 initr_bootstage(common/board_r.c)
static int initr_bootstage(void) { /* We cannot do this before initr_dm() */ bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); return 0; }
标记BOOTSTAGE_ID_START_UBOOT_R阶段信息。
3.10 board_init(board/samsung/smdk2410/smdk2410.c)
int board_init(void) { /* arch number of SMDK2410-Board */ gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* adress of boot parameters */ gd->bd->bi_boot_params = 0x30000100; icache_enable(); dcache_enable(); return 0; }
gd->bd->bi_arch_number修改成SMDK2410的机器码,启动linux需要的,但实际测试,这个就算不设置也能够启动,但我们还是将其添加上(可在arch/arm/include/asm/mach-types.h中找到。
#define MACH_TYPE_SMDK2410 193
然后设置gd->bd->bi_boot_params,从0x30000100地址开始存放启动参数,内核启动时会从这里取出参数。
3.11 stdio_init_tables(common/sdtio.c)
int stdio_init_tables(void) { #if defined(CONFIG_NEEDS_MANUAL_RELOC) /* already relocated for current ARM implementation */ ulong relocation_offset = gd->reloc_off; int i; /* relocate device name pointers */ for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) { stdio_names[i] = (char *) (((ulong) stdio_names[i]) + relocation_offset); } #endif /* CONFIG_NEEDS_MANUAL_RELOC */ /* Initialize the list */ INIT_LIST_HEAD(&(devs.list)); return 0; }
初始化了一个双向循环链表:
static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; }
3.12 initr_serial(common/board_r.c)注册串口驱动
static int initr_serial(void) { serial_initialize(); return 0; }
serial_initialize定义在drivers/serial/serial.c文件:
/** * serial_initialize() - Register all compiled-in serial port drivers * * This function registers all serial port drivers that are compiled * into the U-Boot binary with the serial core, thus making them * available to U-Boot to use. Lastly, this function assigns a default * serial port to the serial core. That serial port is then used as a * default output. */ void serial_initialize(void) { amirix_serial_initialize(); arc_serial_initialize(); arm_dcc_initialize(); asc_serial_initialize(); atmel_serial_initialize(); au1x00_serial_initialize(); bfin_jtag_initialize(); bfin_serial_initialize(); bmw_serial_initialize(); clps7111_serial_initialize(); cogent_serial_initialize(); cpci750_serial_initialize(); evb64260_serial_initialize(); imx_serial_initialize(); iop480_serial_initialize(); jz_serial_initialize(); leon2_serial_initialize(); leon3_serial_initialize(); lh7a40x_serial_initialize(); lpc32xx_serial_initialize(); marvell_serial_initialize(); max3100_serial_initialize(); mcf_serial_initialize(); ml2_serial_initialize(); mpc512x_serial_initialize(); mpc5xx_serial_initialize(); mpc8260_scc_serial_initialize(); mpc8260_smc_serial_initialize(); mpc85xx_serial_initialize(); mpc8xx_serial_initialize(); mxc_serial_initialize(); mxs_auart_initialize(); ns16550_serial_initialize(); oc_serial_initialize(); p3mx_serial_initialize(); pl01x_serial_initialize(); pxa_serial_initialize(); s3c24xx_serial_initialize(); s5p_serial_initialize(); sa1100_serial_initialize(); sandbox_serial_initialize(); sconsole_serial_initialize(); sh_serial_initialize(); stm32_serial_initialize(); uartlite_serial_initialize(); zynq_serial_initialize(); serial_assign(default_serial_console()->name); }
串口设备注册:
void s3c24xx_serial_initialize(void) { serial_register(&s3c24xx_serial0_device); serial_register(&s3c24xx_serial1_device); serial_register(&s3c24xx_serial2_device); }
/** * serial_register() - Register serial driver with serial driver core * @dev: Pointer to the serial driver structure * * This function registers the serial driver supplied via @dev with * serial driver core, thus making U-Boot aware of it and making it * available for U-Boot to use. On platforms that still require manual * relocation of constant variables, relocation of the supplied structure * is performed. */ void serial_register(struct serial_device *dev) { #ifdef CONFIG_NEEDS_MANUAL_RELOC if (dev->start) dev->start += gd->reloc_off; if (dev->stop) dev->stop += gd->reloc_off; if (dev->setbrg) dev->setbrg += gd->reloc_off; if (dev->getc) dev->getc += gd->reloc_off; if (dev->tstc) dev->tstc += gd->reloc_off; if (dev->putc) dev->putc += gd->reloc_off; if (dev->puts) dev->puts += gd->reloc_off; #endif dev->next = serial_devices; serial_devices = dev; }
static struct serial_device *serial_devices;
struct serial_device { /* enough bytes to match alignment of following func pointer */ char name[16]; int (*start)(void); int (*stop)(void); void (*setbrg)(void); int (*getc)(void); int (*tstc)(void); void (*putc)(const char c); void (*puts)(const char *s); #if CONFIG_POST & CONFIG_SYS_POST_UART void (*loop)(int); #endif struct serial_device *next; };
这段代码执行完之后串口设备指针serial_devices指向如下图,可以看出这个一个单向链表结构:
3.13 initr_announce(common/board_r.c)
static int initr_announce(void) { debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr); return 0; }
3.14 power_init_board(common/board_r.c)
__weak int power_init_board(void) { return 0; }
3.15 initr_flash(common/board_r.c)初始化NOR FLASH
static int initr_flash(void) { ulong flash_size = 0; bd_t *bd = gd->bd; puts("Flash: "); if (board_flash_wp_on()) printf("Uninitialized - Write Protect On\n"); else flash_size = flash_init(); print_size(flash_size, ""); #ifdef CONFIG_SYS_FLASH_CHECKSUM /* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ if (getenv_yesno("flashchecksum") == 1) { printf(" CRC: %08X", crc32(0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)); } #endif /* CONFIG_SYS_FLASH_CHECKSUM */ putc('\n'); /* update start of FLASH memory */ #ifdef CONFIG_SYS_FLASH_BASE bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; #endif /* size of FLASH memory (final value) */ bd->bi_flashsize = flash_size; #if defined(CONFIG_SYS_UPDATE_FLASH_SIZE) /* Make a update of the Memctrl. */ update_flash_size(flash_size); #endif #if defined(CONFIG_OXC) || defined(CONFIG_RMU) /* flash mapped at end of memory map */ bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size; #elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */ #endif return 0; }
flash_init用于初始化NOR FLASH。该函数在drivers\mtd\cfi_flash.c中定义:
unsigned long flash_init (void) { unsigned long size = 0; int i; #ifdef CONFIG_SYS_FLASH_PROTECTION /* read environment from EEPROM */ char s[64]; getenv_f("unlock", s, sizeof(s)); #endif #ifdef CONFIG_CFI_FLASH /* for driver model */ cfi_flash_init_dm(); #endif /* Init: no FLASHes known */ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; /* Optionally write flash configuration register */ cfi_flash_set_config_reg(cfi_flash_bank_addr(i), cfi_flash_config_reg(i)); if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) flash_get_size(cfi_flash_bank_addr(i), i); size += flash_info[i].size; if (flash_info[i].flash_id == FLASH_UNKNOWN) { #ifndef CONFIG_SYS_FLASH_QUIET_TEST printf ("## Unknown flash on Bank %d " "- Size = 0x%08lx = %ld MB\n", i+1, flash_info[i].size, flash_info[i].size >> 20); #endif /* CONFIG_SYS_FLASH_QUIET_TEST */ } #ifdef CONFIG_SYS_FLASH_PROTECTION else if (strcmp(s, "yes") == 0) { /* * Only the U-Boot image and it's environment * is protected, all other sectors are * unprotected (unlocked) if flash hardware * protection is used (CONFIG_SYS_FLASH_PROTECTION) * and the environment variable "unlock" is * set to "yes". */ if (flash_info[i].legacy_unlock) { int k; /* * Disable legacy_unlock temporarily, * since flash_real_protect would * relock all other sectors again * otherwise. */ flash_info[i].legacy_unlock = 0; /* * Legacy unlocking (e.g. Intel J3) -> * unlock only one sector. This will * unlock all sectors. */ flash_real_protect (&flash_info[i], 0, 0); flash_info[i].legacy_unlock = 1; /* * Manually mark other sectors as * unlocked (unprotected) */ for (k = 1; k < flash_info[i].sector_count; k++) flash_info[i].protect[k] = 0; } else { /* * No legancy unlocking -> unlock all sectors */ flash_protect (FLAG_PROTECT_CLEAR, flash_info[i].start[0], flash_info[i].start[0] + flash_info[i].size - 1, &flash_info[i]); } } #endif /* CONFIG_SYS_FLASH_PROTECTION */ } flash_protect_default(); #ifdef CONFIG_FLASH_CFI_MTD cfi_mtd_init(); #endif return (size); }
3.16 initr_nand(common/board_r.c)初始化NAND FLASH
static int initr_nand(void) { puts("NAND: "); nand_init(); return 0; }
初始化nand flash,并输出nand falsh信息。其中nand_init()定义在drivers/mtd/nand/nand.c文件中:
void nand_init(void) { #ifdef CONFIG_SYS_NAND_SELF_INIT board_nand_init(); #else int i; for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) nand_init_chip(i); #endif printf("%lu MiB\n", total_nand_size / 1024); #ifdef CONFIG_SYS_NAND_SELECT_DEVICE /* * Select the chip in the board/cpu specific driver */ board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); #endif }
nand_init_chip定义:
#ifndef CONFIG_SYS_NAND_SELF_INIT static void nand_init_chip(int i) { struct mtd_info *mtd = &nand_info[i]; struct nand_chip *nand = &nand_chip[i]; ulong base_addr = base_address[i]; int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; if (maxchips < 1) maxchips = 1; mtd->priv = nand; nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; if (board_nand_init(nand)) return; if (nand_scan(mtd, maxchips)) return; nand_register(i); } #endif
board_nand_init在drivers/mtd/nand/s3c2410_nand.c文件中定义:
int board_nand_init(struct nand_chip *nand) { u_int32_t cfg; u_int8_t tacls, twrph0, twrph1; struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); struct s3c24x0_nand *nand_reg = s3c24x0_get_base_nand(); debug("board_nand_init()\n"); writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon); /* initialize hardware */ #if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING) tacls = CONFIG_S3C24XX_TACLS; twrph0 = CONFIG_S3C24XX_TWRPH0; twrph1 = CONFIG_S3C24XX_TWRPH1; #else tacls = 4; twrph0 = 8; twrph1 = 8; #endif cfg = S3C2410_NFCONF_EN; cfg |= S3C2410_NFCONF_TACLS(tacls - 1); cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); writel(cfg, &nand_reg->nfconf); /* initialize nand_chip data structure */ nand->IO_ADDR_R = (void *)&nand_reg->nfdata; nand->IO_ADDR_W = (void *)&nand_reg->nfdata; nand->select_chip = NULL; /* read_buf and write_buf are default */ /* read_byte and write_byte are default */ #ifdef CONFIG_NAND_SPL nand->read_buf = nand_read_buf; #endif /* hwcontrol always must be implemented */ nand->cmd_ctrl = s3c24x0_hwcontrol; nand->dev_ready = s3c24x0_dev_ready; #ifdef CONFIG_S3C2410_NAND_HWECC nand->ecc.hwctl = s3c24x0_nand_enable_hwecc; nand->ecc.calculate = s3c24x0_nand_calculate_ecc; nand->ecc.correct = s3c24x0_nand_correct_data; nand->ecc.mode = NAND_ECC_HW; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; nand->ecc.strength = 1; #else nand->ecc.mode = NAND_ECC_SOFT; #endif #ifdef CONFIG_S3C2410_NAND_BBT nand->bbt_options |= NAND_BBT_USE_FLASH; #endif debug("end of nand_init\n"); return 0; }
3.17 initr_env(common/board_r.c)
static int initr_env(void) { /* initialize environment */ if (should_load_env()) env_relocate(); else set_default_env(NULL); #ifdef CONFIG_OF_CONTROL setenv_addr("fdtcontroladdr", gd->fdt_blob); #endif /* Initialize from environment */ load_addr = getenv_ulong("loadaddr", 16, load_addr); #if defined(CONFIG_SYS_EXTBDINFO) #if defined(CONFIG_405GP) || defined(CONFIG_405EP) #if defined(CONFIG_I2CFAST) /* * set bi_iic_fast for linux taking environment variable * "i2cfast" into account */ { char *s = getenv("i2cfast"); if (s && ((*s == 'y') || (*s == 'Y'))) { gd->bd->bi_iic_fast[0] = 1; gd->bd->bi_iic_fast[1] = 1; } } #endif /* CONFIG_I2CFAST */ #endif /* CONFIG_405GP, CONFIG_405EP */ #endif /* CONFIG_SYS_EXTBDINFO */ return 0; }
其中should_load_env定义如下:
static int should_load_env(void) { #ifdef CONFIG_OF_CONTROL return fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1); #elif defined CONFIG_DELAY_ENVIRONMENT return 0; #else return 1; #endif }
然后执行env_relocate:
void env_relocate(void) { #if defined(CONFIG_NEEDS_MANUAL_RELOC) env_reloc(); env_htab.change_ok += gd->reloc_off; #endif if (gd->env_valid == 0) { #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD) /* Environment not changable */ set_default_env(NULL); #else bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM); set_default_env("!bad CRC"); #endif } else { env_relocate_spec(); } }
这里介绍一下set_default_env:
void set_default_env(const char *s) { int flags = 0; /* 我们的环境变量没配置过,正常是不会超出范围 */ if (sizeof(default_environment) > ENV_SIZE) { puts("*** Error - default environment is too large\n\n"); return; } /* 上面传进来的是"!bad CRC" */ if (s) { if (*s == '!') { /* 第一个字符是'!' */ printf("*** Warning - %s, " "using default environment\n\n", s + 1); /* 跳过!,打印我们看到的一样 */ } else { flags = H_INTERACTIVE; puts(s); } } else { puts("Using default environment\n\n"); } if (himport_r(&env_htab, (char *)default_environment, sizeof(default_environment), '\0', flags, 0, 0, NULL) == 0) error("Environment import failed: errno = %d\n", errno); /* 标记使用默认环境变量和环境变量成功 */ gd->flags |= GD_FLG_ENV_READY; }
himport_r函数设置环境变量default_environment到Hash Table表中:
/* * Import linearized data into hash table. * * This is the inverse function to hexport(): it takes a linear list * of "name=value" pairs and creates hash table entries from it. * * Entries without "value", i. e. consisting of only "name" or * "name=", will cause this entry to be deleted from the hash table. * * The "flag" argument can be used to control the behaviour: when the * H_NOCLEAR bit is set, then an existing hash table will kept, i. e. * new data will be added to an existing hash table; otherwise, old * data will be discarded and a new hash table will be created. * * The separator character for the "name=value" pairs can be selected, * so we both support importing from externally stored environment * data (separated by NUL characters) and from plain text files * (entries separated by newline characters). * * To allow for nicely formatted text input, leading white space * (sequences of SPACE and TAB chars) is ignored, and entries starting * (after removal of any leading white space) with a '#' character are * considered comments and ignored. * * [NOTE: this means that a variable name cannot start with a '#' * character.] * * When using a non-NUL separator character, backslash is used as * escape character in the value part, allowing for example for * multi-line values. * * In theory, arbitrary separator characters can be used, but only * '\0' and '\n' have really been tested. */ int himport_r(struct hsearch_data *htab, const char *env, size_t size, const char sep, int flag, int crlf_is_lf, int nvars, char * const vars[]) { char *data, *sp, *dp, *name, *value; char *localvars[nvars]; int i; /* Test for correct arguments. */ if (htab == NULL) { __set_errno(EINVAL); return 0; } /* we allocate new space to make sure we can write to the array */ if ((data = malloc(size + 1)) == NULL) { debug("himport_r: can't malloc %zu bytes\n", size + 1); __set_errno(ENOMEM); return 0; } memcpy(data, env, size); data[size] = '\0'; dp = data; /* make a local copy of the list of variables */ if (nvars) memcpy(localvars, vars, sizeof(vars[0]) * nvars); if ((flag & H_NOCLEAR) == 0) { /* Destroy old hash table if one exists */ debug("Destroy Hash Table: %p table = %p\n", htab, htab->table); if (htab->table) hdestroy_r(htab); } /* * Create new hash table (if needed). The computation of the hash * table size is based on heuristics: in a sample of some 70+ * existing systems we found an average size of 39+ bytes per entry * in the environment (for the whole key=value pair). Assuming a * size of 8 per entry (= safety factor of ~5) should provide enough * safety margin for any existing environment definitions and still * allow for more than enough dynamic additions. Note that the * "size" argument is supposed to give the maximum environment size * (CONFIG_ENV_SIZE). This heuristics will result in * unreasonably large numbers (and thus memory footprint) for * big flash environments (>8,000 entries for 64 KB * envrionment size), so we clip it to a reasonable value. * On the other hand we need to add some more entries for free * space when importing very small buffers. Both boundaries can * be overwritten in the board config file if needed. */ if (!htab->table) { int nent = CONFIG_ENV_MIN_ENTRIES + size / 8; if (nent > CONFIG_ENV_MAX_ENTRIES) nent = CONFIG_ENV_MAX_ENTRIES; debug("Create Hash Table: N=%d\n", nent); if (hcreate_r(nent, htab) == 0) { free(data); return 0; } } if (!size) { free(data); return 1; /* everything OK */ } if(crlf_is_lf) { /* Remove Carriage Returns in front of Line Feeds */ unsigned ignored_crs = 0; for(;dp < data + size && *dp; ++dp) { if(*dp == '\r' && dp < data + size - 1 && *(dp+1) == '\n') ++ignored_crs; else *(dp-ignored_crs) = *dp; } size -= ignored_crs; dp = data; } /* Parse environment; allow for '\0' and 'sep' as separators */ do { ENTRY e, *rv; /* skip leading white space */ while (isblank(*dp)) ++dp; /* skip comment lines */ if (*dp == '#') { while (*dp && (*dp != sep)) ++dp; ++dp; continue; } /* parse name */ for (name = dp; *dp != '=' && *dp && *dp != sep; ++dp) ; /* deal with "name" and "name=" entries (delete var) */ if (*dp == '\0' || *(dp + 1) == '\0' || *dp == sep || *(dp + 1) == sep) { if (*dp == '=') *dp++ = '\0'; *dp++ = '\0'; /* terminate name */ debug("DELETE CANDIDATE: \"%s\"\n", name); if (!drop_var_from_set(name, nvars, localvars)) continue; if (hdelete_r(name, htab, flag) == 0) debug("DELETE ERROR ##############################\n"); continue; } *dp++ = '\0'; /* terminate name */ /* parse value; deal with escapes */ for (value = sp = dp; *dp && (*dp != sep); ++dp) { if ((*dp == '\\') && *(dp + 1)) ++dp; *sp++ = *dp; } *sp++ = '\0'; /* terminate value */ ++dp; if (*name == 0) { debug("INSERT: unable to use an empty key\n"); __set_errno(EINVAL); free(data); return 0; } /* Skip variables which are not supposed to be processed */ if (!drop_var_from_set(name, nvars, localvars)) continue; /* enter into hash table */ e.key = name; e.data = value; hsearch_r(e, ENTER, &rv, htab, flag); if (rv == NULL) printf("himport_r: can't insert \"%s=%s\" into hash table\n", name, value); debug("INSERT: table %p, filled %d/%d rv %p ==> name=\"%s\" value=\"%s\"\n", htab, htab->filled, htab->size, rv, name, value); } while ((dp < data + size) && *dp); /* size check needed for text */ /* without '\0' termination */ debug("INSERT: free(data = %p)\n", data); free(data); /* process variables which were not considered */ for (i = 0; i < nvars; i++) { if (localvars[i] == NULL) continue; /* * All variables which were not deleted from the variable list * were not present in the imported env * This could mean two things: * a) if the variable was present in current env, we delete it * b) if the variable was not present in current env, we notify * it might be a typo */ if (hdelete_r(localvars[i], htab, flag) == 0) printf("WARNING: '%s' neither in running nor in imported env!\n", localvars[i]); else printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]); } debug("INSERT: done\n"); return 1; /* everything OK */ }
调试输出信息如下:
Destroy Hash Table: 33f9a890 table = 00000000 Create Hash Table: N=75 INSERT: table 33f9a890, filled 1/79 rv 33b11238 ==> name="bootdelay" value="5" INSERT: table 33f9a890, filled 2/79 rv 33b110f8 ==> name="baudrate" value="115200" INSERT: table 33f9a890, filled 3/79 rv 33b110a8 ==> name="ipaddr" value="10.0.0.110" INSERT: table 33f9a890, filled 4/79 rv 33b11260 ==> name="serverip" value="10.0.0.1" INSERT: table 33f9a890, filled 5/79 rv 33b114f4 ==> name="netmask" value="255.255.255.0" INSERT: free(data = 33b11008) INSERT: done
3.18 initr_secondary_cpu(common/board_r.c)
static int initr_secondary_cpu(void) { /* * after non-volatile devices & environment is setup and cpu code have * another round to deal with any initialization that might require * full access to the environment or loading of some image (firmware) * from a non-volatile device */ /* TODO: maybe define this for all archs? */ cpu_secondary_init_r(); return 0; }
3.19 stdio_add_devices(common/sdtio.c)
int stdio_add_devices(void) { #ifdef CONFIG_DM_KEYBOARD // 定义键盘 struct udevice *dev; struct uclass *uc; int ret; /* * For now we probe all the devices here. At some point this should be * done only when the devices are required - e.g. we have a list of * input devices to start up in the stdin environment variable. That * work probably makes more sense when stdio itself is converted to * driver model. * * TODO(sjg@chromium.org): Convert changing uclass_first_device() etc. * to return the device even on error. Then we could use that here. */ ret = uclass_get(UCLASS_KEYBOARD, &uc); if (ret) return ret; /* Don't report errors to the caller - assume that they are non-fatal */ uclass_foreach_dev(dev, uc) { ret = device_probe(dev); if (ret) printf("Failed to probe keyboard '%s'\n", dev->name); } #endif #ifdef CONFIG_SYS_I2C i2c_init_all(); #else #if defined(CONFIG_HARD_I2C) i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); #endif #endif #ifdef CONFIG_DM_VIDEO // 定义图像 struct udevice *vdev; # ifndef CONFIG_DM_KEYBOARD int ret; # endif for (ret = uclass_first_device(UCLASS_VIDEO, &vdev); vdev; ret = uclass_next_device(&vdev)) ; if (ret) printf("%s: Video device failed (ret=%d)\n", __func__, ret); #else # if defined(CONFIG_LCD) drv_lcd_init (); # endif # if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) drv_video_init (); # endif #endif /* CONFIG_DM_VIDEO */ #if defined(CONFIG_KEYBOARD) && !defined(CONFIG_DM_KEYBOARD) drv_keyboard_init (); #endif #ifdef CONFIG_LOGBUFFER drv_logbuff_init (); #endif drv_system_init (); serial_stdio_init (); #ifdef CONFIG_USB_TTY drv_usbtty_init (); #endif #ifdef CONFIG_NETCONSOLE drv_nc_init (); #endif #ifdef CONFIG_JTAG_CONSOLE drv_jtag_console_init (); #endif #ifdef CONFIG_CBMEM_CONSOLE cbmemc_init(); #endif return 0; }
各种驱动初始化,但是我们都没有定义。只有两个默认函数。
第一个,驱动系统初始化,同时注册好标准IO:
static void drv_system_init (void) { struct stdio_dev dev; memset (&dev, 0, sizeof (dev)); strcpy (dev.name, "serial"); /* 和驱动一样,设备名字很重要 */ dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; dev.putc = stdio_serial_putc; dev.puts = stdio_serial_puts; dev.getc = stdio_serial_getc; dev.tstc = stdio_serial_tstc; stdio_register (&dev); /* 注册输入,输出 */ #ifdef CONFIG_SYS_DEVICE_NULLDEV /* 没定义 */ memset (&dev, 0, sizeof (dev)); strcpy (dev.name, "nulldev"); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; dev.putc = nulldev_putc; dev.puts = nulldev_puts; dev.getc = nulldev_input; dev.tstc = nulldev_input; stdio_register (&dev); #endif }
int stdio_register(struct stdio_dev *dev) { return stdio_register_dev(dev, NULL); } int stdio_register_dev(struct stdio_dev *dev, struct stdio_dev **devp) { struct stdio_dev *_dev; _dev = stdio_clone(dev); /* 前面的是局部变量,这里克隆一份,弄成动态申请的 */ if(!_dev) return -ENODEV; list_add_tail(&(_dev->list), &(devs.list)); /* 加入设备链表中 */ if (devp) *devp = _dev; return 0; } /* 克隆就是动态拷贝一份 */ struct stdio_dev* stdio_clone(struct stdio_dev *dev) { struct stdio_dev *_dev; if(!dev) return NULL; _dev = calloc(1, sizeof(struct stdio_dev)); if(!_dev) return NULL; memcpy(_dev, dev, sizeof(struct stdio_dev)); return _dev; }
第二个,注册系统中的所有串口到驱动链表:
void serial_stdio_init(void) { struct stdio_dev dev; struct serial_device *s = serial_devices; while (s) { memset(&dev, 0, sizeof(dev)); strcpy(dev.name, s->name); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT; dev.start = serial_stub_start; dev.stop = serial_stub_stop; dev.putc = serial_stub_putc; dev.puts = serial_stub_puts; dev.getc = serial_stub_getc; dev.tstc = serial_stub_tstc; dev.priv = s; stdio_register(&dev); s = s->next; } }
3.20 initr_jumptable(common/board_r.c)
static int initr_jumptable(void) { jumptable_init(); return 0; }
初始化跳转表common/exports.c:
void jumptable_init(void) { gd->jt = malloc(sizeof(struct jt_funcs)); /* 申请跳转表的内存 */ #include <_exports.h> //注意这个头文件在函数里面,所以它里面的内容就是函数的内容 }
3.21 console_init_r(common/sonsole.c)
int console_init_r(void) { struct stdio_dev *inputdev = NULL, *outputdev = NULL; int i; struct list_head *list = stdio_get_list(); // 得到设备链表 struct list_head *pos; struct stdio_dev *dev; #ifdef CONFIG_SPLASH_SCREEN /* * suppress all output if splash screen is enabled and we have * a bmp to display. We redirect the output from frame buffer * console to serial console in this case or suppress it if * "silent" mode was requested. */ if (getenv("splashimage") != NULL) { if (!(gd->flags & GD_FLG_SILENT)) outputdev = search_device (DEV_FLAGS_OUTPUT, "serial"); } #endif /* Scan devices looking for input and output devices */ list_for_each(pos, list) { dev = list_entry(pos, struct stdio_dev, list); if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) { inputdev = dev; } if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) { outputdev = dev; } if(inputdev && outputdev) break; } /* Initializes output console first */ if (outputdev != NULL) { console_setfile(stdout, outputdev); console_setfile(stderr, outputdev); #ifdef CONFIG_CONSOLE_MUX console_devices[stdout][0] = outputdev; console_devices[stderr][0] = outputdev; #endif } /* Initializes input console */ if (inputdev != NULL) { console_setfile(stdin, inputdev); #ifdef CONFIG_CONSOLE_MUX console_devices[stdin][0] = inputdev; #endif } #ifndef CONFIG_SYS_CONSOLE_INFO_QUIET stdio_print_current_devices(); #endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ /* Setting environment variables */ for (i = 0; i < 3; i++) { setenv(stdio_names[i], stdio_devices[i]->name); } gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ #if 0 /* If nothing usable installed, use only the initial console */ if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL)) return 0; #endif print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL); return 0; }
将stdin,stdout,stderr与具体的终端设备绑定起来。
3.22 interrupt_init(arch/arm/lib/interrupts.c)
int interrupt_init(void) { return 0; }
3.23 initr_enable_interrupts(common/board_r.c)
static int initr_enable_interrupts(void) { enable_interrupts(); return 0; }
enable_interrupts实际是个空函数。
3.24 initr_ethaddr(common/board_r.c)
static int initr_ethaddr(void) { bd_t *bd = gd->bd; /* kept around for legacy kernels only ... ignore the next section */ eth_getenv_enetaddr("ethaddr", bd->bi_enetaddr); #ifdef CONFIG_HAS_ETH1 eth_getenv_enetaddr("eth1addr", bd->bi_enet1addr); #endif #ifdef CONFIG_HAS_ETH2 eth_getenv_enetaddr("eth2addr", bd->bi_enet2addr); #endif #ifdef CONFIG_HAS_ETH3 eth_getenv_enetaddr("eth3addr", bd->bi_enet3addr); #endif #ifdef CONFIG_HAS_ETH4 eth_getenv_enetaddr("eth4addr", bd->bi_enet4addr); #endif #ifdef CONFIG_HAS_ETH5 eth_getenv_enetaddr("eth5addr", bd->bi_enet5addr); #endif return 0; }
3.25 initr_net(common/board_r.c)
static int initr_net(void) { puts("Net: "); eth_initialize(); #if defined(CONFIG_RESET_PHY_R) debug("Reset Ethernet PHY\n"); reset_phy(); #endif return 0; }
3.26 run_main_loop
到此,board_init_r也就分析完了,最后就是进入u-boot的大循环run_main_loop了,这个里面就是启动内核或者处理用户输入的命令,这部分后面再详细分析一下。
四、总结
board_init_r函数主要做的工作就是准备中断,初始化需要用到的硬件资源,总结下来,大致如下:
- 注册串口驱动;
- 初始化NOR FALSH;
- 初始化NAND FLASH;
- 注册各种驱动(如果有相关宏定义),默认注册标准IO,注册系统中的所有串口到驱动链表;
- 初始化跳表
- 初始化网卡;
- 执行run_main_loop
最终更改了gd部分成员变量:
flags |
指示标志,如设备已经初始化啦 |
GD_FLG_SERIAL_READY|GD_FLG_RELOC|GD_FLG_FULL_MALLOC_INIT|GD_FLG_ENV_READY 0x00100|0x00001|0x00200|0x00080 |
jt |
jump table |
动态申请分配得到 |
参考文章: