在GBA上跑的linux
本修改都以Linux kernel 2.4.24+uclinux针对其的补丁为基础做的。 
在把补丁打好后,将我提供的linux-2.4.24中的文件覆盖进目录。然后将norfs目录中的linux-2.4.24目录中覆盖Kernel目录中相应文件就可以。然后make menuconfig,在System Type中设置ARM system type为GBA,打开General setup中的Support uClinux FLAT format binaries,打开File systems中的NOR file system support。然后make dep,make clean,make,将生成的elf文件linux拷贝到gbarom目录中。 
然后进入norfs目录中的gennorfs目录,输入make,make install,将生成norfs镜象的gennorfs文件装进系统中。 
最后进入make,则将生成linux.gba,这就是给GBA模拟器使用的2进制文件。 
3.GBA的简单介绍以及其优点和缺点 
GBA是一种掌上游戏机,用的ARM7TDMI,256K内存(有的介绍将其32K内存也算在其中的,这是完全不对的,因为那些内存是有专门作用的),显存、声道、多用串口等等一堆游戏机需要的东西,rom是nor flash,详细的介绍网上非常多,我也就不详细介绍了。 
GBA的优点:开发资源丰富。因为对GBA游戏开发有兴趣的人不少,所以对其介绍的资料相当多,还有不少专门的论坛对其的开发进行讨论。同时GBA有多种模拟器可以使用,而且功能都很不错。 
GBA的缺点:有点BT的环境。256K内存是无法将整个KERNEL放入其中的。最主要的问题是GBA将头16K写入了他自己的代码,也就是说GBA自己处理了svc到fiq,其中irq的处理会在GBA自己处理过后跳到指定的代码(这将在后面做详细介绍),其他的以我现有的认识都是无法取到的,GBA都用自己的代码处理掉了。 
4.介绍一下我写的bootloader 
GBA的执行是从0x8000000开始的,这也是flash的开始地址,但是实际上从0x8000004一直到0x80000c0以前的部分是有一套格式的,有什么图标字符、校验位等等一大堆东西,不过这些东西我都没能够在我的GBA镜象生成程序creatrom中完成,只是简单的按照规矩在bootloader中直接从0x8000000跳到了0x80000c0开始执行具体的bootloader代码。在bootloader中我主要做的工作是将存储在flash中的init节和data节放到内存中。下面我就来介绍一下bootloader代码bl.s: 
首先将mess的地址设置到r4,这个mess中存储了init和data在flash中的位置、长度、应在内存中的位置以及text的开始地址,这个部分是在creatrom程序中填写的。 
然后就是先init然后data的拷贝过程,其中具体的拷贝用的是gba_dma3_copy这个函数,这里用的是GBA的DMA3,GBA共有4个DMA,在实际的kernel中我还没能将这几个东西用上。 
在2次拷贝结束的时候,有这么2句: 
mov r0,#0 
mov r1,#210 
这是设置启动参数,在arch/armnommu/kernel/head-armv.S中是这样写的 
/* 
* Kernel startup entry point. 
* 
* The rules are: 
* r0 - should be 0 
* r1 - unique architecture number 
* MMU - off 
* I-cache - on or off 
* D-cache - off 
* 
* See linux/arch/arm/tools/mach-types for the complete list of numbers 
* for r1. 
*/ 
注意这个210是我后加的,具体我将在下面介绍对kernel的修改的时候进行介绍。 
设置好启动参数以后就是将mess中的text开始位置读出,跳到那个位置开始执行。 
5.我对kernel的修改的介绍 
我对kernel的修改除了我自己增加的目录arch/armnommu/mach-gba/和include/asm-armnommu/arch-gba/以外,我都用类似 
//teawater add for gba 2004.2.28------------------------------------------------ 
#ifdef CONFIG_ARCH_GBA 
#else 
#endif 
//AJ2D-------------------------------------------------------------------------- 
标记了出来。 
5.1.编译相关的修改 
arch/armnommu/Makefile,这里增加了一个GBA相关的条目,主要是设置连接脚本,其他也跟别的项目没什么不同,TEXTADDR这个部分本来是在连接脚本用指定kernel开始执行地址的地方,不过在我修改的代码中没什么作用,只是原来临时用保留下来的。 
arch/armnommu/config.in,这里是menuconfig的时候生成菜单以及进行编译配置的地方,在这其中: 
DRAM_BASE 内存的起始地址 
DRAM_SIZE 内存的长度 
FLASH_MEM_BASE flash的起始地址 
FLASH_SIZE flash的长度 
CONFIG_NO_PGT_CACHE 表示Disable pgtable cache 
CONFIG_CPU_WITH_CACHE 表示CPU有CACHE 启动的时候要clean 
CONFIG_CPU_WITH_MCR_INSTRUCTION 表示系统是否有MCR指令 
arch/armnommu/tools/mach-types,实际上这个文件是用来生成文件include/asm-armnommu/mach-types.h的,不过因为关联的问题,修改过这个文件后注意删除一下mach-types.h这个文件,保证修改的生效。 
5.2.连接脚本 
我使用了单独的连接脚本arch/armnommu/mach-gba/romlinux.lds。 
在这里比较主要的就是设置init和data的开始地址设置在了0x2001000,内存开始的部分在0x2000000,而这0x1000是交给kernel让其写中断处理代码的。而在0x8040d00是text的开始地址。 
如果仔细观察的话你可以发现我将*(.text.init)也放进了text,这样的目的就是为了节省内存。 
其中有1个地方比较关键: 
. = ALIGN(4096); 
__init_end = .; 
因为init节实际上在init函数中将要被释放掉的,而且其后跟着的就是init_task_union,所以这里一定要保证init节的长度跟页长度对齐,同时要保证这个__init_end生成的地址是跟task的长度对齐。而前面将开始地址设置在0x2001000也是出于这个目的。 
5.3.kernel在start_kernel以前的代码 
arch/armnommu/kernel/head-armv.S,这是kernel最开始启动的代码,这里体系相关的代码很多,通用的启动部分在448行,也就是我修改后代码的第464行。 
一上来就是函数__lookup_processor_type,这个函数是用来根据芯片的类型ID从.proc.info中取出相应的proc_info_list结构,这个结构定义在include/asm-armnommu/procinfo.h文件中,这个段中的数据是arch/armnommu/mm/proc-*.S文件中的,如果你的芯片比较特殊,可以定义自己的芯片结构以及编写自己的文件,arm7tdmi在arch/armnommu/mm/proc-arm6,7.S文件中。这个函数中,我们可以看到在arch/armnommu/config.in中定义的CONFIG_CPU_WITH_MCR_INSTRUCTION,我也尝试跑过mrc指令取得芯片ID,不过代码马上就跑到了未定义指令上去了,所以我估计用这个是不行的,就定义了不支持mcr。在下面也写明了 
# warning "FIXME: Get Processor ID without MCR Instruction" 
还举出了使用的例子 
@ A possible code 
@ldr r9, PROCESSOR_ID_MEM_LOCATION 
@ldr r9, [r9] 
看一下arch/armnommu/mm/proc-arm6,7.S文件,就可以找到定义arm7tdmi的结构的是729行开始的,所以代码应该写成 
ldr r9, __arm7tdmi_proc_info 
ldr r9, [r9] 
不过我开始是没仔细看这段注释的,直接就 
@set r9 to arm7tdmi id(0x41007700) 
mov r9,#0x7700 
add r9,r9,#0x41000000 
结果都是一样的,当然建议大家按uclinux提供的方式来弄比较好,我嘛,以后再改。 
然后就是一大段根据这个ID比较的过程,最后返回,一些查找结构失败的判断,然后就是函数__lookup_architecture_type。 
__lookup_architecture_type这个函数的作用是在.arch.info段中寻找相应的machine_desc结构,这个结构定义在include/asm-armnommu/mach/arch.h中,具体针对每个arch的定义就在arch/armnommu/kernel/arch.c以及arch/armnommu/mach-*/arch.c中,而GBA的就在arch/armnommu/mach-gba/arch.c中。其中具体的每个宏都在include/asm-armnommu/mach/arch.h中,都很明确我就不详细介绍了。 
往后还有一些初始化工作,不过因为跟没有什么改动,就不详细介绍了。 
5.3.start_kernel 
5.3.1.setup_arch 
这个函数用来做体系相关的初始化的,armnommu的在arch/armnommu/kernel/setup.c。注意一下540行,这里是默认的对物理内存结构meminfo的初始化,这个结构将在后面的内存初始化中起很重要的作用。在这其中,nr_banks指定了内存块的数量,bank是指定了每块内存的范围。而在这里用来指定块开始和长度的PAGE_OFFSET和MEM_SIZE,都定义在include/asm-armnommu/arch-gba/memory.h中,PAGE_OFFSET是内存的开始地址,我设置这个值为0x2001000,就是前面介绍的init和data的开始地址,结束地址自然就是内存的结束地址。后面函数就将根据meminfo进行内存结构初始化。 
其中还有一个地方的修改,bootmem_init函数中调用的函数reserve_node_zero,这个函数的作用是保留一些内存以便使值不能被动态分配。看一下这个函数的内部,原来的代码是: 
reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); 
这行的意思就是从_stext到_end都保留起来不做动态分配,而看我写的连接脚本就能知道这么写是不行的,因为_stext在flash中,所以我将这段代码改为: 
reserve_bootmem_node(pgdat, __pa(&__init_begin), &_end - &__init_begin); 
后面对init_arch_irq进行了初始化,这个将在后面用到。 
5.3.2.parse_options和calibrate_delay 
我的代码跑到这2行以后进去就跑不出来了,正好看一个文章写有什么问题,而且2个函数也没什么作用,所以我就直接将其去掉了。 

 
					
				
 
			
			
			
						
			 我要赚赏金
 我要赚赏金 STM32
STM32 MCU
MCU 通讯及无线技术
通讯及无线技术 物联网技术
物联网技术 电子DIY
电子DIY 板卡试用
板卡试用 基础知识
基础知识 软件与操作系统
软件与操作系统 我爱生活
我爱生活 小e食堂
小e食堂

