GENERIC is too large and breaks stack in bootloader

Hisanobu Tomari <[email protected]> Wed, 6 Mar 2013 11:18:06 +0900
Newsgroups gmane.os.netbsd.ports.prep
Message-ID <[email protected]>
Hi,

I'm trying to netboot NetBSD/prep GENERIC 6.0 and 6.1-RC1 kernels on my
IBM 7248-120 (PReP) with 96 megabytes of memory. The machine has been
running NetBSD-5.0 for a while. On 6.0 and 6.1-RC1, the gzipped kernel
size is larger than the space between the load address and the stack in
the bootloader, so it breaks the bootloader, and no error message is
displayed.

With GENERIC kernel in 6.1-RC1, the values when the control reaches the
loops at init_in(u_long) in src/sys/arch/prep/stand/boot/inkernel.c is:

kern_len	0x21dae3
ladr		0x480410
KERNENTRY	0x600000
&i (the address of a value in the stack) = 0x808048

The kernel (which is 0x21dae3 bytes long) is then moved to address
0x600000, breaking the stack. INSTALL kernel is smaller and free of
this problem.

My proposal is to display a error message when the size of gzipped
kernel is larger than the space between stack and KERNENTRY.
At least this tells the user what is happening, instead of displaying
nothing. The attached patch is against inkernel.c, and implements this.
If someone knows more elegant way to get the SP, please replace mine
with that.

Other things that are nice to have include mkbootimage that warns
large kernels, and GENERIC_SMALL configuration. However, as the
absolute maximum image size that can be loaded can be determined
only after it is actually booted and the address of stack is specified,
whether these are `not too ad-hoc' needs to be discussed.

Best regards,
Hisanobu Tomari
inkernel.diff (text/x-patch, 900 B)
Index: inkernel.c
===================================================================
RCS file: /cvs/cvsroot/src/sys/arch/prep/stand/boot/inkernel.c,v
retrieving revision 1.9
diff -c -r1.9 inkernel.c
*** inkernel.c	28 Apr 2008 20:23:33 -0000	1.9
--- inkernel.c	6 Mar 2013 02:16:18 -0000
***************
*** 43,52 ****
--- 43,59 ----
  	extern char _start[], _edata[];
  	char *p = (char *)(ladr + (_edata - _start));
  	u_int i;
+ 	u_long sp;
  
  	for (i = 0; i < 4096; i++, p++) {
  		if (memcmp(p, prep_magic, PREP_MAGICSIZE) == 0) {
  			kern_len = *(int *)(p + PREP_MAGICSIZE);
+ 			/* check compressed kernel size against stack */
+ 			asm("mr %0,%%r1" : "=r"(sp));
+ 			if( (KERNENTRY<sp) && (sp<KERNENTRY+kern_len)) {
+ 				printf("Compressed kernel image is too large.\n");
+ 				return;
+ 			}
  			memmove((char *)KERNENTRY,
  				p + PREP_MAGICSIZE + KERNLENSIZE, kern_len);
  			return;