一、常用命令 


 内存测试 

mt -v 

newmt 

下载内核 

通过网络下载 load tftp://10.2.5.22/vmlinux.32 


通过硬盘下载 load /dev/fs/ext2@wd0/boot/vmlinux.32 


 通过SD卡下载load //fs/ext2@sdcard0/boot/vmlinux.32 


升级 bios load -r -f 0xbfc00000 tftp://10.2..5.22/gzrom.bin 

输出到串口从硬盘启动内核 g console=ttyS0,115200 init=/bin/sh root=/dev/hda1 rw 

显示输出到显卡从硬盘启动内核 g console=tty root=/dev/hda1 fb =vfb:1 



二、常见问题 


1.串口不出字 


可以按如下顺序检查 

a.是否放入flash芯片烧入的bios是否跟主板匹配 

b.是否有晶振是否放对位置是否能起振倍频关系是否合适 

c.串口是否接对串口线是否是交叉线主机串口是否可用串口设置设备波特率是

否对) 

d.CPU能否从LPC flash中取指 



2.串口乱码怎么办 


a.bios中串口的波特率是否和串口终端上的波特率设置是否一致 

.串口线是否完好(可以换一跟串口线试一试 

.bios中串口初始化的时候设置的倍频系数是否和外部的时钟频率一致: 

LEAF(initserial_uart) 

li a0, GS3_UART_BASE 


 

li t1,128 

# addiu a2,a0,3 

sb t1,3(a0) 

li t1,0x12 # divider, highest possible baud rate, 33M 

# li t1,0x0e # divider, highest possible baud rate, 25M 

sb t1,0(a0) 

li t1,0x0 # divider, highest possible baud rate 

sb t1,1(a0) 

li t1,3 

sb t1,3(a0) 

........................................................ 

计算公式如下115200hz X 0x0e = 25M/16 

115200hz X 0x12 = 33M/16 

相应的kernel中串口频率也需要改过来主要修改的文件有 

include/asm-mips/serial.h 

include/asm/serial.h这个文件链接到 include/asm-mips/serial.h 

arch/mips/kernel/8250-platform.c 

如何在bios里面如何选用LPC串口 

在 bios的配置文件里面conf.3aconf里,有 USE_LPC_UAR选项选中就会使用LPC串口 

1. 

#option USE_LPC_UART 

如果要使用UART_0串口呢 

在配置文件中禁用USE_LPC_UART而在UART0和UART1之间选用呢 

修改相应B下的 /.中的 GS3——UART——

BASE的定义 

#define GS3_UART0_BASE 0xbfe001e0 

#define GS3_UART1_BASE 0xbfe001e8 

#define GS3_UART_BASE GS3_UART0_BASE 

如果串口始终不出字可用EJTAG看CPU到底有没有跑起来。 



3.碰到内存跑飞怎么办 


在时经常碰到内存跑飞的问题阅读.源代码可以看到出现这个问题

出现在把从中解压出来的代码和数据拷贝到内存的过称中。只要是内

存是正确的初始化并且当前设置的频率范围适当就不会出现这个问题。所以碰到这

个问题需要检查内存 



.在内存插槽内是否放入内存条 

.内存的晶振是否存在晶振频率、倍频系数是否合适 

内存条和晶振是否好的 

.放入内存条的插槽是否没有问题(比如有的MCP68板子上只能使用MC1 

4。在内存不是自动检测和配置的板卡上手动设置的选项通道大小)是否和板卡上内

存的位置和大小匹配(有两个控制器可以引出四个插槽每个控制器对应两个插槽) 

5。在内存自动检测和配置的板卡上通过I2C读出的DDR信息是否正确 

.内存参数是否匹配 


 


4.网卡不能用怎么办 


1.pmon里面是否可以找到设备devls:rte0,fxp0,rtk0,rte0,em0 

2。网卡是否没有损坏 

3。网卡的支持是否在相应bonito的conf配置选项里面加入进去 

4.网卡ping 的时候是否丢包3A3里面RTL8169网卡驱动里面是否要改个东西?) 

PCI基址的设置是否正确 

在对应Bonito下边检查pci/pci_machdep.c: 利用CPU 的PCI设备需要改这个) 

169 /*set pci base0 address and window size*/ 

170 pci_local_mem_pci_base = 0x80000000; 

171 #ifdef LS3_HT 

172 #else 

173 BONITO_PCIBASE0 = 0x80000000; 

174 #endif 



5.硬盘找不到怎么办 


1.硬盘的数据线和电源线是否接好 

2. BIOS里面的配置文件是否支持硬盘 

3.如果是利用HT端口接桥片HT的接受端口窗口是否打开 

#if 1//OPEN RX SPACE in HOST 

TTYDBG("HT RX DMA address ENABLE\r\n") 

dli t2, 0x90000efdfb000060 

li t0, 0xc0000000 

sw t0, 0x0(t2) 

li t0, 0x0080fff0 

sw t0, 0x4(t2) 

TTYDBG("HT RX DMA address ENABLE done 1\r\n") 

li t0, 0xc0000000 

sw t0, 0x8(t2) 

li t0, 0x00008000 

sw t0, 0xc(t2) 

TTYDBG("HT RX DMA address ENABLE done 2\r\n") 




三、BIOS启动 


 

bios是系统启动时最早的初始化和引导固件。主要完成初始化处理器状态、初始化cache

和DDR、划分并映射地址空间、设备自检、引导内核的功能。 


 


1.处理器状态初始化 


这个过程主要初始化CP0的一些寄存器设置CPU异常处理程序入口。系统reset或者上

电后首先会执行 

/* NOTE!! Not more that 16 instructions here!!! Right now it's FULL! */ 

mtc0 zero, COP_0_STATUS_REG 

mtc0 zero, COP_0_CAUSE_REG 

li t0, SR_BOOT_EXC_VEC /* Exception to Boostrap Location */ 

mtc0 t0, COP_0_STATUS_REG 

la sp, stack 

la gp, _gp 

SR_BOOT_EXC_VEC = 0x04000000,告诉CPU使用ROMKSEG1空间的异常入口在

操作系统启动后BEV一般设置为0。另外设置gp,sp寄存器因为以后初始化DDR 从

flash中load 数据的时候需要用到。 

Disassembly of section .text: 

80010000 <_ftext>: 

80010000: 00 60 80 40 00 68 80 40 40 00 08 3c 00 60 88 40 .`.@.h.@@..<.`.@ 

80010010: 01 80 1d 3c 00 c0 bd 67 10 80 1c 3c e0 c0 9c 67 ...<...g...<...g 

80010020: 00 00 00 00 e0 bf 02 3c 1c 01 42 34 04 00 43 8c .......<..B4..C. 

80010030: 0f 00 63 34 0f 00 63 38 04 00 43 ac 0d 00 03 24 ..c4..c8..C....$ 

80010040: 00 00 43 ac 00 10 03 24 ff ff 63 24 fe ff 60 14 ..C....$..c$..`. 

80010050: 00 00 00 00 f8 02 11 04 00 00 00 00 ............ 

实现的链接脚本如下 

ENTRY(_start) 

SECTIONS 

. = 0xffffffff80010000; 

.text : 

_ftext = . ; 

*(.text) 

*(.rodata) 

*(.rodata1) 

*(.reginfo) 

*(.init) 

*(.stub) 




 *(.gnu.warning) 

} =0 

_etext = .; 

。。。。。。。。 

_gp = ALIGN(16) + 0x7ff0; 

初始化gp是为了减少全局变量寻址的访存次数。而sp的值是 

mips64el-linux-nm gzrom | grep stack 

ffffffff80ffc000 T stack 


刚好对应于start.S中的 

_start: 

start: 

.globl stack 

stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */ 

而start.S中的_start来自编译链接脚本 zloader/ld.conf.S中的 

KENTRY(_start) 

8 SECTIONS 

9 { 

10 

11 . = 0xffffffff81000000; 

…………………. 

接下面放置的代码是一些约定的中断向量的地址具体见《MIPS处理器设计透视》85

页。它们并不会立即执行启动代码实际会接着执行 

locate: 

la s0,start 

subu s0,ra,s0 

and s0,0xffff0000 

li t0,SR_BOOT_EXC_VEC 

mtc0 t0,COP_0_STATUS_REG 

mtc0 zero,COP_0_CAUSE_REG 

.set noreorder 

这段代码主要是得到s0的值。在这段代码中ra还是 基于访问bios flash的0xbfc0000地

址而s0的地址是基于编译器指定的start0x81000之上的地址反汇编代码如下 

81000c38 <locate>: 

locate(): 

81000c38: 3c108100 lui s0,0x8100 

81000c3c: 66100000 daddiu s0,s0,0 

81000c40: 03f08023 subu s0,ra,s0 

81000c44: 3c01ffff lui at,0xffff 

81000c48: 02018024 and s0,s0,at 

81000c4c: 3c080040 lui t0,0x40 

81000c50: 40886000 mtc0 t0,c0_sr 

81000c54: 40806800 mtc0 zero,c0_cause 

81000c58: 3c14bfe0 lui s4,0xbfe0 

因此s0 = 0xbfc00000-0x81000000,它用来修正编译链接脚步中的指定的地址和访问flash芯

片的真实地址的偏移。此外s0如果为0怎表示系统是从RAM启动。这一点在阅读

start.S的代码中需要注意不能忘记。比如start.S串口打印的函数 

LEAF(stringserial) 



 move a2, ra 

#ifdef ROM_EXCEPTION 

li a1,0x3ec00000 

addu a1, a0, a1 

#else 

addu a1, a0, s0 

#endif 

lbu a0, 0(a1) 

………………………………………. 



接下来一个很重要的操作时在系统上打开64 bit 的地址空间。这是跟2F及之前的CPU 

bios里面有差别的地方因为LS3A及以后的芯片是支持64位的而且后面的代码中也会

出现64bit地址比如内存自检前使能SMB总线。 

//Open 64-bit address space 

mfc0 t0, CP0_STATUS 

li t1, 0x00e0 # {cu3,cu2,cu1,cu0}<={0110, status_fr<=1 

or t0, t0, t1 

mtc0 t0, CP0_STATUS 

.set mips64 

使能对User segment、supirvisor segment、kernel segment的64 bit 访问同时设置系统为

kernel model。为了便于 调试有的板卡上带有7段数码管下边这段代码就是往数码管

里面写入0x99的。这样调试人员就容易确定程序大概已经执行到的地方。 

li a0,0x900000001ff00080 

li t0,0x99 

sb t0,0x0(a0) 

完成上述操作之后主处理器核开始初始化一、二级cross bar和DDR而从处理器核进入

等待。多核之间的引导顺序可参考《龙芯3A启动概要》。 



2.DDR/TLB/cache的初始化 


龙芯3A及以后的多核芯片中每个处理器有不少于4个的处理器核每个核内部有自己

的指令cache、数据cache、指令TLB、数据TLB。这些由每个核自己初始化。核间通过一

级cross bar连接二级cache二级cache可以通过主处理器核初始化。同样连接在二级

cross bar 上的双通道内存控制器(MC0和MC1),一般也由主处理器核初始化。 

主处理器核首先会跳到core0_start去执行 

#ifdef LS3_HT 

b core0_start 

nop 

#endif 

首先通过下边的代码判断是否从RAM启动如果是就说明DDR已经初始化完成指令已

经是在内存中执行因此可以直接跳到initmips去执行。 

bnez s0,1f 

nop 

li a0,128 

la v0,initmips 



 jr v0 

nop 

1: 

否则必须先初始化内存和cache。后面接着会看到 

#include "loongson3_fixup.S" 

这是为了禁止一些猜测执行的非法访问以防止死机。 



接着调用 #include "loongson3_ddr2_config.S" 来执行内存初始化并且设置二级CROSS 

BAR的窗口。二级cross bar里必不可少的是把系统内存的低256M路由到放入内存的

DDR控制器上。如果要用到网卡等PCI设备的话还必须把PCI DMA空间路由到一个放

入了内存条的DDR控制器上。 

内存初始化完成之后理论上就可以用了。但只能通过uncache 的方式去访问因为这时

cache还没有初始化。不过uncach方式访存速度比较慢为此需要先初始化cache。至于 

TLB虽然PMON里面中不会用到但为了避免猜测执行可能导致的TLB的意外命中

需要初始化TLB的每一项的entry_hi为unmapped的地址以确保在BIOS运行过程中不

可能出现TLB命中。 

下面是初始化TLB的代码 

LEAF(CPU_TLBClear) 

li a3, 0 # First TLB index. 

li a2, PG_SIZE_4K 

MTC0 a2, COP_0_TLB_PG_MASK # Whatever... 

1: 

MTC0 zero, COP_0_TLB_HI # Clear entry high. 

MTC0 zero, COP_0_TLB_LO0 # Clear entry low0. 

MTC0 zero, COP_0_TLB_LO1 # Clear entry low1. 

mtc0 a3, COP_0_TLB_INDEX # Set the index. 

addiu a3, 1 

li a2, 64 

nop 

nop 

tlbwi # Write the TLB 

bne a3, a2, 1b 

nop 

jr ra 

nop 

END(CPU_TLBClear) 

清空TLB的每一项全部都清0,这样接可以避免命中CPU重启之后的一些不定状态。接

着初始化TLB从wired寄存器指定的到最后的TLB项 

LEAF(tlb_init) 

mtc0 $0, CP0_WIRED 

mtc0 $0, CP0_PAGEMASK 

tlb_flush_all: 

lui a0, 0x8000 

addiu a1, $0, 64 

#a0=KSEG0,a1 = tlbsize, v0, v1, a3 used as local registers 

mtc0 $0, CP0_ENTRYLO0 

mtc0 $0, CP0_ENTRYLO1 

mfc0 v0, CP0_WIRED 

addu v1, $0, a0 



 1: sltu a3, v0, a1 

beq a3, $0, 1f 

nop 

mtc0 v1, CP0_ENTRYHI 

mtc0 v0, CP0_INDEX 

tlbwi 

addiu v1, v1, 0x2000 

beq $0, $0, 1b 

addiu v0, v0, 1 

1: 


###tlb_init finish#### 

tlbp 

jr ra 

nop 

END(tlb_init) 

############################### 

注意这时候往 ENTRY_Hi中写入的是 0x80000000 + 0x2000 * n的值因为这段地址都是

unmap的所以这就可以保证每次都不会命中从而后面所有对mapped 空间的第一次方

访内存都会出现TLB Miss。接着跳到初始化一级cache的代码 

LEAF(godson2_cache_init) 

####part 2#### 

cache_detect_4way: 

mfc0 t4, CP0_CONFIG 

andi t5, t4, 0x0e00 // get size of L1 cache 

srl t5, t5, 9 // t5 = L1 cache size IC 

andi t6, t4, 0x01c0 // T6 DC size 

srl t6, t6, 6 

addiu t6, t6, 10 #4way 

addiu t5, t5, 10 #4way 

addiu t4, $0, 1 

sllv t6, t4, t6 // 

sllv t5, t4, t5 

addiu t7, $0, 4 //计算出 icache /dache大小及每一组的大小 16k 

####part 3#### 

lui a0, 0x8000 // start address of KSEG0 CACHED 

#addu a1, $0, t5 

#addu a2, $0, t6 

li a1, (1<<14) #64k/4way 

li a2, (1<<14) 

cache_init_d4way: 

#a0=0x80000000, a1=icache_size, a2=dcache_size 

#a3, v0 and v1 used as local registers 

mtc0 $0, CP0_TAGHI 

li t0, 0x22 

mtc0 t0, CP0_ECC 

addu v0, $0, a0 

addu v1, a0, a2 

1: slt a3, v0, v1 

beq a3, $0, 1f 

nop 

mtc0 $0, CP0_TAGLO // 

//对当前cache line 

cache Index_Store_Tag_D, 0x0(v0) // 初始化第一路对应cache line的TAG 域 

cache Index_Store_Tag_D, 0x1(v0) // 初始化第二路对应cache line 的 TAG 域 

cache Index_Store_Tag_D, 0x2(v0) 



 cache Index_Store_Tag_D, 0x3(v0) 

beq $0, $0, 1b 

addiu v0, v0, 0x20 

1: 

对 ICAHCE 也是一样初始化的过程如下 

cache_flush_i4way: 

addu v0, $0, a0 

addu v1, a0, a1 

mtc0 $0, CP0_TAGLO 

mtc0 $0, CP0_TAGHI 


 mtc0 $0, CP0_ECC 

1: slt a3, v0, v1 

beq a3, $0, 1f 

nop 

cache 0x08, 0x0(v0)/*Index_Store_Tag_I*/ 

cache 0x08, 0x1(v0)/*Index_Store_Tag_I*/ 

cache 0x08, 0x2(v0)/*Index_Store_Tag_I*/ 

cache 0x08, 0x3(v0)/*Index_Store_Tag_I*/ 

beq $0, $0, 1b 

addiu v0, v0, 0x20 

1: 

cache_init_finish: 

//TTYDBG ("\r\ncache init ok\r\n") 

jr ra 

nop 

// cache_init_panic: 

// TTYDBG ("\r\ncache init panic\r\n") 

// 1: b 1b 

// nop 

.end godson2_cache_init 

同理之后对scache 的初始化类似也是把相应的tat_lo, tag_hi清0.因此所有第一次访

存都会出现cache mipss,这时会直接去访问mem ,完成之后又会用访存的物理地址的高

位来设置tag域 至于具体哪一项的tag被替换是根据随即替换算法实现。 

这样此后就可以用cache 方式去访问0xbfc0000对应的指令和数据为此首先把当前

的ra (0xbfc00000)的地址与上0xdfff上实现,这样PC就变成了0x9fc开始的地址。 

#if 1 

PRINTSTR("Jump to 9fc\r\n") 

lui t0, 0xdfff ####################### go to 9fc 

ori t0, t0, 0xffff 

bal 1f 

nop 

1: 

and ra, ra, t0 

addiu ra, ra, 16 

jr ra 

nop 

#endif 

此外还需要配置KSEG0段访问的缓存行为设置为缓存模式。否则即便PC地址是

cached的实际的访存行还是uncache的。 

#if 1 

mfc0 $4, $16 

and $4,0xfffffff8 

or $4,0x3 // config寄存器的K0域为 0x3 



 mtc0 $4,$16 

TTYDBG("cache enable done\r\n") 

nop 

#endif 

接着执行初始化 一级 CROSS BAR 的操作。(主要是为了兼容之前的32 bit pmon和PCI

设备,实现64位地址向32地址的映射以便CPU可以用32位地址访问HT 空间/PCI空

间)。最后把flash 里面的bios可执行代码和数据拷贝到编译脚本ld.script指定的相应地

方。完成上述所有操作后就可以完全跳转到initmips去执行。