根据笔记2对Makefile的配置,接下来就应该加入这些平台相关的代码到相应的目录下。
所以这次的主要任务是:增加代碼或修改代碼
把平台相关的代碼,加入内核工程中:
(1)将soc3210-board目录拷贝到arch/mips目录下,这里拷贝到arch/mips/mips-boards/。
这个目录将在arch/mips/kernel/Makefile中添加进去,这样集成到内核里。
(2)将mach-longmeng目录拷贝到include/asm-mips/目录下,这部分代码也将在arch/mips/kernel/Makefile中添加进去,作为头文件路径。这部分代码是loongmeng平台的特殊性,对SOC32101不知道相不相关。
(3)PCI操作相关的两个文件:fixup-soc-soc.c和ops-soc-soc.c拷贝到./arch/mips/pci/下。
(4)把平台相关的头文件目录soc-soc/加入到./include/asm-mips/下面,其中soc_soc.h保存了外设资源的地址。
在内核工程中的代碼上添加或修改平台相关的代碼:
我们开始看代码,汇编部分不用作修改,直接看main.c文件,汇编head.S执行完之后直接跳转到main.c中的start_kernel函数。
在start_kernel函数中,看与平台相关的函数setup_arch。
(1)cpu_probe() 从函数名中看,这是一个探测CPU信息的函数,所以这肯定是一个需要修改的部分。
进入cpu_probe,可以看到这个函数实际上就是读协处理器C0的PRId寄存器值。查看《See Mips Run》关于这部分的内容:
31-----------------------24-23-------------------16-15-------------8-7----------------0
| Company Options | Company ID | CPU ID | Revision |
------------------------------------------------------------------------------------------------
Company ID字段是新近定义的,因此,以前的CPU都把其设为0。
所以:
struct cpuinfo_mips *c = ¤t_cpu_data;
c->processor_id = PRID_IMP_UNKNOWN;
c->fpu_id = FPIR_IMP_NONE;
c->cputype = CPU_UNKNOWN;
c->processor_id = read_c0_prid();
switch (c->processor_id & 0xff0000) {
... ...
}
c->processor_id & 0xff0000为0
执行:cpu_probe_legacy(c);
switch (c->processor_id & 0xff00) {
... ...
}
实际上开始判断CPU ID,然后根据CPU ID来填写cpuinfo_mips。
在include/asm-mips/cpu.h中添加:
#definePRID_IMP_SOC32101 0x4200
#define CPU_SOC32101 65
#defineCPU_LAST 66
在cpu_probe_legacy里增加PRID_IMP_SOC32101的操作:
switch (c->processor_id & 0xff00) {
case ... ...
... ...
case PRID_IMP_SOC32101:
c->cputype = CPU_SOC32101;
c->isa_level = MIPS_CPU_ISA_II;
c->options = R4K_OPTS |
/* MIPS_CPU_FPU | */ MIPS_CPU_LLSC;
c->tlbsize = 48;
break;
}
对于上面定义的宏CPU_SOC32101,这个只是在本内核中的一个代号而已,没有特殊意义的。
(2)在./arch/mips/kernel/proc.c中,在cpu_name数组下多加一个元素:
static const char *cpu_name[] = {
... ...
[CPU_SOC32101] = "SOC32101",
}
此时CPU_SOC32101就是刚才在cpu.h中定义的一个宏,而这个宏的具体值就是按这个数组的第几个元素,或者说是下标,则在本内核中所支持的CPU编号。当然了宏CPU_LAST的值就是最后的一个CPU编号,要大于或者等于最后一个,我们的最后一个是CPU_SOC32101。
在show_cpuinfo()中可以看到打印这些信息的操作。相应地,在文件系统/proc下有一个cpuinfo的文件,cat cpuinfo就可以看到这些信息了。
(3)关于mips_machgroup和mips_machtype这两个变量
执行完cpu_probe之后就是prom_init()了。prom_init函数定义在./arch/mips/mips-boards/soc32101-boards/soc-soc/prom.c里面。这个文件是平台相关的,在笔记2里说的被拷贝到内核源码的平台相关代码。在prom_init函数一开始就对这两个变量进行赋值:
mips_machgroup = MACH_GROUP_ICT;
mips_machtype = MACH_SOC3210EV2E;
所以MACH_GROUP_ICT和MACH_SOC3210EV2E这两个宏要定义了。
关于组和类型的定义,一般在文件./include/asm-mips/bootinfo.h里定义的,所以在该文件里添加两个宏:
#defineMACH_GROUP_ICT 23
#define MACH_SOC3210EV2E 5
这两个宏的值不是特殊的值,只要不跟之前的重复了就行了。根据字面意思,大概这两个宏的意义是CPU组和该CPU在该组内的ID。这两个变量在代码中的具体的作用不太明确。因为之前已经存在一些CPU相关的宏定义了。当然用这两个变量也可以作一些与该CPU相关的特殊的操作。
(4)在./include/asm-mips/modules.h里定义MODULE_PROC_FAMILY
可以在看到这个文件里已经为各种支持的CPU都定义这个宏了,所以在后面加上:
#elifdefined CONFIG_CPU_SOC32101
#defineMODULE_PROC_FAMILY "SOC32101"
这个宏的实际意义不太清楚,搜索了一遍代码,没有发现代码中用到这个宏来作任何操作,但是如果不定义这个宏的话,编译会报错,因为:
#else
#error MODULE_PROC_FAMILY undefined for your processor configuration
#endif
(5)修改MM部分,主要是Cache和TLB
编译通过之后,下载到开发板运行了一下,結果打印一些信息就停止了。后来跟踪代码发现进入trap_init之后,就不出来了。打开./arch/mips/kernel/traps.c查看trap_init函数,发现这个函数的主要功能是设置异常处理,不过在最后调用了两个函数:
flush_icache_range(ebase, ebase + 0x400);
flush_tlb_handlers();
从函数的名字上可以看到这两个函数是操作cache和tlb的,程序就停在这里。
后查资料知道,龙芯CPU对Cache和TLB的管理有其特殊性,在笔记2的第4点也提到了。所以要对./arch/mips/mm/目录下的相关文件进行修改。
在./include/asm-mips/cacheops.h中,
修改:
#define Hit_Invalidate_I 0x10
为:
#ifdefined(CONFIG_CPU_SOC32101)
#defineHit_Invalidate_I 0x00
#else
#defineHit_Invalidate_I 0x10
#endif
如果编译报错找不到CONFIG_CPU_SOC32101这个宏,那么添加头文件#include <linux/config.h>。
probe_pcache里添加:
switch (c->cputype) {
case ... ...
......
break;
caseCPU_SOC32101:
icache_size= 1 << (12 + ((config & CONF_IC) >> 9));
c->icache.linesz= 16 << ((config & CONF_IB) >> 5);
if(prid & 0x3) {
c->icache.ways = 4;
}else {
c->icache.ways = 2;
}
c->icache.waybit=0;
dcache_size= 1 << (12 + ((config & CONF_DC) >> 6));
c->dcache.linesz= 16 << ((config & CONF_DB) >> 4);
if(prid & 0x3) {
c->dcache.ways = 4;
}else {
c->dcache.ways = 2;
}
c->dcache.waybit= 0;
break;
}
在./arch/mips/mm/tlb-r4k.c去掉宏定义BARRIER的意义:
#ifdefined(CONFIG_CPU_SOC32101)
#defineBARRIER
#else
….....
#endif
在./arch/mips/mm/tlbex.c中加入操作码insn_dsrl32
在opcode枚举中加多一个成员:
insn_dsrl32
在insn_table加多一个成员:
{insn_dsrl32, M(spec_op,0,0,0,0,dsrl32_op), RT | RD | RE },
在build_get_pmde64函数中:
修改:
i_dsrl(p,tmp, tmp, PGDIR_SHIFT-3);
为:
if(PGDIR_SHIFT - 3 >= 32) {
i_dsrl32(p,tmp, tmp, PGDIR_SHIFT-3 - 32); /* get pgd offset in bytes */
}else{
i_dsrl(p,tmp, tmp, PGDIR_SHIFT-3); /* get pgd offset in bytes */
}
这个函数从字面上看,是64位的操作,不太清楚这段的应用。
build_adjust_context中对shift变量操作加入:
#ifdefCONFIG_PAGE_SIZE_16KB
shift+=2;
#endif
这个是针对页面大小为16K的情况,页面大小可在内核配置里设置,一般都是4K
按上面的记录修改后,内核跑过trap_init这个函数里,但是还是继续跑了一段就停止了,继续调试,找到原因。
PS:这部分的修改非常重要,主要是根据实际的平台作相应的修改,特别是关于CPU的Cache和TLB部分,由于不太清楚,现在只是通过对比以前的内核来确定这些修改,而这些修改对移植到一个新的CPU是至关重要的,所以这部分要继续深入。
-