http://blog.chinaunix.net/u2/66601/showart_1013934.html
uclinux的启动主要分为两个阶段:
第一部分bootloader启动阶段
第二部分linux 内核初始化和启动阶段
第一节:start_kernel
第二节:用户模式( user_mode )开始,start_kernel结束
第三节:加载linux内核完毕,转入cpu_idle进程
Specification of the machine
- CPU: S3C2410
- MEM: 64MB (bank 0)
- ROM: 64MB (Inte Starata Flash Memory)
- got UART ports
64MB RAM (DRAM)
0x3400 0000 +----------------------+
|VIVI RAM (size: 1M) |
0x33F0 0000 +----------------------+ VIVI_RAM_BASE
|HEAP AREA(size: 1M) |
0x33E0 0000 +----------------------+ HEAP_BASE
|MMU TABLE(size: 16k) |
0x33DF C000 +----------------------+ MMU_TABLE_BASE
| |
mtd partition table (size: 16k)| MTD_PART_SIZE -----\
0x33DF 8000 +----------------------+ |
vivi parameter table(size: 16k)| PARAMETER_TLB_SIZE | vivi pri data
0x33DF 4000 |----------------------+ |
linux command line (size: 16k) | LINUX_CMD_SIZE -----/
| |
0x33DF 0000 +----------------------+ VIVI_PRIV_RAM_BASE
| |
| free memory |
| |
0x3010 8000 +----------------------+
| kernel (size: 1MB) |
0x3000 8000 +----------------------+
| |
| free memory |
| |
0x3000 0100 +----------------------+
kernel param (size 256Byte) |
0x3000 0000 +----------------------+ DRAM_BASE
64MB ROM (NAND Flash)
* 0x0400 0000 +-----------------------------+
* | |
* | jffs2 (size: 61M) | user
* | |
* 0x0030 0000 +-----------------------------+
* | root (size: 2048k) |
* 0x0010 0000 +-----------------------------+
* | kernel (size: 832k) |
* 0x0003 0000 +-----------------------------+
* | param (size: 64k) |
* 0x0002 0000 +-----------------------------+
* | vivi (size: 128k) |
* 0x0000 0000 +-----------------------------+
[root@localhost root]# minicom
Welcome to minicom 2.00.0
OPTIONS:
History Buffer, F-key Macros, Search History Buffer, I18nCompiled on
Jan 25 2003, 00:15:18.Press CTRL-A Z for help on special keys
minicom信息
VIVI version 0.1.4 (root@BC) (gcc version 2.95.2 20000516 (release) [Rebel.com]5
Bootloader头信息,版本等,这个因不同的bootloader的设计而有所不同,由此你能看出bootloader的版本信息,有很多使用的是通用的bootloader,如u-boot,redboot等。
源代码: vivi/lib/version.c vivi/main.c
#define VIVI_RELEASE "0.1.4"
#define VIVI_COMPILE_BY "root"
#define VIVI_COMPILE_HOST "localhost.localdomain"
#define VIVI_COMPILER "gcc version 2.95.2 20000516 (release)[Rebel.com]"
#define UTS_VERSION "#0.1.4 六 6月 5 05:27:07 CST 2004"
const
char *vivi_banner = "VIVI version " VIVI_RELEASE " (" VIVI_COMPILE_BY
"@" VIVI_COMPILE_HOST ") (" VIVI_COMPILER ") " UTS_VERSION
"\r\n";putstr(vivi_banner);
MMU table base address = 0x33DFC000
Succeed memory mapping.
链接
内存映射(memory
map)就是指在整个4GB物理地址空间中有哪些地址范围被分配用来寻址系统的RAM单元。比如,在SA-1100CPU中,从0xC000,0000开
始的512M地址空间被用作系统的RAM地址空间,而在Samsung S3C44B0X
CPU中,从0x0c00,0000到0x1000,0000之间的64M地址空间被用作系统的RAM地址空间。虽然 CPU
通常预留出一大段足够的地址空间给系统
RAM,但是在搭建具体的嵌入式系统时却不一定会实现CPU预留的全部RAM地址空间。也就是说,具体的嵌入式系统往往只把CPU预留的全部RAM地址空
间中的一部分映射到RAM单元上,而让剩下的那部分预留RAM地址空间处于未使用(unused)状态。由于上述这个事实,因此Boot
Loader的stage2必须在它想干点什么之前——比如,将存储在flash上的内核映像读到RAM空间中——检测整个系统的内存映射情况,也即它必
须知道CPU预留的全部RAM地址空间中的哪些被真正映射到RAM地址单元,哪些是处于unused状态的。
VIVI中用CONFIG_BOOTUP_MEMTEST中的memtest完成基本的RAM检测,针对Samsung S3C2410 CPU,从0x3000,0000到0x3400,0000之间的64M地址空间被用作系统的RAM地址空间。源代码:vivi/arch/s3c2410/mmu.c #define MMU_TABLE_BASE (HEAP_BASE - MMU_TABLE_SIZE)
#define HEAP_BASE (VIVI_RAM_BASE - HEAP_SIZE)
#define VIVI_RAM_BASE (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
#define DRAM_BASE DRAM_BASE0
#define DRAM_BASE0 0x30000000 /* base address of dram bank 0 */
#define DRAM_SIZE SZ_64M
#define SZ_8M 0x00800000
#define VIVI_RAM_SIZE SZ_1M
#define HEAP_SIZE SZ_1M
#define SZ_1M 0x00100000
#define MMU_TABLE_SIZE SZ_16K
#define SZ_16K 0x00004000static
unsigned long *mmu_tlb_base = (unsigned long *) MMU_TABLE_BASE;
putstr_hex("MMU table base address = 0x", (unsigned long)mmu_tlb_base);
源代码:vivi/main.c
mem_map_init();
mmu_init();
putstr("Succeed memory mapping.\r\n");NAND device: Manufacture ID: 0xec, Chip ID: 0x76 (Samsung K9D1208V0M)
链接:
NAND Flash:1989年,东芝公司发明。是以块和页为单位来读写的,不能随机访问某个指定的点。因而相对来说读取速度较慢,而擦除和写入的速度则比较快。一般适用在大容量的多媒体应用中,如:CF,SM。
NOR Flash:Intel于1988年发明.随机读取的速度比较快,随机按字节写,写入和擦除速度很低。一般适合应用于数据/程序的存贮应用中,如:手机,机顶盒。NOR还可以片内执行(execute-in-place)XIP。
简单的说,NAND类似于硬盘,NOR类似于内存
源代码:vivi/drivers/mtd/nand/smc_core.cstruct nand_flash_dev {
char * name;
int manufacture_id;
int model_id;
int chipshift;
char page256;
char pageadrlen;
unsigned long erasesize;
};
static struct nand_flash_dev nand_flash_ids[] = {
{"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000}, // 2Mb 5V
{"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000}, // 2Mb 3.3V
{"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000}, // 4Mb 5V
{"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000}, // 4Mb 3.3V
{"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000}, // 8Mb 3.3V
{"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000}, // 16Mb
{"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000}, // 32Mb
{"Toshiba TH58512FT", NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000}, // 64Mb
{"Toshiba TH58NS100/DC", NAND_MFR_TOSHIBA, 0x79, 27, 0, 3, 0x4000}, // 128Mb
{"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000}, // 2Mb 5V
{"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000}, // 2Mb 3.3V
{"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000}, // 4Mb 5V
{"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000}, // 4Mb 3.3V
{"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000}, // 4Mb 3.3V
{"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000}, // 8Mb 3.3V
{"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000}, // 16Mb
{"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000}, // 32Mb
{"Samsung K9D1208V0M", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000}, // 64Mb
{"Samsung K9D1G08V0M", NAND_MFR_SAMSUNG, 0x79, 27, 0, 3, 0x4000}, // 128Mb
{NULL,}
};
mtd->name = nand_flash_ids[i].name;
printk("NAND device: Manufacture ID:" \
" 0x%02x, Chip ID: 0x%02x (%s)\n",
nand_maf_id, nand_dev_id, mtd->name);
Could not found stored vivi parameters. Use default vivi parameters.
vivi参数
vivi_parameter_t default_vivi_parameters[] = {
{ "mach_type", MACH_TYPE, NULL },
{ "media_type", MT_S3C2410, NULL },
{ "boot_mem_base", 0x30000000, NULL },
{ "baudrate", UART_BAUD_RATE, NULL },
{ "xmodem_one_nak", 0, NULL },
{ "xmodem_initial_timeout", 300000, NULL },
{ "xmodem_timeout", 1000000, NULL },
{ "ymodem_initial_timeout", 1500000, NULL },
{ "boot_delay", 0x1000000, NULL }
};
#define MACH_TYPE 193
#define MT_S3C2410 MT_SMC_S3C2410
#define UART_BAUD_RATE 115200
源代码:vivi/lib/priv_data.c
int init_priv_data(void)
{
int ret_def;
#ifdef CONFIG_PARSE_PRIV_DATA //#define CONFIG_PARSE_PRIV_DATA 1
int ret_saved;
#endif
ret_def = get_default_priv_data();
#ifdef CONFIG_PARSE_PRIV_DATA
ret_saved = load_saved_priv_data();
if (ret_def && ret_saved) {
printk("Could not found vivi parameters.\n");
return -1;
} else if (ret_saved && !ret_def) {
printk("Could not found stored vivi parameters.");
printk(" Use default vivi parameters.\n");
} else {
printk("Found saved vivi parameters.\n");
}
#else
if (ret_def) {
printk("Could not found vivi parameters\n");
return -1;
} else {
printk("Found default vivi parameters\n");
}
#endif
#ifdef CONFIG_DEBUG_VIVI_PRIV
display_param_tlb();
display_mtd_partition();
#endif
return 0;Press Return to start the LINUX now, any other key for vivi按回车进入linux,其它键进vivi。当然,从vivi中boot也可以。源代码:vivi/main.cvoid boot_or_vivi(void)
{
char c;
int ret;
ulong boot_delay;
boot_delay = get_param_value("boot_delay", &ret);
if (ret) boot_delay = DEFAULT_BOOT_DELAY;
/* If a value of boot_delay is zero,
* unconditionally call vivi shell */
if (boot_delay == 0) vivi_shell();
/*
* wait for a keystroke (or a button press if you want.)
*/
printk("Press Return to start the LINUX now, any other key for vivi\n");
c = awaitkey(boot_delay, NULL);
if (((c != '\r') && (c != '\n') && (c != '\0'))) {
printk("type \"help\" for help.\n");
vivi_shell();
} // 有问题,'\r' '\n'都是回车,'\0'是什么?
run_autoboot();
return;
}
【注:这里还有个小问题。就是在这里添加的时候,\n\r要合起来用,不能只用\n。原因如下:
计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。
这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。
后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。Unix 系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“<换行><回车>”,即“\ n\r”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
这几个地方我都遇到过,不过一直没有搞清楚。现在才算是找到根源了。】
Copy linux kernel from 0x00030000 to 0x30008000, size = 0x00100000 ... done 启
动linux kernel,kernel映像必须被放到MTD设备的一个分区中,from、size分别表示linux
kernel起始地址和kernel的大小。为什么要指定kernel大小呢?因为kernel首先要被copy到boot_mem_base +
0x8000的地方,然后在boot_mem_base + 0x100开始的地方设置内核启动参数。要拷贝
kernel,当然需要知道kernel的大小啦,这个大小不一定非要和kernel实际大小一样,但是必须大于等于kernel的大小。(单位字节)
media_type是指定的媒介类型,因为boot命令对不同媒介的处理方式是不同的,例如如果kernel在 SDRAM中,那么boot执行的过程中就可以跳过拷贝kernel映像到SDRAM中这一步骤了
Boot命令识别的媒介类型有以下三种:
ram 表示从RAM中启动linux kernel,linux kernel必须要放在RAM中,我的S3C2410平台就是如此。
nor 表示从NOR Flash中启动linux kernel,linux kernel必须已经被烧写到了NOR Flash中
smc 表示从NAND Flash中启动linux kernel,linux kernel必须已经被烧写到了NAND Flash中 源代码:vivi/lib/boot-kernel.c
int boot_kernel(ulong from, size_t size, int media_type)
{
int ret;
ulong boot_mem_base; /* base address of bootable memory */
ulong to;
boot_mem_base = get_param_value("boot_mem_base", &ret);
if (ret) {
printk("Can't get base address of bootable memory\n");
printk("Get default DRAM address. (0x%08lx\n", DRAM_BASE);
boot_mem_base = DRAM_BASE;
} //boot_mem_base = 0x30000000
/* copy kerne image */
to = boot_mem_base + LINUX_KERNEL_OFFSET;
// #define LINUX_KERNEL_OFFSET 0x8000
// to = 0x30008000
printk("Copy linux kernel from 0x%08lx to 0x%08lx, size = 0x%08lx ... ",from, to, size);
ret = copy_kernel_img(to, (char *)from, size, media_type);
// 将内核镜像从NAND FLASH拷入RAM
// media_type=MT_SMC_S3C2410 if (ret) {
printk("failed\n");
return -1;
} else {
printk("done\n");
}
return 0;
}
void command_boot(int argc, const char **argv)
{
int media_type = 0;
ulong from = 0;
size_t size = 0;
mtd_partition_t *kernel_part;
int ret;
media_type = get_param_value("media_type", &ret);
if (ret) {
printk("Can't get default 'media_type'\n");
return;
}
kernel_part = get_mtd_partition("kernel");
if (kernel_part == NULL) {
printk("Can't find default 'kernel' partition\n");
return;
}
from = kernel_part->offset;
size = kernel_part->size;
boot_kernel(from, size, media_type);
}
user_command_t boot_cmd = {
"boot",
command_boot,
NULL,
"boot [{cmds}] \t\t\t-- Booting linux kernel"
};
zImage magic = 0x016f2818
判断内核文件是否为压缩镜像,而当前文件是压缩镜像
源文件: vivi/lib/boot-kernel.c
#define LINUX_ZIMAGE_MAGIC 0x016f2818
if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) {
printk("Warning: this binary is not compressed linux kernel image\n");
printk("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
} else {
printk("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
}
Setup linux parameters at 0x30000100
linux command line is: "noinitrd root=/dev/bon/3 init=/linuxrc console=ttyS0"
设置linux参数和控制行
static void setup_linux_param(ulong param_base)
{
/*linux parameters*/ struct param_struct *params = (struct param_struct *)param_base;
char *linux_cmd;
printk("Setup linux parameters at 0x%08lx\n", param_base);
memset(params, 0, sizeof(struct param_struct));
params->u1.s.page_size = LINUX_PAGE_SIZE;
params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);
/* set linux command line */
linux_cmd = get_linux_cmd_line();//linux_cmd = 0x33DF8008
if (linux_cmd == NULL) {
printk("Wrong magic: could not found linux command line\n");
} else {
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
printk("linux command line is: \"%s\"\n", linux_cmd);
}
}
//#define boot_mem_base 0x30000000
//#define LINUX_PARAM_OFFSET 0x100setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);
MACH_TYPE = 193
/* Get machine type */
mach_type = get_param_value("mach_type", &ret);
printk("MACH_TYPE = %d\n", mach_type);
NOW, Booting Linux......
激动人心的消息,终于可以引导linux了。。。
源代码: vivi/lib/boot-kernel.c
printk("NOW, Booting Linux......\n");
call_linux(0, mach_type, to);
#elif defined(CONFIG_ARCH_S3C2410)
void call_linux(long a0, long a1, long a2)
{
cache_clean_invalidate();
tlb_invalidate();
__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" /* zero PID */
"mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
"mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
"mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
"mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
"bic ip, ip, #0x0001\n" /* disable MMU */
"mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
"mov pc, r2\n"
"nop\n"
"nop\n"
: /* no outpus */
: "r" (a0), "r" (a1), "r" (a2)
);
//全是汇编,有空再研究
Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000 地址处。在跳转时,下列条件要满足:
1. CPU 寄存器的设置:
R0=0;
R1=机器类型 ID;关于 Machine Type Number,可以参见 linux/arch/arm/tools/mach-types。
R2=启动参数标记列表在 RAM 中起始基地址;
2. CPU 模式:
必须禁止中断(IRQs和FIQs);
CPU 必须 SVC 模式;
3. Cache 和 MMU 的设置:
MMU 必须关闭;
指令 Cache 可以打开也可以关闭;
数据 Cache 必须关闭;
阅读(1599) | 评论(0) | 转发(0) |