u-boot-2021.01(imx6ull)启动流程分析之三:board_init_f函数分析

3.4.2 board_init_f

顾名思义,函数主要工作是早期的一些硬件初始化和设置全局变量gd结构体的成员:

/* file: common/board_f.c */
void board_init_f(ulong boot_flags)
{
	gd->flags = boot_flags;
	gd->have_console = 0;

	if (initcall_run_list(init_sequence_f))
		hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
		!defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64) && \
		!defined(CONFIG_ARC)
	/* NOTREACHED - jump_to_copy() does not return */
	hang();
#endif
}

函数内容不多,但是init_sequence_f这个函数指针数组内容可不简单,由于数组元素较多,所以将相关宏定义简化之后就是:

/* file: common/board_f.c */
static const init_fnc_t init_sequence_f[] = {
	setup_mon_len,
    fdtdec_setup,
    trace_early_init,
    initf_malloc,
	log_init,
	initf_bootstage,	/* uses its own timer, so does not need DM */
    setup_spl_handoff,
	initf_console_record,
    arch_cpu_init,		/* basic arch cpu dependent setup */
	mach_cpu_init,		/* SoC/machine dependent CPU setup */
	initf_dm,
	arch_cpu_init_dm,
    board_early_init_f,
    get_clocks,		/* get CPU and bus clocks (etc.) */
	timer_init,		/* initialize timer */
    board_postclk_init,
	env_init,		/* initialize environment */
	init_baud_rate,		/* initialze baudrate settings */
	serial_init,		/* serial communications setup */
	console_init_f,		/* stage 1 init of console */
	display_options,	/* say that we are here */
	display_text_info,	/* show debugging info if required */
	checkcpu,
    print_cpuinfo,		/* display cpu info (and speed) */
    show_board_info,
    INIT_FUNC_WATCHDOG_INIT
    misc_init_f,
    INIT_FUNC_WATCHDOG_RESET
    init_func_i2c,
    init_func_vid,
    announce_dram_init,
	dram_init,		/* configure available RAM banks */
    post_init_f,
    init_post,
    ...
	reserve_uboot,
	reserve_malloc,
	reserve_board,
	setup_machine,
	reserve_global_data,
	reserve_fdt,
    ...
	dram_init_banksize,
	show_dram_config,
	setup_bdinfo,
	display_new_sp,
	...
	reloc_fdt,
	reloc_bootstage,
	reloc_bloblist,
	setup_reloc,
	...
	NULL,
};

这个数组最终作为函数initcall_run_list的参数,遍历执行这个函数指针数组的每一个元素(函数),所以重点可以放在函数指针数组里的各个元素。篇章问题,这里就不一一探究,挑出个别容易在移植过程中出现的函数即可。

3.4.2.1 setup_mon_len:设置gd的mon_len成员(代码长度)

去掉宏定义之后的函数定义:

/* file: common/board_f.c */
static int setup_mon_len(void)
{
#if defined(__ARM__) || defined(__MICROBLAZE__)
	gd->mon_len = (ulong)&__bss_end - (ulong)_start;
#elif ...
}

其中,bss段就是存放程序中未初始化或初始化为0的全局变量和静态变量的一块可读可写内存,一般可执行程序就包含代码段、数据段、BSS段。根据u-boot.lds文件可以知道gd->mon_len的赋值就是u-boot程序的长度。


3.4.2.2 fdtdec_setup:如果u-boot中使用设备树,则需处理一些相关工作

第一篇文章查看.config配置文件知道关于设备树就有下面几个定义:

CONFIG_OF_CONTROL=y
CONFIG_OF_SEPARATE=y
CONFIG_OF_TRANSLATE=y
CONFIG_OF_LIBFDT=y

所以去掉宏定义之后的函数定义就是:

/* file: lib/fdtdec.c */
int fdtdec_setup(void)
{
	int ret;
	
	/* Allow the board to override the fdt address. */
	gd->fdt_blob = board_fdt_blob_setup();

	/* Allow the early environment to override the fdt address */
	gd->fdt_blob = map_sysmem
		(env_get_ulong("fdtcontroladdr", 16,
			       (unsigned long)map_to_sysmem(gd->fdt_blob)), 0);
	ret = fdtdec_prepare_fdt();
	if (!ret)
		ret = fdtdec_board_setup(gd->fdt_blob);
	return ret;
}

函数先是调用board_fdt_blob_setup来设置gd结构体的fdt_blob成员,简化宏定义之后函数如下:

/* file: lib/fdtdec.c */
__weak void *board_fdt_blob_setup(void)
{
	void *fdt_blob = NULL;
	/* FDT is at end of image */
	fdt_blob = (ulong *)&_end;
	return fdt_blob;
}

由前面的镜像组成可以知道u-boot设备树文件是放在u-boot.bin的末尾,所以取它的地址返回即可。 后面继续调用env_get_ulong从环境变量中获取u-boot设备树的地址,如果该环境变量没有被设置则返回原本的默认值(unsigned long)map_to_sysmem(gd->fdt_blob)。最终继续调用fdtdec_prepare_fdt函数来打印一些信息:

/* file: lib/fdtdec.c */
int fdtdec_prepare_fdt(void)
{
	if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
	    fdt_check_header(gd->fdt_blob)) {
#ifdef CONFIG_SPL_BUILD
		puts("Missing DTB\n");
#else
		puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
# ifdef DEBUG
		if (gd->fdt_blob) {
			printf("fdt_blob=%p\n", gd->fdt_blob);
			print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4,
				     32, 0);
		}
# endif
#endif
		return -1;
	}
	return 0;
}

3.4.2.3 arch_cpu_init:cpu初始化
int arch_cpu_init(void)
{
	struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;

	init_aips();

	/* Need to clear MMDC_CHx_MASK to make warm reset work. */
	clear_mmdc_ch_mask();

	/*
	 * Disable self-bias circuit in the analog bandap.
	 * The self-bias circuit is used by the bandgap during startup.
	 * This bit should be set after the bandgap has initialized.
	 */
	init_bandgap();
	...

	if (is_mx6ull()) {
		/*
		 * GPBIT[1:0] is suggested to set to 2'b11:
		 * 2'b00 : always PUP100K
		 * 2'b01 : PUP100K when PMIC_ON_REQ or SOC_NOT_FAIL
		 * 2'b10 : always disable PUP100K
		 * 2'b11 : PDN100K when SOC_FAIL, PUP100K when SOC_NOT_FAIL
		 * register offset is different from i.MX6UL, since
		 * i.MX6UL is fixed by ECO.
		 */
		writel(readl(MX6UL_SNVS_LP_BASE_ADDR) |
			0x3, MX6UL_SNVS_LP_BASE_ADDR);
	}

	/* Set perclk to source from OSC 24MHz */
	if (has_err007805())
		setbits_le32(&ccm->cscmr1, MXC_CCM_CSCMR1_PER_CLK_SEL_MASK);

	imx_wdog_disable_powerdown(); /* Disable PDE bit of WMCR register */
	...
    init_src();
	...
	return 0;
}

3.4.2.4 board_early_init_f:配置串口引脚
/* file: board/freescale/mx6ullevk/mx6ullevk.c */
int board_early_init_f(void)
{
	setup_iomux_uart();

	return 0;
}

/* setup_iomux_uart的实现:↓ */
#define UART_PAD_CTRL  (PAD_CTL_PKE | PAD_CTL_PUE |		\
	PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED |		\
	PAD_CTL_DSE_40ohm   | PAD_CTL_SRE_FAST  | PAD_CTL_HYS)

static iomux_v3_cfg_t const uart1_pads[] = {
	MX6_PAD_UART1_TX_DATA__UART1_DCE_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
	MX6_PAD_UART1_RX_DATA__UART1_DCE_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
};

static void setup_iomux_uart(void)
{
	imx_iomux_v3_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads));
}

发现函数目的是串口uart1引脚的复用配置,至于函数imx_iomux_v3_setup_multiple_pads就不继续往下追踪了,这里只需要知道函数的功能即可,函数一般不会有太大问题,如果程序运行不了再继续往下追,否则会篇幅太长。

3.4.2.5 env_init:初始化环境变量
/* file: env/env.c */
int env_init(void)
{
	struct env_driver *drv;
	int ret = -ENOENT;
	int prio;

	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
		if (!drv->init || !(ret = drv->init()))
			env_set_inited(drv->location);
		if (ret == -ENOENT)
			env_set_inited(drv->location);

		debug("%s: Environment %s init done (ret=%d)\n", __func__,
		      drv->name, ret);
	}

	if (!prio)
		return -ENODEV;

	if (ret == -ENOENT) {
		gd->env_addr = (ulong)&default_environment[0];
		gd->env_valid = ENV_VALID;
		...
}

说是初始化环境变量,然后如果追踪函数调用,就会发现对于imx6ull环境变量保存在mmc中但没提供init函数的话,就会执行后面的gd成员的赋值。而在env/mmc.c文件中又可以看到.load = env_mmc_load函数里才真正地去获取环境变量保存的位置和大小:

static int env_mmc_load(void)
{
	struct mmc *mmc;
	u32 offset;
	int ret;
	int dev = mmc_get_env_dev();
	const char *errmsg;
	env_t *ep = NULL;

	mmc = find_mmc_device(dev);

	errmsg = init_mmc_for_env(mmc);
	...

	if (mmc_get_env_addr(mmc, 0, &offset)) {
	...

	if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
	...
	ret = env_import(buf, 1, H_EXTERNAL);
	if (!ret) {
		ep = (env_t *)buf;
		gd->env_addr = (ulong)&ep->data;
		...
}

对于u-boot-2021.01版本来说,关于imx6ull的环境变量相关信息可以通过它的默认配置文件得到:

CONFIG_ENV_SIZE=0x2000
CONFIG_ENV_OFFSET=0xC0000
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_MMC=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_SYS_MMC_ENV_DEV=1
CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y

不同版本不同SoC将这些信息保存的文件不一样,有些就会在xxx_defconfig文件中定义,而有些放在对应的头文件中。


3.4.2.6 init_baud_rate:设置波特率
/* file: common/board_f.c */
static int init_baud_rate(void)
{
	gd->baudrate = env_get_ulong("baudrate", 10, CONFIG_BAUDRATE);
	return 0;
}

env_get_ulong函数定义可以知道它是从环境变量里获取十进制的baudrate值,如果没有则使用默认的CONFIG_BAUDRATE,它可以在xxx_defconfig配置文件中定义,如果没有定义则使用tools/env/fw_env_private.h文件中的定义。


3.4.2.7 serial_init:初始化串口

(如果不知道函数在哪个文件中定义,可以通过u-boot.map和各个子目录下的.o文件快速定位。)

/* file: drivers/serial/serial.c */
int serial_init(void)
{
	gd->flags |= GD_FLG_SERIAL_READY;
	return get_current()->start();
}

/* get_current函数的实现:↓ */
static struct serial_device *get_current(void)
{
	struct serial_device *dev;

	if (!(gd->flags & GD_FLG_RELOC))
		dev = default_serial_console();
	else if (!serial_current)
		dev = default_serial_console();
	else
		dev = serial_current;

	/* We must have a console device */
	if (!dev) {
#ifdef CONFIG_SPL_BUILD
		puts("Cannot find console\n");
		hang();
#else
		panic("Cannot find console\n");
#endif
	}

	return dev;
}

get_current函数的一进来就是判断是否已经代码重定位了,很明显,执行到这里还没有,所以条件为真,于是通过default_serial_console()函数来获取默认的串口设备,按照u-boot.map文件和各个子目录下的.o文件也不难发现它的定义:

/* file: drivers/serial/serial_mxc.c */
__weak struct serial_device *default_serial_console(void)
{
	return &mxc_serial_drv;
}

/* mxc_serial_drv的定义如下:↓ */
static struct serial_device mxc_serial_drv = {
	.name	= "mxc_serial",
	.start	= mxc_serial_init,
	.stop	= NULL,
	.setbrg	= mxc_serial_setbrg,
	.putc	= mxc_serial_putc,
	.puts	= default_serial_puts,
	.getc	= mxc_serial_getc,
	.tstc	= mxc_serial_tstc,
};

最终会调用到mxc_serial_init函数,往里面继续探究看看:

/* file: drivers/serial/serial_mxc.c */
static int mxc_serial_init(void)
{
	_mxc_serial_init(mxc_base, false);

	serial_setbrg();

	return 0;
}

继续往里探究,其中mxc_base的定义如下:

/* file: drivers/serial/serial_mxc.c */
#define mxc_base	((struct mxc_uart *)CONFIG_MXC_UART_BASE)

还没能看清楚使用的是哪个串口,那就继续往下追:

/* file: include/configs/mx6ullevk.h */
#define CONFIG_MXC_UART_BASE		UART1_BASE

看完参数看函数,看看_mxc_serial_init如何实现:

/* file: drivers/serial/serial_mxc.c */
static void _mxc_serial_init(struct mxc_uart *base, int use_dte)
{
	writel(0, &base->cr1);
	writel(0, &base->cr2);

	while (!(readl(&base->cr2) & UCR2_SRST));

	if (use_dte)
		writel(0x404 | UCR3_ADNIMP, &base->cr3);
	else
		writel(0x704 | UCR3_ADNIMP, &base->cr3);

	writel(0x704 | UCR3_ADNIMP, &base->cr3);
	writel(0x8000, &base->cr4);
	writel(0x2b, &base->esc);
	writel(0, &base->tim);

	writel(0, &base->ts);
}

最终就是直接操作寄存器了。到此为止,串口初始化函数serial_init()基本追踪完毕了。


3.4.2.8 display_options:打印u-boot版本、编译时间

接着查看一下u-boot启动时一系列打印辅助信息函数,我们可以通过这些打印信息确定程序是否能执行到这里或者串口等问题。

/* file: lib/display_options.c */
int display_options(void)
{
	char buf[DISPLAY_OPTIONS_BANNER_LENGTH];

	display_options_get_banner(true, buf, sizeof(buf));
	printf("%s", buf);

	return 0;
}

/* 追踪display_options_get_banner */
char *display_options_get_banner(bool newlines, char *buf, int size)
{
	return display_options_get_banner_priv(newlines, BUILD_TAG, buf, size);
}

/* 追踪display_options_get_banner_priv */
char *display_options_get_banner_priv(bool newlines, const char *build_tag,
				      char *buf, int size)
{
	int len;

	len = snprintf(buf, size, "%s%s", newlines ? "\n\n" : "",
		       version_string);
	...
	return buf;
}

/* 追踪version_string (file: cmd/version.c) */
const char __weak version_string[] = U_BOOT_VERSION_STRING;

/* 追踪U_BOOT_VERSION_STRING (file: include/version.h) */
#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \
	U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING

/* 追踪U_BOOT_DATE等 (file: include/generated/timestamp_autogenerated.h) */
#define U_BOOT_DATE "Feb 05 2021"
#define U_BOOT_TIME "17:14:08"
#define U_BOOT_TZ "+0800"
#define U_BOOT_DMI_DATE "02/05/2021"
#define U_BOOT_BUILD_DATE 0x20210205
#define U_BOOT_EPOCH 1612516448

最终就是串口打印出u-boot的版本以及编译时间等等,继续查看下一个打印函数。


3.4.2.9 display_text_info:打印u-boot的地址
/* file: common/board_f.c */
static int display_text_info(void)
{
#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_EFI_APP)
	ulong bss_start, bss_end, text_base;

	bss_start = (ulong)&__bss_start;
	bss_end = (ulong)&__bss_end;

#ifdef CONFIG_SYS_TEXT_BASE
	text_base = CONFIG_SYS_TEXT_BASE;
#else
	text_base = CONFIG_SYS_MONITOR_BASE;
#endif

	debug("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",
	      text_base, bss_start, bss_end);
#endif

	return 0;
}

这个函数就只是debug一下u-boot的代码在内存中的位置而已,相对上面的简单一点。


3.4.2.10 print_cpuinfo:打印CPU信息
/* file: arch/arm/mach-imx/cpu.c */
int print_cpuinfo(void)
{
	u32 cpurev;
	__maybe_unused u32 max_freq;

	cpurev = get_cpu_rev();

#if defined(CONFIG_IMX_THERMAL) || defined(CONFIG_IMX_TMU)
	struct udevice *thermal_dev;
	int cpu_tmp, minc, maxc, ret;

	printf("CPU:   Freescale i.MX%s rev%d.%d",
	       get_imx_type((cpurev & 0x1FF000) >> 12),
	       (cpurev & 0x000F0) >> 4,
	       (cpurev & 0x0000F) >> 0);
	max_freq = get_cpu_speed_grade_hz();
	if (!max_freq || max_freq == mxc_get_clock(MXC_ARM_CLK)) {
		printf(" at %dMHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000);
	} else {
		printf(" %d MHz (running at %d MHz)\n", max_freq / 1000000,
		       mxc_get_clock(MXC_ARM_CLK) / 1000000);
	}
#else
	...
#endif

#if defined(CONFIG_IMX_THERMAL) || defined(CONFIG_IMX_TMU)
	puts("CPU:   ");
	switch (get_cpu_temp_grade(&minc, &maxc)) {
	case TEMP_AUTOMOTIVE:
		puts("Automotive temperature grade ");
		break;
	case TEMP_INDUSTRIAL:
		puts("Industrial temperature grade ");
		break;
	case TEMP_EXTCOMMERCIAL:
		puts("Extended Commercial temperature grade ");
		break;
	default:
		puts("Commercial temperature grade ");
		break;
	}
	printf("(%dC to %dC)", minc, maxc);
	ret = uclass_get_device(UCLASS_THERMAL, 0, &thermal_dev);
	if (!ret) {
		ret = thermal_get_temp(thermal_dev, &cpu_tmp);

		if (!ret)
			printf(" at %dC", cpu_tmp);
		else
			debug(" - invalid sensor data\n");
	} else {
		debug(" - invalid sensor device\n");
	}
	puts("\n");
#endif

	printf("Reset cause: %s\n", get_reset_cause());
	return 0;
}

3.4.2.11 show_board_info:打印单板信息
/* file: common/board_info.c */
int __weak show_board_info(void)
{
#ifdef CONFIG_OF_CONTROL
	DECLARE_GLOBAL_DATA_PTR;
	const char *model;

	model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);

	if (model)
		printf("Model: %s\n", model);
#endif

	return checkboard();
}

由于imx6ull默认配置中定义了CONFIG_OF_CONTROL,也就是在u-boot中也使用了设备树文件,所以从设备树中获取“model”属性并打印出来,然后继续调用了checkboard函数:

/* file: board/freescale/mx6ullevk/mx6ullevk.c */
int checkboard(void)
{
	if (is_cpu_type(MXC_CPU_MX6ULZ))
		puts("Board: MX6ULZ 14x14 EVK\n");
	else
		puts("Board: MX6ULL 14x14 EVK\n");

	return 0;
}

3.4.2.12 init_func_i2c:初始化i2c并打印
/* file: common/board_f.c */
static int init_func_i2c(void)
{
	puts("I2C:   ");
	i2c_init_all();
	puts("ready\n");
	return 0;
}

3.4.2.13 announce_dram_init:打印“DRAM: ”字符串
/* file: common/board_f.c */
static int announce_dram_init(void)
{
	puts("DRAM:  ");
	return 0;
}

3.4.2.14 dram_init:设置gd结构体的ram_size成员
/* file: board/freescale/mx6ullevk/mx6ullevk.c */
int dram_init(void)
{
	gd->ram_size = imx_ddr_size();

	return 0;
}

函数并非初始化,其实就只是根据MMDC的配置来设置一下前面所说的gd全局变量:

unsigned int imx_ddr_size(void)
{
	struct esd_mmdc_regs *mem = (struct esd_mmdc_regs *)MEMCTL_BASE;
	unsigned int ctl = readl(&mem->ctl);
	unsigned int misc = readl(&mem->misc);
	int bits = 11 + 0 + 0 + 1;      /* row + col + bank + width */

	bits += ESD_MMDC_CTL_GET_ROW(ctl);
	bits += col_lookup[ESD_MMDC_CTL_GET_COLUMN(ctl)];
	bits += bank_lookup[ESD_MMDC_MISC_GET_BANK(misc)];
	bits += ESD_MMDC_CTL_GET_WIDTH(ctl);
	bits += ESD_MMDC_CTL_GET_CS1(ctl);

	/* The MX6 can do only 3840 MiB of DRAM */
	if (bits == 32)
		return 0xf0000000;

	return 1 << bits;
}

3.4.2.15 setup_dest_addr:从这里开始设置重定位的目的地址
static int setup_dest_addr(void)
{
	debug("Monitor len: %08lX\n", gd->mon_len);
	/*
	 * Ram is setup, size stored in gd !!
	 */
	debug("Ram size: %08lX\n", (ulong)gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
	/*
	 * Subtract specified amount of memory to hide so that it won't
	 * get "touched" at all by U-Boot. By fixing up gd->ram_size
	 * the Linux kernel should now get passed the now "corrected"
	 * memory size and won't touch it either. This should work
	 * for arch/ppc and arch/powerpc. Only Linux board ports in
	 * arch/powerpc with bootwrapper support, that recalculate the
	 * memory size from the SDRAM controller setup will have to
	 * get fixed.
	 */
	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
#ifdef CONFIG_SYS_SDRAM_BASE
	gd->ram_base = CONFIG_SYS_SDRAM_BASE;
#endif
	gd->ram_top = gd->ram_base + get_effective_memsize();
	gd->ram_top = board_get_usable_ram_top(gd->mon_len);
	gd->relocaddr = gd->ram_top;
	debug("Ram top: %08lX\n", (ulong)gd->ram_top);
#if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500))
	/*
	 * We need to make sure the location we intend to put secondary core
	 * boot code is reserved and not used by any part of u-boot
	 */
	if (gd->relocaddr > determine_mp_bootpg(NULL)) {
		gd->relocaddr = determine_mp_bootpg(NULL);
		debug("Reserving MP boot page to %08lx\n", gd->relocaddr);
	}
#endif

+	printf("gd->ram_size: %#x", gd->ram_size);
+	printf("gd->ram_base: %#x", gd->ram_base);
+	printf("gd->ram_top: %#x",  gd->ram_top);
+	printf("gd->relocaddr: %#x", gd->relocaddr);

	return 0;
}

各种宏定义有点多,可以根据宏定义去查看设置过程,当然也可以直接在设置之后加上打印语句一目了然。


3.4.2.16 reserve_fdt:预留设备树文件的内存,并设置设备树新的内存地址到gd->new_fdt
/* file: common/board_f.c */
static int reserve_fdt(void)
{
#ifndef CONFIG_OF_EMBED
	/*
	 * If the device tree is sitting immediately above our image then we
	 * must relocate it. If it is embedded in the data section, then it
	 * will be relocated with other data.
	 */
	if (gd->fdt_blob) {
		gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob), 32);

		gd->start_addr_sp = reserve_stack_aligned(gd->fdt_size);
		gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
		debug("Reserving %lu Bytes for FDT at: %08lx\n",
		      gd->fdt_size, gd->start_addr_sp);
	}
#endif

	return 0;
}

3.4.2.17 dram_init_banksize:设置gd结构体的dram信息
/* file: common/board_f.c */
__weak int dram_init_banksize(void)
{
	gd->bd->bi_dram[0].start = gd->ram_base;
	gd->bd->bi_dram[0].size = get_effective_memsize();

	return 0;
}

3.4.2.18 show_dram_config:打印内存的大小
/* file: common/board_f.c */
static int show_dram_config(void)
{
	unsigned long long size;
	int i;

	debug("\nRAM Configuration:\n");
	for (i = size = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
		size += gd->bd->bi_dram[i].size;
		debug("Bank #%d: %llx ", i,
		      (unsigned long long)(gd->bd->bi_dram[i].start));
#ifdef DEBUG
		print_size(gd->bd->bi_dram[i].size, "\n");
#endif
	}
	debug("\nDRAM:  ");

	print_size(size, "");
	board_add_ram_info(0);
	putc('\n');

	return 0;
}

这个函数才真正的打印出DRAM的大小,如果还想知道单位等等就继续往print_size里查看,这里不继续往里探讨。


3.4.2.19 display_new_sp:打印新的sp栈内存地址
/* file: common/board_f.c */
static int display_new_sp(void)
{
	debug("New Stack Pointer is: %08lx\n", gd->start_addr_sp);

	return 0;
}

3.4.2.20 reloc_fdt:拷贝u-boot的设备树内容到新的地址去(即重定位),并重新设置gd->fdt_blob
/* file: common/board_f.c */
static int reloc_fdt(void)
{
#ifndef CONFIG_OF_EMBED
	if (gd->flags & GD_FLG_SKIP_RELOC)
		return 0;
	if (gd->new_fdt) {
		memcpy(gd->new_fdt, gd->fdt_blob, fdt_totalsize(gd->fdt_blob));
		gd->fdt_blob = gd->new_fdt;
	}
#endif

	return 0;
}

这里需要知道的是,gd->fdt_blob最初已经在fdtdec_setup函数默认设置为u-boot的尾部了,因为默认配置中的设备树是和u-boot分开的。这个函数的功能是重定位设备树,并不是和u-boot程序一起重定位的。


3.4.2.21 setup_reloc:设置重定位相关的信息,后面会用到
/* file: common/board_f.c */
static int setup_reloc(void)
{
	if (gd->flags & GD_FLG_SKIP_RELOC) {
		debug("Skipping relocation due to flag\n");
		return 0;
	}

#ifdef CONFIG_SYS_TEXT_BASE
#ifdef ARM
	gd->reloc_off = gd->relocaddr - (unsigned long)__image_copy_start;
#elif defined(CONFIG_M68K)
	/*
	 * On all ColdFire arch cpu, monitor code starts always
	 * just after the default vector table location, so at 0x400
	 */
	gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400);
#elif !defined(CONFIG_SANDBOX)
	gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
#endif
#endif
	memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));

	debug("Relocation Offset is: %08lx\n", gd->reloc_off);
	debug("Relocating to %08lx, new gd at %08lx, sp at %08lx\n",
	      gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd),
	      gd->start_addr_sp);

	return 0;
}

setup_reloc函数计算了重定位的偏移地址值赋给gd->reloc_off,并且将gd结构体拷贝到前面reserve_xxx系列函数计算得到的新地址gd->new_gd,这里所涉及的2个gd结构体成员很重要,board_init_f函数返回到_main函数里马上用来赋值给r0和r9寄存器:

/* file: arch/arm/lib/crt0.S */
	bl	board_init_f

	ldr	r0, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
	bic	r0, r0, #7	/* 8-byte alignment for ABI compliance */
	mov	sp, r0
	ldr	r9, [r9, #GD_NEW_GD]		/* r9 <- gd->new_gd */

	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0
#if defined(CONFIG_CPU_V7M)
	orr	lr, #1				/* As required by Thumb-only */
#endif
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	b	relocate_code

所以接下来的任务就是分析relocate_code代码重定位了。

未完待续…

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R-QWERT

你的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值