星期六, 十一月 24, 2007

S3C2410启动信息注释(vivi部分)『转』

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等。

链接 Boot Loader  VIVI

源代码: 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.


链接 MMU

    内存映射(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.c


struct 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.c

void 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 0x100
setup_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 必须关闭;

0 意見: