文章目录
- uboot 进入 main_loop() 前
- . arch/arm/cpu/u-boot.lds
- . arch/arm/cpu/armv7/start.S
- .设置异常向量表。
- . 定义uboot中断处理函数栈
- . reset 段:进入 SVC 模式、关闭 IRQ、FIQ.....
- . cp15协处理器的工作:修改异常向量表的入口地址为\_start、调用 lowlevel_init 函数.....
- .arch/arm/lib/ctr0.S/\_main
- 初始化 board_init_f() 环境
- .调用 board_init_f()
- .serial_init
- .dram_init
- relocate_code
- board_init_r
uboot 进入 main_loop() 前
主要文件:start.S,_main(board_init_f,relocate_code,board_init_r)
. arch/arm/cpu/u-boot.lds
链接脚本默认目录。
makefile、relocate_code、uboot.lds.md
. arch/arm/cpu/armv7/start.S
1. 初始化异常向量表
2. 设置 SVC 模式
3. 关中断
4. 配置cp15协处理器
5. 初始化 mmu、cache、tlb(cpu_init_cp15)
6. 板级初始化(cpu_init_crit)
.设置异常向量表。
#include <asm-offsets.h>
#include <config.h>
#include <version.h>
#include <asm/system.h>
#include <linux/linkage.h>
.globl _start // 声明 _start 为全局符号,_start 会被链接器链接,链接脚本的入口地址
/*
设置异常向量表(4B/item):
0. 复位异常:复位电平有效时,程序跳转到复位处理程序处执行
1. 未定义指令异常:遇到不能处理的指令时,产生未定义指令异常
2. 软件中断异常:执行SWI指令产生的异常
3. 预存指令异常:处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取终止异常
4. 数据操作异常:处理器数据访问指令的地址不存在时,或该地址不允许当前指令访问时,产生数据中止异常
5. 未使用
6. IRQ:外部中断请求有效,且 CPSR 中的 I 位为 0 时,产生 IRQ 异常
7. FIQ:快速中断请求引脚有效,且 CPSR 中的 F 位为 0 时,产生 FIQ 异常
*/
_start: b reset
ldr pc, _undefined_instruction // 跳转指令
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
#ifdef CONFIG_SPL_BUILD
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word _irq
_fiq: .word _fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#else // 如果没有定义 CONFIG_SPL_BUILD...
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#endif /* CONFIG_SPL_BUILD */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef // 指定接下来的代码要16字节对齐,空缺的用0xdeadbeef(非法值),方便更加高效的访问内存
. 定义uboot中断处理函数栈
#ifdef CONFIG_USE_IRQ // 如果uboot使用中断,这里会声明中断处理函数栈起始地址
/* 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
/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de
. reset 段:进入 SVC 模式、关闭 IRQ、FIQ…
// cpu 上电或者重启后执行的代码
reset:
bl save_boot_params // 再跳转到save_boot_params, 实际什么也没做,直接返回,毕竟栈没有初始化
/*
* 修改 CPSR 寄存器(程序状态寄存器),设置处理器进入 SVC 模式,并且关掉 IRQ、FIQ
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
...
ENTRY(save_boot_params)
bx lr @ back to my caller // 什么也没做,返回,栈没有初始化,最好不要在这里有操作
ENDPROC(save_boot_params)
.weak save_boot_params // 如果该函数在其它地方没有定义,则为空函数,有则定义该函数
. cp15协处理器的工作:修改异常向量表的入口地址为_start、调用 lowlevel_init 函数…
cp15 协处理器的工作:
1. 设置异常向量入口
2. cpu_init_cp15:配置 cp15 协处理器相关寄存器来设置处理器的 MMU、cache 以及 TLB。
3. cpu_init_crit:调用 `lowlevel_init函数` —— ddr、clk 初始化
/*
* Setup vector:
* (OMAP4 spl TEXT_BASE is not 32 byte aligned.
* Continue to use ROM code vector only in OMAP4 spl)
*/
/*
对cp15协处理器进行操作
*/
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start // 修改了ARM默认的异常向量表入口地址,将0x00变为了_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT // 如果没有定义这个宏...
bl cpu_init_cp15
bl cpu_init_crit
#endif
bl _main // 跳转到 main start.S 使命结束
...
.arch/arm/lib/ctr0.S/_main
初始化 board_init_f() 环境
/*这个文件处理uboot启动中与生成目标无关的阶段,即准备C环境。
该文件由 _start.S 进入。
主要执行顺序是:
1. 为调用 board_init_f() 设置初始化环境
该环境提供了一个栈和存储 GD 数据结构的地方
2. 调用 board_init_f() —— 为硬件做准备
使用 GD 存储数据 —— relocation destination、future stack、future GD location
(非SPL构建)
3. 建立中间环境
堆栈和GD为board_init_f()分配的,接下来设置BSS和SS
4. 调用relocate_code()
将uboot从当前位置前往board_init_f()中得到的目的地址
5. 为调用board_init_r()设置环境
BSS:初始化为0
初始化 non-const 数据
在系统中设置堆栈
一些CPU需要调用c_runtime_cpu_setup() 为GD做一些工作
6. 前往board_init_r()*/
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) // 加载栈指针到sp中
#endif
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ // 8 字节对齐
sub sp, #GD_SIZE /* allocate one GD above SP */ // 减去GD_SIZE的大小
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r9, sp /* GD is above SP */ // r9寄存器保存gd结构体的首地址
mov r0, #0
bl board_init_f
...
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9") // 使用 gd 结构体需要加入宏定义
.调用 board_init_f()
初始化 gd
调用执行 init_sequence
有必要关注的初始化函数:
arch_cpu_init: 可以先写一个空函数启动uboot
timer_init 在lib/time.c中有实现,也是空函数,但是有__WEAK关键字,如果自己实现,则会调用自己实现的这个函数
serial_init
mark_bootstage
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 */
dram_init
// board.c
void board_init_f(ulong bootflag)
{
...
gd->mon_len = _bss_end_ofs; // 初始化mon_len,代表uboot code的大小
...
// 遍历调用 init_sequence 所有函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
...
}
...
//
// 数组内容是指向函数的指针
init_fnc_t *init_sequence[] = {
arch_cpu_init, // 可以写一个空函数先启动uboot
mark_bootstage,
#ifdef CONFIG_OF_CONTROL
fdtdec_check_fdt,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, // timer_init在lib/time.c中有实现,也是空函数,但是有__WEAK关键字,如果自己实现,则会调用自己实现的这个函数
#ifdef CONFIG_BOARD_POSTCLK_INIT
board_postclk_init,
#endif
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, // 精简uboot启动必须
console_init_f, // 精简uboot启动必须
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_SYS_I2C)
init_func_i2c,
#endif
dram_init, // 精简uboot启动必须
NULL, // 退出循环
};
回到 board_init_f,剩余代码将会对sdram空间进行规划
// board_init_f
// 一部分内存空间隐藏
...
#if defined(CONFIG_SYS_MEM_TOP_HIDE) // CONFIG_SYS_MEM_TOP_HIDE 将一部分内存空间隐藏
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; // 可用sdram的顶端
...
// 预留出tlb空间
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
/* reserve TLB table */
gd->arch.tlb_size = 4096 * 4; // 如果打开了 icache 以及 dcache,则预留出PATABLE_SIZE大小的tlb空间
addr -= gd->arch.tlb_size;
/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);
gd->arch.tlb_addr = addr; // tlb空间存放首地址
debug("TLB table from %08lx to %08lx\n", addr, addr + gd->arch.tlb_size);
#endif
/* round down to next 4 kB limit */
addr &= ~(4096 - 1);
debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
// 确定 frambuffer
#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
gd->fb_base = CONFIG_FB_ADDR; // 获取frambuffer大小
#else
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr; // framebuffer 首地址
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len; // 为uboot的code留出空间
addr &= ~(4096 - 1);
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
// 确定 addr_sp 位置
#ifndef CONFIG_SPL_BUILD
/*
* 预留 malloc len 空间
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
debug("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> 10, addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd; // 全局信息 bd_t 结构体空间的首地址存在 gd->bd
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp);
#ifdef CONFIG_MACH_TYPE
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp; // gd_t 结构体的空间首地址在gd->bd
debug("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp);
#if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)
/*
* If the device tree is sitting immediate 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) {
fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
addr_sp -= fdt_size;
new_fdt = (void *)addr_sp;
debug("Reserving %zu Bytes for FDT at: %08lx\n",
fdt_size, addr_sp);
}
#endif
/* setup stackpointer for exeptions */
gd->irq_sp = addr_sp; // 异常栈指针
#ifdef CONFIG_USE_IRQ
addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
// 留出12字节
addr_sp -= 12;
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
#else
addr_sp += 128; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif
interrupt_init();
debug("New Stack Pointer is: %08lx\n", addr_sp);
#ifdef CONFIG_POST
post_bootmode_init();
post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif
gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */
gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug("relocation Offset is: %08lx\n", gd->reloc_off);
if (new_fdt) {
memcpy(new_fdt, gd->fdt_blob, fdt_size);
gd->fdt_blob = new_fdt;
}
memcpy(id, (void *)gd, sizeof(gd_t));
}
.serial_init
gd 中的 have_console 字段就是该函数设置的
// serial.c
int serial_init(void)
{
return get_current()->start(); // 串口驱动中给出的默认调试串口结构体,执行start,做一些特定串口初始化:console_init_f 将gd中have_console置1
}
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) // serial_current 用来存放我们当前要使用的 serial
dev = default_serial_console(); // default_serial_console 在 srial 驱动中有实现,来返回一个默认的调试串口
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;
}
.dram_init
对gd的ram_size字段进行设置(dram_init实现可以通过配置文件定义宏定义来实现,也可以通过对ddrc控制器读获取dram信息。)
// misc.c
int dram_init(void)
{
gd->ram_size = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE);
return 0;
}
// memsize.c
long get_ram_size(long *base, long maxsize)
{
volatile long *addr;
long save[32];
long cnt;
long val;
long size;
int i = 0;
for (cnt = (maxsize / sizeof (long)) >> 1; cnt > 0; cnt >>= 1) {
addr = base + cnt; /* pointer arith! */
sync ();
save[i++] = *addr;
sync ();
*addr = ~cnt;
}
addr = base;
sync ();
save[i] = *addr;
sync ();
*addr = 0;
sync ();
if ((val = *addr) != 0) {
/* Restore the original data before leaving the function.
*/
sync ();
*addr = save[i];
for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) {
addr = base + cnt;
sync ();
*addr = save[--i];
}
return (0);
}
for (cnt = 1; cnt < maxsize / sizeof (long); cnt <<= 1) {
addr = base + cnt; /* pointer arith! */
val = *addr;
*addr = save[--i];
if (val != ~cnt) {
size = cnt * sizeof (long);
/* Restore the original data before leaving the function.
*/
for (cnt <<= 1; cnt < maxsize / sizeof (long); cnt <<= 1) {
addr = base + cnt;
*addr = save[--i];
}
return (size);
}
}
return (maxsize);
}
relocate_code
board_init_r
gd->flags |= GD_FLG_RELOC; // 标志已经relocate
使能cache
调用板级支持函数—— board_init()
serial_initialize();
对malloc预留的空间初始化,起始地址,结束地址,清空。
接下来的代码是做一些外设的初始化
比如 mmc flash eth,环境变量的设置,还有中断的使能
...
进入死循环
for (;;) {
main_loop();
}
// cr0.s
...
here:
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
// bss 段清空
ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:cmp r0, r1 /* while not at end of BSS */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
bl coloured_LED_init // 实现上电后指示灯亮
bl red_led_on
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
ldr pc, =board_init_r /* this is auto-relocated! */
/* we should not return here. */
#endif
ENDPROC(_main)
// ...
ENTRY(c_runtime_cpu_setup)
/*
* 如果icache是enable,则无效掉icache,保证从sdram中更新指令到cache中
*/
#ifndef CONFIG_SYS_ICACHE_OFF
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
#endif
/*
* 更新异常向量表首地址
*/
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
bx lr
ENDPROC(c_runtime_cpu_setup)
// board.c
void board_init_r(gd_t *id, ulong dest_addr)
{
ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
ulong flash_size;
#endif
gd->flags |= GD_FLG_RELOC; // 置位 gd->flags,标志已经relocate
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); // 调试用
monitor_flash_len = _end_ofs;
/* Enable caches */
enable_caches();
debug("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */
/*
* 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
serial_initialize(); // drivers/serial/serial.c 所有串口驱动都会实现一个xxxx_serial_initialize函数,并且添加到serial_initialize中,总结一下,serial_initialize工作是将所有serial驱动中所有串口注册到serial_devices链表中,然后找到指定的默认串口。
debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);
#ifdef CONFIG_LOGBUFFER
logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
post_output_backlog();
#endif
/* The Malloc area is immediately below the monitor copy in DRAM */
malloc_start = dest_addr - TOTAL_MALLOC_LEN;
mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN); // 对malloc预留的空间初始化,起始地址,结束地址,清空。
#ifdef CONFIG_ARCH_EARLY_INIT_R
arch_early_init_r();
#endif
power_init_board();
#if !defined(CONFIG_SYS_NO_FLASH)
puts("Flash: ");
flash_size = flash_init();
if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
print_size(flash_size, "");
/*
* 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));
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND */
#endif
#if defined(CONFIG_CMD_ONENAND)
onenand_init();
#endif
#ifdef CONFIG_GENERIC_MMC
puts("MMC: ");
mmc_initialize(gd->bd);
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* initialize environment */
if (should_load_env())
env_relocate();
else
set_default_env(NULL);
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init();
#endif
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 */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
# ifdef CONFIG_OF_CONTROL
/* Put this here so it appears on the LCD, now it is ready */
display_fdt_model(gd->fdt_blob);
# else
checkboard();
# endif
#endif
#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();
/* Initialize from environment */
load_addr = getenv_ulong("loadaddr", 16, load_addr);
#ifdef CONFIG_BOARD_LATE_INIT
board_late_init();
#endif
#ifdef CONFIG_BITBANGMII
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
puts("Net: ");
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif
#ifdef CONFIG_POST
post_run(NULL, POST_RAM | post_bootmode_get(0));
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
/*
* Export available size of memory for Linux,
* taking into account the protected RAM at top of memory
*/
{
ulong pram = 0;
uchar memsz[32];
#ifdef CONFIG_PRAM
pram = getenv_ulong("pram", 10, CONFIG_PRAM);
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* Also take the logbuffer into account (pram is in kB) */
pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;
#endif
#endif
sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);
setenv("mem", (char *)memsz);
}
#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 */
}