diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f088c593db..63695621ff 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -239,4 +239,13 @@ config CARTEST help Test the CAR area after it has been set up. +config CHECK_STACK_USAGE + bool "Check stack usage" + default n + help + Continuously monitor stack usage and keep statistics. + The monitoring code is invoked from printk because printk is + a leaf function and consumes quite a bit of stack for itself. + This slows down booting a LOT. + endmenu diff --git a/arch/x86/stage1.c b/arch/x86/stage1.c index bcb1f2c4bb..7bd0e52a98 100644 --- a/arch/x86/stage1.c +++ b/arch/x86/stage1.c @@ -105,6 +105,28 @@ void global_vars_init(struct global_vars *globvars) } +#ifdef CONFIG_CHECK_STACK_USAGE +/* STACKFILL_BYTE could be a special value like 0x6b. Just make sure the stage0 + * code fills the complete CAR area with it. And the stack switching needs to + * overwrite the unused parts of the stack with STACKFILL_BYTE as well. + */ +#define STACKFILL_BYTE 0x0 +void check_stack() +{ + unsigned long stacksize, i; + char *lowestaddr; + if (global_vars()->ram_available) + stacksize = RAM_STACK_SIZE; + else + stacksize = CAR_STACK_SIZE; + lowestaddr = bottom_of_stack() - stacksize; + for (i = 0; i < stacksize; i++) + if (lowestaddr[i] != STACKFILL_BYTE) + break; + global_vars()->loweststack = lowestaddr + i; +} +#endif + void dump_mem_range(int msg_level, unsigned char *buf, int size) { int i; @@ -194,6 +216,10 @@ void __attribute__((stdcall)) stage1_phase1(u32 bist, u32 init_detected) */ console_init(); +#ifdef CONFIG_CHECK_STACK_USAGE + printk(BIOS_DEBUG, "Initial lowest stack is %p\n", + global_vars()->loweststack); +#endif if (bist!=0) { printk(BIOS_INFO, "BIST FAILED: %08x", bist); die(""); @@ -227,6 +253,10 @@ void __attribute__((stdcall)) stage1_phase1(u32 bist, u32 init_detected) die("Failed RAM init code\n"); printk(BIOS_DEBUG, "Done RAM init code\n"); +#ifdef CONFIG_CHECK_STACK_USAGE + printk(BIOS_DEBUG, "After RAM init, lowest stack is %p\n", + global_vars()->loweststack); +#endif /* Switch the stack location from CAR to RAM, rebuild the stack, * disable CAR and continue at stage1_phase3(). This is all wrapped in @@ -299,6 +329,9 @@ void __attribute__((stdcall)) stage1_phase3(void) mem->map[0].type = LB_MEM_RAM; #endif /* CONFIG_PAYLOAD_ELF_LOADER */ + /* Provide an easy way to check whether RAM is available. */ + global_vars()->ram_available = 1; + // location and size of image. init_archive(&archive); @@ -318,6 +351,10 @@ void __attribute__((stdcall)) stage1_phase3(void) #endif /* CONFIG_PAYLOAD_ELF_LOADER */ entry = load_file_segments(&archive, "normal/payload"); +#ifdef CONFIG_CHECK_STACK_USAGE + printk(BIOS_DEBUG, "Before handoff to payload, lowest stack is %p\n", + global_vars()->loweststack); +#endif if (entry != (void*)-1) { /* Final coreboot call before handing off to the payload. */ mainboard_pre_payload(); diff --git a/include/arch/x86/cpu.h b/include/arch/x86/cpu.h index 5f91c5eef9..43f5e0b07f 100644 --- a/include/arch/x86/cpu.h +++ b/include/arch/x86/cpu.h @@ -265,6 +265,7 @@ void * bottom_of_stack(void); EXPORT_SYMBOL(bottom_of_stack); struct global_vars * global_vars(void); EXPORT_SYMBOL(global_vars); +void check_stack(void); #define CAR_STACK_BASE (CONFIG_CARBASE + CONFIG_CARSIZE - 4) #define RAM_STACK_BASE 0x88ffc @@ -278,6 +279,8 @@ EXPORT_SYMBOL(global_vars); #else #define CAR_STACK_SIZE (CONFIG_CARSIZE - 4) #endif +/* To be honest, this limit is arbitrary and only used for stack checking. */ +#define RAM_STACK_SIZE (65536 - 4) /* resource maps. These started out as special for the K8 but now have more general usage */ /* it's not totally clear that the type and union are a great idea, but see the v2 code: diff --git a/include/globalvars.h b/include/globalvars.h index d3e8755683..bd3269caa0 100644 --- a/include/globalvars.h +++ b/include/globalvars.h @@ -63,6 +63,10 @@ struct global_vars { struct sys_info sys_info; /* has the spd hardware been set up? */ int spd_inited; + int ram_available; +#ifdef CONFIG_CHECK_STACK_USAGE + void *loweststack; +#endif }; #endif /* GLOBALVARS_H */ diff --git a/lib/console.c b/lib/console.c index 29b2524959..0eda0b1e49 100644 --- a/lib/console.c +++ b/lib/console.c @@ -150,6 +150,9 @@ int printk(int msg_level, const char *fmt, ...) i += vtxprintf(console_tx_byte, (void *)0, fmt, args); va_end(args); +#ifdef CONFIG_CHECK_STACK_USAGE + check_stack(); +#endif return i; }