嵌入式linux编程arm初步接触之存储控制中的程序运行时动态加载

        韦东山老师的开发板在讲解存储管理技术的时候大致意思如下,片内ram只有4K,可执行程序大小超过4K怎么办,在这里韦东山老师的办法是在可执行代码的首部,运行自身拷贝,把自己拷贝到内存地址足够存放存放程序的地址处,然后直接跳转过去执行,这种技术在stm32里面叫做程序自举,这种技术也叫作程序动态加载,既可以用于软件自举,操作系统引导加载,还可以用于嵌入式系统不停机运行升级。
       这种技术本身跟操作系统内核引导加载是同一种技术,linux下我们使用uBoot加载操引导操作系统内核,内核再引导加载整个操作系统,都是用了这种技术。韦东山老师使用汇编编写的引导加载代码,并且在链接的时候指定了加载地址。我决定用C语言实现,汇编代码做尽可能少的工作。实在是汇编代码编写难度太大,只能回到熟悉的C了,用另一种思路实现,也算是对自己学习的考验吧。闲话休提,总计使用了两个C文件一个汇编文件,上代码。

//这个是systm.c文件,用于初始化硬件
#define	WATCH_DOG			(*(volatile unsigned long *)0x53000000)
#define SDRAM_BASE		(volatile unsigned long *)0x30000000
#define	MEM_CTL_BASE	(volatile unsigned long *)0x48000000

const unsigned long memcfgval[]={
	0x22011110,	0x00000700,	0x00000700,	0x00000700,
	0x00000700,	0x00000700,	0x00000700,	0x00018005,
	0x00018005,	0x008C07A3,	0x000000B1,	0x00000030,
	0x00000030
};

void disable_watch_dog(void)				//禁用看门狗
{
	WATCH_DOG = 0;
}

void mem_setup(void)								//配置内存存储设置
{
	int i;
	volatile unsigned long* ptr;
	ptr = MEM_CTL_BASE;
	for(i = 0; i < sizeof(memcfgval)/sizeof(memcfgval[0]); i++)
	{
		*ptr = memcfgval[i];
		ptr++;
	}
}

void executable_code_jmp(void)			//内存拷贝,实现自身从0地址拷贝到指定地址
{
	int i;
	volatile unsigned long* src;
	volatile unsigned long* dest;
	src=0;
	dest = SDRAM_BASE;
	for(i = 0; i < 1024; i++)
	{
			*dest = *src;
			dest++;
			src++;
	}
}

这个是汇编启动代码文件statrup.S,用于设置C使用环境
.text
.global _start
_start:
	ldr sp,=1<<12  
	bl disable_watch_dog
	bl mem_setup  
	bl executable_code_jmp           
  ldr sp,=0x34000000
  ldr pc,=(0x30000000 + main)
halt_loop:
	b halt_loop

这个是main.c文件,常规C程序,使用韦东山的点灯

#define	GPFCON		(*(volatile unsigned long *)0x56000050)
#define	GPFDAT		(*(volatile unsigned long *)0x56000054)

#define	GPF4_out	(1<<(4*2))
#define	GPF5_out	(1<<(5*2))
#define	GPF6_out	(1<<(6*2))

extern void disable_watch_dog(void);
extern void mem_setup(void);
extern void executable_code_jmp(void);

void  wait(volatile unsigned long dly)
{
	for(; dly > 0; dly--);
}

int main(void)
{
	unsigned long i = 0;
	GPFCON = GPF4_out|GPF5_out|GPF6_out;		// 将LED1,2,4对应的GPF4/5/6三个引脚设为输出
	while(1){
		wait(30000);
		GPFDAT = (~(i<<4));	 	// 根据i的值,点亮LED1,2,4
		if(++i == 8)
			i = 0;
	}

	return 0;
}
一下是makefile文件
led.bin:startup.S systm.c main.c
	arm-linux-gcc  -c -o startup.o startup.S
	arm-linux-gcc  -c -o systm.o systm.c
	arm-linux-gcc  -c -o main.o main.c
	arm-linux-ld -Ttext 0x00000000 -g startup.o systm.o main.o -o led_elf
	arm-linux-objcopy -O binary -S led_elf led.bin
	arm-linux-objdump -D -m arm led_elf > led.dis

汇编启动代码还是按照常规,关键代码在bl executable_code_jmp           
  ldr sp,=0x34000000
  ldr pc,=(0x30000000 + main)
executable_code_jmp 实现了代码自动拷贝到内存指定地址处,然后设置堆栈,直接跳转main函数入口地址+0x30000000处。反汇编结果如下所示
   c:    eb000024     bl    a4
  10:    e3a0d30d     mov    sp, #872415232    ; 0x34000000
  14:    e59ff000     ldr    pc, [pc, #0]    ; 1c <.text+0x1c>00000018 :
  18:    eafffffe     b    18
  1c:    30000154     andcc    r0, r0, r4, asr r1
        然后下载运行,跟韦东山的代码执行效果一模一样。在这里我使用  ldr pc,=(0x30000000 + main)语句跳转到main函数地址,韦东山的代码通过链接时候指定偏移地址0x30000000,然后bl main实现。可见,只要思路行得通,软件的实现方法是多种多样的。
       

你可能感兴趣的:(韦东山S3C2440板学习笔记)