libgloss/mips/cma101.c has a function called __sizemem() that cleverly
switches to non-cached kseg1 to size memory. However, our vr9721
boards seem to have some cache still enabled even in kseg1, so we end
up with the wrong $sp at startup.
I wrote a new function __destructive_mem_top() that tests for the top
of memory in a way that flushes the cache anyway. It returns the
correct value, but can't replace __sizemem() for other purposes
(sbrk() calls it) because it's destructive, although it does set
__sizemem_default to avoid duplicate work.
Comments? Problems? Approval? ;-)
DJ
* mips/cma101.c (__destructive_mem_top): New.
* mips/crt0.S: Use it.
Index: cma101.c
===================================================================
RCS file: /cvs/src/src/libgloss/mips/cma101.c,v
retrieving revision 1.5
diff -p -U3 -r1.5 cma101.c
--- cma101.c 2 May 2003 20:06:52 -0000 1.5
+++ cma101.c 1 Jun 2007 21:05:12 -0000
@@ -258,6 +258,68 @@ __sizemem ()
return((probe - base) * sizeof(unsigned int));
}
+/* Similar to the above, but destructively tests in case the cache is
+ partially enabled (vr9721). Used only by crt0. */
+void *
+__destructive_mem_top ()
+{
+ volatile unsigned int *base;
+ volatile unsigned int *probe, *maxtop;
+ unsigned int baseorig;
+ unsigned int sr;
+ extern char end[];
+ char *endptr = (char *)&end;
+ int extra, offset;
+
+ /* Max of 2Gb RAM. */
+ maxtop = 0x80000000;
+ offset = 0;
+
+ /* If we are running in kernel segment 0 (possibly cached), try sizing memory
+ in kernel segment 1 (uncached) to avoid some problems with monitors. */
+ if (endptr >= K0BASE_ADDR && endptr < K1BASE_ADDR)
+ {
+ endptr = (endptr - K0BASE_ADDR) + K1BASE_ADDR;
+ /* Should be 0xB0000000 - 512M RAM. */
+ maxtop = K1BASE_ADDR + (K1BASE_ADDR - K0BASE_ADDR);
+ offset = K1BASE_ADDR - K0BASE_ADDR;
+ }
+
+ INTDISABLE(sr,baseorig); /* disable all interrupt masks */
+
+ __default_buserr_handler();
+ __cpu_flush();
+
+ DOSYNC();
+
+ /* Align to our step, so that the first failing step is just after
+ the last byte of memory. */
+ extra = ((int) endptr & (SM_INCR - 1));
+ base = ((void *) endptr + SM_INCR - extra);
+ baseorig = *base;
+
+ for (probe = base; probe < maxtop; probe += SM_INCR)
+ *probe = (unsigned int)probe;
+ DOSYNC();
+ for (probe = base; probe < maxtop; probe += SM_INCR)
+ if (*probe != (unsigned int)probe)
+ break;
+
+ probe -= (SM_INCR / sizeof(*probe));
+
+ *base = baseorig;
+ __restore_buserr_handler();
+ __cpu_flush();
+
+ DOSYNC();
+
+ INTRESTORE(sr); /* restore interrupt mask to entry state */
+
+ __sizemem_default = ((probe - base) * sizeof(unsigned int));
+
+ return (char *)probe - offset;
+}
+
/* Provided as a function, so as to avoid reading the I/O location
multiple times: */
static int
Index: crt0.S
===================================================================
RCS file: /cvs/src/src/libgloss/mips/crt0.S,v
retrieving revision 1.11
diff -p -U3 -r1.11 crt0.S
--- crt0.S 27 Nov 2006 16:12:51 -0000 1.11
+++ crt0.S 1 Jun 2007 21:05:12 -0000
@@ -161,12 +161,11 @@ zerobss:
bne t0,zero,4f
nop
- /* NOTE: a0[0] contains the amount of memory available, and
- not the last memory address. */
- la a0, __memsize
- lw t0,0(a0) # last address of memory available
- la t1,K0BASE # cached kernel memory
- addu t0,t0,t1 # get the end of memory address
+ jal __destructive_mem_top
+ nop
+ /* NOTE: v0 now contains a pointer to the first invalid memory location. */
+
+ move t0,v0
/* Allocate 32 bytes for the register parameters. Allocate 16
bytes for a null argv and envp. Round the result up to 64
bytes to preserve alignment. */