Recently I've been trying to build a uClinux (2.6.x kernel)ROM based
image for Coldfire uc5272, with the kernel executing (XIP) from within
ROM, .data, .init and .bss segments in the RAM, and ROMfs as rootfs in
ROM. This arrangement saves memory as only these changable parts of the
system are in RAM.
There are three files to change to build such an image:
1. vmlinux.lds.S
This
is the linker script file for 2.6.x kernel. It describes the memory
layout of the image. The .romvec and .text segments are defined in ROM,
and .data, .init, .bss segments are defined in RAM.
2. Head.S
This
is the second bootloader for 2.6.x kernel. It't the entry point of the
kernel. In this file, some codes are needed to move .data, .init into
RAM, and clear out .bss segments.
3. uclinux.c
This is the mapping driver for ROMFS partition support. Here the starting address of romfs has to be given.
I have made all the changes, load the image into ROM, and issue a "go" command. What I got first is:
go 0x10c20000 ...
Then
it just stopped there. After digging down for a while, I found the
problem. The _ramend variable is 0, while it's supposed to be the end of
the RAM. But I clearly set a value for it in head.S. And here is the
code snap:
GET_MEM_SIZE /* macro code determines size */
addl %a7,%d0
movel %d0,_ramend /* set end ram addr */
And here is the code for moving .data, .init into RAM:
lea _etext, %a0
lea _sdata, %a1
lea _sbss, %a2
_copy_data:
movel (%a0)+, (%a1)+
cmpal %a1, %a2
bhi _copy_data
How
come _ramend is 0? By the way, the logs at __log_buf (0x3df44 of RAM in
my case) indicates that there was a memory access violation, which
coincides a _ramend with wrong value.
The answer is really
simple. But finding the answer took me some time. It turned out that the
order of setting _ramend and moving .data into RAM is significant and I
did it in a wrong order.
The right order is to move .data into
RAM first and then set _ramend. Why? because when the image is burnt
into ROM, the .data segment is in ROM, with all the variables in it have
their init values. For _ramend, it's 0. But _ramend's address is
actually in RAM (0x2000c in my case). So if _ramend is set before .data
is moved, the set value will be over taken by the init value from ROM.
So the .data needs to be moved into RAM first, then set the value for
_ramend, the value will be kept and for later use.
How do I find the address of _ramend? I found it from System.map. The first 4 variables in .data segment are:
00020000 D _rambase
00020000 D _sdata
00020004 D _ramvec
00020008 D _ramstart
0002000c D _ramend