Chinaunix首页 | 论坛 | 博客
  • 博客访问: 868401
  • 博文数量: 190
  • 博客积分: 7021
  • 博客等级: 少将
  • 技术积分: 1752
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 19:26
文章分类

全部博文(190)

文章存档

2014年(9)

2011年(32)

2010年(149)

我的朋友

分类: LINUX

2010-05-22 17:09:47

在看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) |
0

上一篇:ascii表和转义字符

下一篇:boot_or_vivi(3)

给主人留下些什么吧!~~