在看run_autoboot()的时候,涉及到一个重要的内容,就是bootloader很重要的一个功能(向内核传递参数),下面我们具体来分析。
run_autoboot()---->exec_string("boot")---->后面就是一些解些和执行函数,在上面一章已经说明
现在就来看看具体的boot的函数:
user_command_t boot_cmd = {
"boot",
command_boot,
NULL,
"boot [{cmds}] \t\t\t-- Booting linux kernel"
}; //首先定义了一个命令结构体,包含了boot的各类信息
|
再来看看command_boot函数,这个函数是命令行执行boot时所真正调用的函数。
(vivi/lib/boot_kernel.c)
/*
* default values:
* kernel mtd partition = "kernel"
* base adress of bootable memory = DRAM_BASE
* media type =
*
* avalable commands
*
* boot
* boot
* boot
* boot
* boot help
*
* Anyway, I need three values. this:
* media type, address of kernel image, size of kernel image,
*/
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;
switch (argc) {
case 1:
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;
break;
case 2:
if (strncmp("help", argv[1], 4) == 0) {
display_help();
return;
}
media_type = media_type_is(argv[1]);
kernel_part = get_mtd_partition("kernel");
from = kernel_part->offset;
size = kernel_part->size;
break;
case 3:
media_type = media_type_is(argv[1]);
kernel_part = get_mtd_partition(argv[2]);
from = kernel_part->offset;
size = kernel_part->size;
break;
case 4:
media_type = media_type_is(argv[1]);
from = strtoul(argv[2], NULL, 0, NULL);
size = strtoul(argv[3], NULL, 0, NULL);
break;
default:
display_help();
break;
}
boot_kernel(from, size, media_type);
}
|
首先要获取介质的类型,在只有一个参数的情况下,查找param_tlb中的media_type中的值。或者已经指定了启动介质的时候,则是需要判定media_type_is(arg[1]);
其次就是获取kernel在所储存介质的起始地址和大小。定义在mtd_partition_t数据结构中
然后就是调用boot_kernel(from, size, media_type)函数
(vivi/lib/boot_kernel.c)
这个函数的作用从前面的函数说明也可以看出,就是将储存在外不介质中的内核转移到SDRAM中
内核在SDRAM的起始地址是:0x30000000+LINUX_KERNEL_OFFSET(0x30008000)
/*
* boot_kernel: booting the linux kernel
*
* from: address of stored kernel image
* size: size of kernel image
* media_type: a type of stoage device
*/
int boot_kernel(ulong from, size_t size, int media_type)
{
int ret;
ulong boot_mem_base; /* base address of bootable memory ¸Â3a? */
ulong to;
ulong mach_type;
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;
}
/* copy kerne image */
to = boot_mem_base + LINUX_KERNEL_OFFSET;
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);
if (ret) {
printk("failed\n");
return -1;
} else {
printk("done\n");
}
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 and linux command line */
setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);
/* Get machine type */
mach_type = get_param_value("mach_type", &ret);
printk("MACH_TYPE = %d\n", mach_type);
/* Go Go Go */
printk("NOW, Booting Linux......\n");
call_linux(0, mach_type, to);
return 0;
}
|
在上面这个函数里面封装了一个子函数,copy_kernel_ima(ulong dst, const char *src, size_t size, int mt)。这个函数才是真正实现内核搬运的函数。首先判断外部介质的类型,然后从外部介质将内核盘运到sdram的从0x30008000开始的0x00200000大小的空间,看具体函数
/*
* dst: destination address
* src: source
* size: size to copy
* mt: type of storage device
*/
static inline int copy_kernel_img(ulong dst, const char *src, size_t size, int mt)
{
int ret = 0;
switch (mt) {
case MT_RAM:
/* noting to do */
break;
case MT_NOR_FLASH:
memcpy((char *)dst, (src + FLASH_UNCACHED_BASE), size);
break;
case MT_SMC_S3C2410:
#if defined(CONFIG_S3C2410_NAND_BOOT) || defined(CONFIG_S3C2440_NAND_BOOT)
ret = nand_read_ll((unsigned char *)dst,
(unsigned long)src, (int)size);
#endif
break;
case MT_UNKNOWN:
default:
printk("Undefined media type.\n");
return -1;
}
return ret;
}
|
完了内核的转移之后则是需要验证zImage的魔数。
zImage的魔数存放在zImage相对的偏移位置36的地方
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));
}
|
#define LINUX_ZIMAGE_MAGIC 0x016f2818
|
继续往下看:
/* Setup linux parameters and linux command line */
setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);
|
进入函数内部:看到这个函数的时候遇到问题了,在vivi中没有struct param_struct的定义,然后看了CalmArrow的blog我才知道,原来这个和函数定义在linux中,在2.4内核的时候定义在(include/arm/setup.h)中,
/*
* pram_base: base address of linux paramter
*/
static void setup_linux_param(ulong param_base)
{
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);
#if 0
params->u1.s.page_size = LINUX_PAGE_SIZE;
params->u1.s.nr_pages = (dram_size >> LINUX_PAGE_SHIFT);
params->u1.s.ramdisk_size = 0;
params->u1.s.rootdev = rootdev;
params->u1.s.flags = 0;
/* TODO */
/* If use ramdisk */
/*
params->u1.s.initrd_start = ?;
params->u1.s.initrd_size = ?;
params->u1.s.rd_start = ?;
*/
#endif
/* set linux command line */
linux_cmd = get_linux_cmd_line();
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);
}
}
|
/*
* Usage:
* - do not go blindly adding fields, add them at the end
* - when adding fields, don't rely on the address until
* a patch from me has been released
* - unused fields should be zero (for future expansion)
* - this structure is relatively short-lived - only
* guaranteed to contain useful data in setup_arch()
*/
#define COMMAND_LINE_SIZE 1024
/* 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[COMMAND_LINE_SIZE];
};
|
一共就设置了3个参数:
1: params->u1.s.page_size = LINUX_PAGE_SIZE;
2:params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);
3:linux_cmd = get_linux_cmd_line();
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
设置页的大小,以及SDRAM中的页数,(一共有64M,每页4K)
下面就是找处理器类型:
mach_type = get_param_value("mach_type", &ret);
最后,设置各种状态,然后跳转到KERNEL中执行内核。vivi的任务也就已经完成了!!!
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)
);
}
|
阅读(952) | 评论(0) | 转发(0) |