📄 main.c
字号:
/********************************************************************
' 创建日期: 2006/04/18
' 文件名称: header.h
' 文件作者: GENE.SHAO (E-Mail: sjiang1981@163.com)
'
' 文件功能: 初始化内存控制器,I/O端口,将linux kernel从FLASH读到内存中,
设置linux启动参数,然后跳转到内核的起始地址运行.
' 文件描述: 在使用该文件的时候请确保DOWNLOAD_ADDRESS,MEM_OFFSET,
LINUX_PARAM_OFFSET和你的linux内核中设置的值一致.
该程序在Samsung的s3c2410处理器上测试通过,你可以重新发
布或修改该程序,如果对该程序有何疑问请通过Email联系作者。
*********************************************************************/
#include "header.h"
///linux参数结构, 是从linux内核中取出来的
/* This is the old deprecated way to pass parameters to the kernel */
struct param_struct {
union {
struct {
unsigned long page_size; /* 0 */
unsigned long nr_pages; /* 4 */
unsigned long ramdisk_size; /* 8 */
unsigned long flags; /* 12 */
#define FLAG_READONLY 1
#define FLAG_RDLOAD 4
#define FLAG_RDPROMPT 8
unsigned long rootdev; /* 16 */
unsigned long video_num_cols; /* 20 */
unsigned long video_num_rows; /* 24 */
unsigned long video_x; /* 28 */
unsigned long video_y; /* 32 */
unsigned long memc_control_reg; /* 36 */
unsigned char sounddefault; /* 40 */
unsigned char adfsdrives; /* 41 */
unsigned char bytes_per_char_h; /* 42 */
unsigned char bytes_per_char_v; /* 43 */
unsigned long pages_in_bank[4]; /* 44 */
unsigned long pages_in_vram; /* 60 */
unsigned long initrd_start; /* 64 */
unsigned long initrd_size; /* 68 */
unsigned long rd_start; /* 72 */
unsigned long system_rev; /* 76 */
unsigned long system_serial_low; /* 80 */
unsigned long system_serial_high; /* 84 */
unsigned long mem_fclk_21285; /* 88 */
} s;
char unused[256];
} u1;
union {
char paths[8][128];
struct {
unsigned long magic;
char n[1024 - sizeof(unsigned long)];
} s;
} u2;
char commandline[1024];
};
//内存控制器初始值
static unsigned long memory_controller_inti_value[] =
{
0x22111120,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00000700,
0x00018005,
0x00018005,
0x008E0459,
0x000000B2,
0x00000030,
0x00000030,
};
void Delay(int time)
{
int i;
for(i=0;i<1000;i++);
}
//设置led灯, led1-led4, 0表示亮, 1表示灭
void set_led(int led1, int led2, int led3, int led4)
{
(*(volatile char *) GPFDAT) = (led1 << 4) | (led2 << 5) | (led3 << 6) | (led4 << 7);
return;
}
//等待NAND FLASH空闲
inline void wait_idle(void)
{
int i;
while(!(rNFSTAT & BUSY))
for(i=0; i<10; i++);
}
//初始化内存控制器
void init_memory_controller()
{
int i;
for (i=0; i<13; i++)
{
(*(volatile long *) (BWSCON + 4 * i)) = memory_controller_inti_value[i];
}
}
//读取NAND FLASH, 将start_addr处开始的size个字节读到buf中.
int read_nand_flash(unsigned char *buf, unsigned long start_addr, int size)
{
int i = 0, j;
//要读取的FLASH首地址必须是按块对其的,且读取的大小为块的整倍数
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
{
return -1;
}
//启用FLASH控制器
rNFCONF &= ~0x800;
for(i=0; i<10; i++);
for(i=start_addr; i < (start_addr + size);)
{
//发送读命令
rNFCMD = 0;
//发送要读取的地址
rNFADDR = i & 0xff;
rNFADDR = (i >> 9) & 0xff;
rNFADDR = (i >> 17) & 0xff;
rNFADDR = (i >> 25) & 0xff;
wait_idle();
//读取一个FLASH块
for(j=0; j < NAND_SECTOR_SIZE; j++, i++)
{
*buf = (rNFDATA & 0xff);
buf++;
}
}
// 禁用FLASH控制器
rNFCONF |= 0x800;
return 0;
}
//初始化UART
void init_uart(int pclk,int baud)
{
int i;
if(pclk == 0)
pclk = PCLK;
rUFCON0 = 0x0;
rUMCON0 = 0x0;
rULCON0 = 0x3;
rUCON0 = 0x245;
rUBRDIV0=( (int)(PCLK/16./115200) -1 );
for(i=0;i<100;i++);
}
//发送一个字节
void uart_send_byte(int data)
{
if(data=='\n')
{
while(!(rUTRSTAT0 & 0x2));
Delay(10); //because the slow response of hyper_terminal
WrUTXH0('\r');
}
while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty.
Delay(10);
WrUTXH0(data);
}
//一个字符串发送
void uart_send_string(char *pt)
{
while(*pt)
uart_send_byte(*pt++);
}
//重置NAND FLASH
void nand_flash_reset(void)
{
int i;
NF_nFCE_L();
NF_CMD(0xFF);
for(i=0;i<10;i++);
NF_WAITRB();
NF_nFCE_H();
}
//初始化NAND FLASH
void nand_flash_init(void)
{
rNFCONF = 0x0000F830;
nand_flash_reset();
}
//初始化I/O端口和外部中断
void port_init(void)
{
rGPACON = 0x7fffff;
rGPBUP = 0x7ff;
rGPCCON = 0xaaaaaaaa;
rGPCUP = 0xffff;
rGPDCON = 0xaaaaaaaa;
rGPDUP = 0xffff;
rGPECON = 0xaaaaaaaa;
rGPEUP = 0xffff;
rGPFCON = 0x55aa;
rGPFUP = 0xff;
rGPGCON = 0xff95ffba;
rGPGUP = 0xffff;
rGPHCON = 0x16faaa;
rGPHUP = 0x7ff;
rEXTINT0 = 0x22222222;
rEXTINT1 = 0x22222222;
rEXTINT2 = 0x22222222;
}
//调用linux内核, a0: 0; a1: 处理器类型号; a2: 内核首地址
//关于协处理器15的设置,可参考<<ARM体系结构与编程>>第五章"ARM存储系统".
void call_linux(long a0, long a1, long a2)
{
__asm__(
"mov r0, %0\n"
"mov r1, %1\n"
"mov r2, %2\n"
"mov ip, #0\n"
"mcr p15, 0, ip, c13, c0, 0\n" //将PID置为0, 禁用快速上下文切换技术
"mcr p15, 0, ip, c7, c7, 0\n" //使I,D caches无效
"mcr p15, 0, ip, c7, c10, 4\n" //清空写缓冲区
"mcr p15, 0, ip, c8, c7, 0\n" //使I,D快表(TLB)无效
"mrc p15, 0, ip, c1, c0, 0\n" //将寄存器c1中的内容读到ip(r12)中
"bic ip, ip, #0x0001\n" //清除0位, 禁用MMU
"mcr p15, 0, ip, c1, c0, 0\n" //将ip的内容写回c1
"mov pc, r2\n" //跳转到内核的起始地址
"nop\n"
"nop\n"
:
: "r" (a0), "r" (a1), "r" (a2)
);
}
//自己定义的三个函数, 因为这里不能使用库函数.
//如果使用汇编语言写,可能效率会高一点. 但是这里只用来拷贝linux启动参数,足够用了.
int strlen(char * str)
{
int i;
for (i=0; i<1024; i++) {
if (str[i] == 0) {
return i;
}
}
return 0;
}
void memcpy(char * dst, char * src, int len)
{
int i;
for (i=0; i<len; i++){
*dst++ = *src++;
}
}
void memset(char * dst, unsigned char data, int len)
{
int i;
for (i=0; i<len; i++) {
*dst++ = data;
}
}
//设置linux启动参数, 我的根文件系统为yaffs文件系统.
static void setup_linux_param(unsigned long param_base)
{
struct param_struct *params = (struct param_struct *)param_base;
char linux_cmd[] = "noinitrd root=/dev/mtdblock/2 init=/linuxrc console=ttyS0";
memset((char *)params, 0, sizeof(struct param_struct));
params->u1.s.page_size = LINUX_PAGE_SIZE;
params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);
if (linux_cmd == NULL) {
} else {
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
}
}
int main(int argc,char **argv)
{
volatile unsigned char *kernel_start_addr;
//打开第一个LED灯
set_led(0, 1, 1, 1);
init_memory_controller();
port_init();
//打开第二个LED灯
set_led(1, 0, 1, 1);
init_uart(PCLK, 115200);
//打开第三个LED灯
set_led(1, 1, 0, 1);
kernel_start_addr = (unsigned char *)(DOWNLOAD_ADDRESS+MEM_OFFSET);
nand_flash_init();
//打开第四个LED灯
set_led(1, 1, 1, 0);
//打印引导消息
uart_send_string("This bootloader is wrote by ShaoJiang.\n");
uart_send_string("If you have any question about the programme, please email to sjiang1981@163.com.\n");
uart_send_string("Copy linux kernel image from 0x30000 to 0x30008000....\n");
//拷贝linux内核
read_nand_flash(kernel_start_addr, 0x30000, 1024 * 1024);
uart_send_string("Begin to decompress linux kernel.\n");
//设置linux启动参数
setup_linux_param(DOWNLOAD_ADDRESS+LINUX_PARAM_OFFSET);
//跳转到linux的起始地址
call_linux(0, 193, DOWNLOAD_ADDRESS+MEM_OFFSET);
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -