文章目錄
- 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 */
}