[U-Boot] Where I'm going with x86 board.c

Graeme Russ graeme.russ at gmail.com
Tue Dec 20 13:06:39 CET 2011


With Simon's work on generic relocation support, I thought I would throw in
what I am planning for arch/x86/lib/board.c

Now this is not a patch, it is a work-in-progress complete version of the
file (compiles, will test soon) - If feedback is positive, I'll add this to
an upcoming patch set

Notice the amount of wrapping around void functions - If all other arch's
follow this lead, most of this wrapping can be removed by changing the
function signatures.

Lines 428 - 585 are effectively the generic init sequence - The rest is
wrappers, init sequence arrays, or fluff that should be moved

I noticed something along the way - gd is no longer special... let me
explain...

Some arch's use a dedicated register for the gd pointer - This allows the
pointer to be written to prior to relocation. For x86, the gd pointer is
simply passed around as a function parameter early - If the init_sequence_f
functions accepted a gd pointer as a parameter, there would be no need for
it to be global prior to relocation and therefore no need to allocate a
permanent register for it - Of course do_init_loop() would no longer be
generic for both pre and post relocation. This does mess with the
stand-alone API, but as discussed before, stand alone applications should
not be accessing gd anyway, so there should be no API to break ;)

And on that note, the following comment is well and truly wrong:

 * The requirements for any new initalization function is simple: it
 * receives a pointer to the "global data" structure as it's only
 * argument, and returns an integer return code, where 0 means
 * "continue" and != 0 means "fatal error, hang the system".

And finally, here 'tis:

/*
 * (C) Copyright 2008-2011
 * Graeme Russ, <graeme.russ at gmail.com>
 *
 * (C) Copyright 2002
 * Daniel Engström, Omicron Ceti AB, <daniel at omicron.se>
 *
 * (C) Copyright 2002
 * Wolfgang Denk, DENX Software Engineering, <wd at denx.de>
 *
 * (C) Copyright 2002
 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Marius Groeger <mgroeger at sysgo.de>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <stdio_dev.h>
#include <version.h>
#include <malloc.h>
#include <net.h>
#include <ide.h>
#include <serial.h>
#include <asm/u-boot-x86.h>
#include <elf.h>

#ifdef CONFIG_BITBANGMII
#include <miiphy.h>
#endif

/*
 * Pointer to initial global data area
 *
 * Here we initialize it.
 */
#undef	XTRN_DECLARE_GLOBAL_DATA_PTR
#define XTRN_DECLARE_GLOBAL_DATA_PTR	/* empty = allocate here */
DECLARE_GLOBAL_DATA_PTR = (gd_t *) (CONFIG_SYS_INIT_GD_ADDR);

/************************************************************************
 * Init Utilities							*
 ************************************************************************
 * Some of this code should be moved into the core functions,
 * or dropped completely,
 * but let's get it working (again) first...
 */
static int init_baudrate(void)
{
	gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);
	return 0;
}

static int display_banner(void)
{

	printf("\n\n%s\n\n", version_string);

	return 0;
}

static int display_dram_config(void)
{
	int i;

	puts("DRAM Configuration:\n");

	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
		printf("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);
		print_size(gd->bd->bi_dram[i].size, "\n");
	}

	return 0;
}

#ifndef CONFIG_SYS_NO_FLASH
static void display_flash_config(ulong size)
{
	puts("Flash: ");
	print_size(size, "\n");
}
#endif

/*
 * Breath some life into the board...
 *
 * Initialize an SMC for serial comms, and carry out some hardware
 * tests.
 *
 * The first part of initialization is running from Flash memory;
 * its main purpose is to initialize the RAM so that we
 * can relocate the monitor code to RAM.
 */

/*
 * All attempts to come up with a "common" initialization sequence
 * that works for all boards and architectures failed: some of the
 * requirements are just _too_ different. To get rid of the resulting
 * mess of board dependend #ifdef'ed code we now make the whole
 * initialization sequence configurable to the user.
 *
 * The requirements for any new initalization function is simple: it
 * receives a pointer to the "global data" structure as it's only
 * argument, and returns an integer return code, where 0 means
 * "continue" and != 0 means "fatal error, hang the system".
 */
typedef int (init_fnc_t) (void);

static int calculate_relocation_address(gd_t *);
static int copy_uboot_to_ram(gd_t *);
static int clear_bss(gd_t *);
static int do_elf_reloc_fixups(gd_t *);

init_fnc_t *init_sequence_f[] = {
	cpu_init_f,
	board_early_init_f,
	env_init,
	init_baudrate,
	serial_init,
	console_init_f,
	dram_init_f,

	NULL,
};

gd_t *gd;
bd_t bd_data;

static int init_bd_struct_r(void)
{
	gd->bd = &bd_data;
	memset(gd->bd, 0, sizeof(bd_t));

	return 0;
}

static int mem_malloc_init_r(void)
{
	mem_malloc_init(((gd->relocaddr - CONFIG_SYS_MALLOC_LEN)+3)&~3,
			CONFIG_SYS_MALLOC_LEN);

	return 0;
}

static int set_default_baudrate_r(void)
{
	gd->baudrate = CONFIG_BAUDRATE;
	return 0;
}

#ifdef CONFIG_SERIAL_MULTI
static int serial_initialize_r(void)
{
	serial_initialize();

	return 0;
}
#endif

#ifndef CONFIG_SYS_NO_FLASH
static int flash_init_r(void)
{
	ulong size;

	/* configure available FLASH banks */
	size = flash_init();
	display_flash_config(size);
	show_boot_progress(0x24);

	return 0;
}
#endif

static int env_relocate_r(void)
{
	/* initialize environment */
	env_relocate();
	show_boot_progress(0x26);

	return 0;
}

#ifdef CONFIG_CMD_NET
static int init_ip_address_r(void)
{
	/* IP Address */
	bd_data.bi_ip_addr = getenv_IPaddr("ipaddr");

	return 0;
}
#endif

#ifdef CONFIG_PCI
static int pci_init_r(void)
{
	/*
	 * Do pci configuration
	 */
	pci_init();

	return 0;
}
#endif

static int jumptable_init_r(void)
{
	jumptable_init();

	return 0;
}

#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
static int pci_init_r(void)
{
	puts("PCMCIA:");
	pcmcia_init();

	return 0;
}
#endif

#if defined(CONFIG_CMD_KGDB)
static int kgdb_init_r(void)
{
	puts("KGDB:  ");
	kgdb_init();

	return 0;
}
#endif

static int enable_interrupts_r(void)
{
	/* enable exceptions */
	enable_interrupts();
	show_boot_progress(0x28);

	return 0;
}

#ifdef CONFIG_STATUS_LED
static int status_led_set_r(void)
{
	status_led_set(STATUS_LED_BOOT, STATUS_LED_BLINKING);
	udelay(20);

	return 0;
}
#endif

#if defined(CONFIG_CMD_NET)
static int set_bootfile_r(void)
{
	char *s;

	s = getenv("bootfile");

	if (s != NULL)
		copy_filename(BootFile, s, sizeof(BootFile));

	return 0;
}

static int eth_initialize_r(void)
{
	puts("Net:   ");
	eth_initialize(gd->bd);

	return 0;
}

#ifdef CONFIG_RESET_PHY_R
static int reset_phy_r(void)
{
#ifdef DEBUG
	puts("Reset Ethernet PHY\n");
#endif
	reset_phy();

	return 0;
}
#endif

#endif

static int set_load_addr_r(void)
{
	/* Initialize from environment */
	load_addr = getenv_ulong("loadaddr", 16, load_addr);

	return 0;
}

#if defined(CONFIG_CMD_IDE)
static int ide_init_r(void)
{
	puts("IDE:   ");
	ide_init();

	return 0;
}
#endif

#if defined(CONFIG_CMD_SCSI)
static int scsi_init_r(void)
{
	puts("SCSI:  ");
	scsi_init();

	return 0;
}
#endif

#if defined(CONFIG_CMD_DOC)
static int doc_init_r(void)
{
	puts("DOC:   ");
	doc_init();

	return 0;
}
#endif

#ifdef CONFIG_BITBANGMII
static int bb_miiphy_init_r(void)
{
	bb_miiphy_init();

	return 0;
}
#endif

#ifdef CONFIG_POST
static int post_run_r(void)
{
	post_run(NULL, POST_RAM | post_bootmode_get(0));

	return 0;
}
#endif

init_fnc_t *init_sequence_r[] = {
	init_bd_struct_r,
	mem_malloc_init_r,
	set_default_baudrate_r,
	cpu_init_r,		/* basic cpu dependent setup */
	board_early_init_r,	/* basic board dependent setup */
	dram_init,		/* configure available RAM banks */
	interrupt_init,		/* set up exceptions */
	timer_init,
	display_banner,
	display_dram_config,
#ifdef CONFIG_SERIAL_MULTI
	serial_initialize_r,
#endif
#ifndef CONFIG_SYS_NO_FLASH
	flash_init_r,
#endif
	env_relocate_r,
#ifdef CONFIG_CMD_NET
	init_ip_address_r,
#endif
#ifdef CONFIG_PCI
	pci_init_r,
#endif
	stdio_init,
	jumptable_init_r,
	console_init_r,
#ifdef CONFIG_MISC_INIT_R
	misc_init_r,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
	pci_init_r,
#endif
#if defined(CONFIG_CMD_KGDB)
	kgdb_init_r,
#endif
	enable_interrupts_r,
#ifdef CONFIG_STATUS_LED
	status_led_set_r,
#endif
	set_load_addr_r,
#if defined(CONFIG_CMD_NET)
	set_bootfile_r,
#endif
#if defined(CONFIG_CMD_IDE)
	ide_init_r,
#endif
#if defined(CONFIG_CMD_SCSI)
	scsi_init_r,
#endif
#if defined(CONFIG_CMD_DOC)
	doc_init_r,
#endif
#ifdef CONFIG_BITBANGMII
	bb_miiphy_init_r,
#endif
#if defined(CONFIG_CMD_NET)
	eth_initialize_r,
#ifdef CONFIG_RESET_PHY_R
	reset_phy_r,
#endif
#endif
#ifdef CONFIG_LAST_STAGE_INIT
	last_stage_init,
#endif
	NULL,
};

static int calculate_relocation_address(gd_t *id)
{
	ulong text_start = (ulong)&__text_start;
	ulong bss_end = (ulong)&__bss_end;
	ulong dest_addr;
	ulong rel_offset;

	/* Calculate destination RAM Address and relocation offset */
	dest_addr = id->start_addr_sp;
	dest_addr -= CONFIG_SYS_STACK_SIZE;
	dest_addr -= (bss_end - text_start);

	/*
	 * Round destination address down to 16-byte boundary to keep
	 * IDT and GDT 16-byte aligned
	 */
	dest_addr &= ~15;

	rel_offset = dest_addr - text_start;

	id->relocaddr = dest_addr;
	id->reloc_off = rel_offset;

	return 0;
}

static int copy_uboot_to_ram(gd_t *id)
{
	size_t len = (size_t)&__data_end - (size_t)&__text_start;

	memcpy((void *)id->relocaddr, (void *)&__text_start, len);

	return 0;
}

static int clear_bss(gd_t *id)
{
	ulong dst_addr = (ulong)&__bss_start + id->reloc_off;
	size_t len = (size_t)&__bss_end - (size_t)&__bss_start;

	memset((void *)dst_addr, 0x00, len);

	return 0;
}

static int do_elf_reloc_fixups(gd_t *id)
{
	Elf32_Rel *re_src = (Elf32_Rel *)(&__rel_dyn_start);
	Elf32_Rel *re_end = (Elf32_Rel *)(&__rel_dyn_end);

	Elf32_Addr *offset_ptr_rom;
	Elf32_Addr *offset_ptr_ram;

	/* The size of the region of u-boot that runs out of RAM. */
	uintptr_t size = (uintptr_t)&__bss_end - (uintptr_t)&__text_start;

	do {
		/* Get the location from the relocation entry */
		offset_ptr_rom = (Elf32_Addr *)re_src->r_offset;

		/* Check that the location of the relocation is in .text */
		if (offset_ptr_rom >= (Elf32_Addr *)CONFIG_SYS_TEXT_BASE) {

			/* Switch to the in-RAM version */
			offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom +
							id->reloc_off);

			/* Check that the target points into .text */
			if (*offset_ptr_ram >= CONFIG_SYS_TEXT_BASE &&
					*offset_ptr_ram <
					(CONFIG_SYS_TEXT_BASE + size)) {
				*offset_ptr_ram += id->reloc_off;
			}
		}
	} while (re_src++ < re_end);

	return 0;
}

static void do_init_loop(init_fnc_t **init_fnc_ptr)
{
	for (; *init_fnc_ptr; ++init_fnc_ptr) {
		WATCHDOG_RESET();
		if ((*init_fnc_ptr)() != 0)
			hang();
	}
}

/* Load U-Boot into RAM, initialize BSS, perform relocation adjustments */
void board_init_f(ulong boot_flags)
{
	gd->flags = boot_flags;

	do_init_loop(init_sequence_f);

	/* SDRAM is now initialised setup a new stack in SDRAM */
	setup_sdram_environment(gd->ram_size, GENERATED_GBL_DATA_SIZE);

	/* NOTREACHED - relocate_code() does not return */
	while (1)
		;
}

typedef void (board_init_r_t) (gd_t *, ulong);

void relocate_code(ulong stack_ptr, gd_t *id, ulong reloc_addr)
{
	board_init_r_t *board_init_r_func;

	/* We are running from flash, but the stack is now in SDRAM */

	/* gd is still in CAR - Copy it into SDRAM */
	memcpy(id, gd, sizeof(gd_t));

	id->start_addr_sp = stack_ptr;

	if (init_cache() != 0)
		hang();

	calculate_relocation_address(id);
	copy_uboot_to_ram(id);
	clear_bss(id);
	do_elf_reloc_fixups(id);

	board_init_r_func = board_init_r;
	board_init_r_func += id->reloc_off;
	board_init_r_func(id, id->relocaddr);

	/* NOTREACHED - relocate_code() does not return */
	while (1)
		;
}

void board_init_r(gd_t *id, ulong dest_addr)
{
	/* Global data pointer is now writable */
	gd = id;

	gd->flags |= GD_FLG_RELOC;

	/* compiler optimization barrier needed for GCC >= 3.4 */
	__asm__ __volatile__("" : : : "memory");

	do_init_loop(init_sequence_r);

	/* 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 */
}

void hang(void)
{
	puts("### ERROR ### Please RESET the board ###\n");
	for (;;)
		;
}

unsigned long do_go_exec(ulong (*entry)(int, char * const []),
			 int argc, char * const argv[])
{
	unsigned long ret = 0;
	char **argv_tmp;

	/*
	 * x86 does not use a dedicated register to pass the pointer to
	 * the global_data, so it is instead passed as argv[-1]. By using
	 * argv[-1], the called 'Application' can use the contents of
	 * argv natively. However, to safely use argv[-1] a new copy of
	 * argv is needed with the extra element
	 */
	argv_tmp = malloc(sizeof(char *) * (argc + 1));

	if (argv_tmp) {
		argv_tmp[0] = (char *)gd;

		memcpy(&argv_tmp[1], argv, (size_t)(sizeof(char *) * argc));

		ret = (entry) (argc, &argv_tmp[1]);
		free(argv_tmp);
	}

	return ret;
}

void setup_pcat_compatibility(void)
	__attribute__((weak, alias("__setup_pcat_compatibility")));

void __setup_pcat_compatibility(void)
{
}




More information about the U-Boot mailing list