操作系统

FindyourRootFileSystemwithMTD
作者 rwen2012 2006年05月11日 07:53

    Find your Root File System with MTD

    

    

    

    

    

    

    

    

    

    

    

    

    

    

     Once a brave soul finally gets the kernel to compile for their system.

    Their high hopes for immediate gratification and

    Instant Unix are normally dashed by the little message...

    

    You have done well, the kernel boots, but nothing else will work.

    The Question is now what to do next.

    

    

    

    

    

    

    

    

     -->

    

    

    

    

    

     This session is dedicated to the latest set of solutions to this problem.

    How does the Kernel mount its Root ?

     This is the message your want to see

     VFS: Mounted root (romfs filesystem) readonly.

    

     This is where it came from...

    linux/init/do_mounts.c: printk("VFS: Mounted root (%s filesystem)%s.\n",

     But how did the kernel get there and what needs to be set up

    to make it all happen.

    Here is the code that tries to mount root.

    linux/init/do_mounts.c: mount_block_root(char *name, int flags)

     this looks through all the fs_names ands tries to mount one of them

     name is an input root name /dev/root

     fs_names is a list of possible file systems

     either from the "rootfstype=" cmdline option of from the

     list obtained from get_filesystem_list

     root_mount_data is derived from the "rootflags=" cmdline option

     This is used to specify a Read Only root mount

    

     for (p = fs_names; *p; p += strlen(p)+1) {

     int err = sys_mount(name, "/root", p, flags, root_mount_data);

     ....

     The sys_mount call can fail in a number of ways

     if err == 0 we are done root have been mounted

     if err == -EACCES we try a read only option

     if err == -EINVAL we give up with this one.

     if we have gone through all the possible file systems and could not

     mount one then we give the Kernel Panic message.

    Command Line root= Option

    The other main command line option is

     root=/dev/mtd0

    or

     root=1f:00.

    This will override the natural search for mounted file systems and

    go directly to the chosen device.

    What does all this mean ?

    During the system init a number of file systems were "discovered".

    These were typically systems compiled into the kernel that were initialized

    during the boot process.

     As a test, add some debug code code to mount_block_root

    in linux/init/do_mounts.c to see some of

    what is happening here.

    static void __init mount_block_root(char *name, int flags)

    {

     char *fs_names = __getname();

     char *p;

    

     get_fs_names(fs_names);

    // New debug code here

     printk("*******\nVFS: test name = \n",name);

     for (p = fs_names; *p; p += strlen(p)+1) {

     printk("VFS: fs_name = \n",p);

     }

     printk("VFS: root name \n*******\n",kdevname(ROOT_DEV));

    //End of new debug code

    retry:

     for (p = fs_names; *p; p += strlen(p)+1) {

     int err = sys_mount(name, "/root", p, flags, root_mount_data);

    // More debug code here ( just a single line )

     printk("VFS: tried fs_name = err= %d\n",p,err);

     switch (err) {

    Here are the results on one of my target systems.

    *******

    VFS: test name =

    VFS: fs_name =

    VFS: fs_name =

    VFS: root name

    *******

    mtdblock_open

    ok

    mtdblock_release

    ok

    VFS: tried fs_name = err= -22

    mtdblock_open

    ok

    VFS: tried fs_name = err= 0

     This means that :

    

    What is /dev/root ?

    Good question ...

    You can also ask what is ROOT_DEV.

    On the particular target system used for this test ( an ArmTwister )

    there was no /dev/root specified. It would not matter

    even if there was a /dev/root

     on the file system since that file system is not yet mounted.

    There is a command line option to set up ROOT_DEV but this was not used in

    this case.

    ROOT_DEV did have a value, however, so where was this set up.

    To answer this you have to look at the kernel init sequence and

    discover where romfs for example was set up.

     This used to be done in

    linux/drivers/block/blkmem.c but funnily enough

    this file was NOT included in this kernel build.

    The new MTD flash device drivers were used exclusively.

    

    I am writing this article because I wanted to trace how this

    new MTD root system worked.

    Even stranger, the root file system is working from a RAM area not a

    Flash Device. I thought that the MTD driver only worked on Flash Devices !!

    Lets look at ROOT_DEV first.

     this search through the kernel gave me some clues

     find linux/ -name "*.c" | xargs grep "ROOT_DEV" | more

    Here are some search results

    linux/arch/armnommu/kernel/arch.c: ROOT_DEV=MKDEV(RAMDISK_MAJOR,0);

    linux/arch/armnommu/kernel/setup.c: ROOT_DEV=to_kdev_t(tag->u.core.rootdev);

    linux/arch/armnommu/kernel/setup.c: ROOT_DEV=to_kdev_t(params-u1.s.rootdev);

    linux/arch/armnommu/kernel/setup.c: ROOT_DEV=MKDEV(0, 255);

    linux/drivers/mtd/maps/uclinux.c: //ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0);

    linux/drivers/mtd/maps/uclinux.c: ROOT_DEV=MKDEV(MTD_BLOCK_MAJOR, mtd->index);

    linux/fs/nfs/nfsroot.c: ROOT_DEV=MKDEV(UNNAMED_MAJOR, 255);

    linux/init/do_mounts.c: create_dev("/dev/root", ROOT_DEV, root_device_name);

    ROOT_DEV is manipulated in various places as the system tries to default

    its choice of root file system.

     I'll pick out the most interesting ones.

    linux/drivers/mtd/maps/uclinux.c: ROOT_DEV=MKDEV(MTD_BLOCK_MAJOR, mtd->index);

    linux/init/do_mounts.c: create_dev("/dev/root", ROOT_DEV, root_device_name);

    This means that linux/drivers/mtd/maps/uclinux.c set

    ROOT_DEV up when it was setting up its partitions.

    The missing /dev/root is explained in

    linux/init/do_mounts.c, it was simply created for us.

    A QUICK TIMEOUT

    When setting you the MTD device you select a whole bundle of options in

    make menuconfig. All this will change as you change these

    options. You can, however, still use the techniques I mention here to

    find out how your system works.

    Here are the config options chosen for this system.

    # this is the command used to find the important options

     grep MTD linux/.config | grep =y

    # these are the results. //with brief comments ? = I think

    CONFIG_MTD=y //use the MTD file system

    CONFIG_MTD_DEBUG=y // debug

    CONFIG_MTD_PARTITIONS=y // use partitions ?

    CONFIG_MTD_CMDLINE_PARTS=y // read partitions from command line

    CONFIG_MTD_CHAR=y // allow char device access

    CONFIG_MTD_BLOCK_RO=y // allow ro block access

    CONFIG_MTD_CFI=y // Common (huh?) Flash Interface

    CONFIG_MTD_JEDECPROBE=y // use JEDEC Probe

    CONFIG_MTD_GEN_PROBE=y // use GEN probe

    CONFIG_MTD_CFI_AMDSTD=y // look for AMD devices

    CONFIG_MTD_RAM=y // allow MTD_RAM ..... hmm

    CONFIG_MTD_PHYSMAP=y // include physmap for flash

    CONFIG_MTD_UCLINUX=y // include the uclinux.c ram mapping file

    Knowing the above information allowed the search for important files

    to be narrowed down quickly.

    MTD Partitions

    So now take a closer look at

    

    linux/drivers/mtd/maps/uclinux.c: .

    This is one of the files where the partitions are set up for the MTD file

    system.

    (Note the other file is physmap.c but more on that later )

    Let's step through this code.

    int __init uclinux_mtd_init(void)

    {

     struct mtd_info *mtd;

     struct map_info *mapp;

     //extern char _ebss; // the system used to use _ebss to

     // find the rom start

     // The armtwister changed this to

     // use flashstart defined in the link map

     extern char _flashstart;

     mapp = &uclinux_ram_map; // set up the partition map.

     In the next section we are setting up the map_priv_2 structures

     with the start address and size of the map.

    

     Note that the 3rd word in the romFS image is the size.

     //mapp->map_priv_2 = (unsigned long) &_ebss;

     //mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8)));

     mapp->map_priv_2 = (unsigned long) &_flashstart;

     mapp->size = PAGE_ALIGN(*((unsigned long *)((&_flashstart) + 8)));

     mapp->buswidth = 4;

     printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",

     (int) mapp->map_priv_2, (int) mapp->size);

     // now remap map_priv_1 as the address space.

     mapp->map_priv_1 = (unsigned long)

     ioremap_nocache(mapp->map_priv_2, mapp->size);

     if (mapp->map_priv_1 == 0) {

     printk("uclinux[mtd]: ioremap_nocache() failed\n");

     return(-EIO);

     }

     The location and size of the romfs has been found.

    Now probe the map and get an mtd structure. In this case the probe is non

    destructive.

     // normally probes for the device

     // then sets up the mtd structure

     // the probe is destructive so it should

     // be modified.

     mtd = do_map_probe("map_ram", mapp);

     if (!mtd) {

     printk("uclinux[mtd]: failed to find a mapping?\n");

     iounmap((void *) mapp->map_priv_1);

     return(-ENXIO);

     }

    

     Set up more of the structure.

     mtd->module = THIS_MODULE;

     mtd->point = uclinux_point;

     mtd->priv = mapp;

     uclinux_ram_mtdinfo = mtd;

     The kernel command line now sets up the partitions.

     // this is now done on the kernel command line

     //xx add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS);

     add_mtd_device(mtd);

     //printk("uclinux[mtd]: set %s to be root filesystem\n",

     // uclinux_romfs[0].name);

     //ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0);

     // use this mtd device for the root_dev

     printk("uclinux[mtd]: set %s to be root filesystem index = %d\n",

     uclinux_romfs[0].name,mtd->index);

     ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);

     put_mtd_device(mtd);

     return(0);

    }

     This is neat the uclinux.c map creates a ram map and finds the addresses

    and then creates out ROOT_DEV for us.

    This is "Fine" for ArmTwister Now How Do I use it ?

    Good question. You have two options here.

    

    Using _ebss

    The setting up of _ebss can be quite difficult.

     You have to modify the linker script to locate the label.

    Here is an example

    # extract from file

    # linux/arch/armnommu/vmlinux-armv.lds.in

    # Note _ebss is commented out here.

     .bss : {

     __bss_start = .; /* BSS */

     *(.bss)

     *(COMMON)

     _end = . ;

     /* _ebss = . ; */ /* added PSW */

     }

    

    Using partition in the command line

    Note This may be an "ArmTwister only" modification.

    The file

    linux/drivers/mtd/maps/physmap.c has been adapted to accept

    a command line mapping specification.

    The command line could look like

     mtdparts=physmap:256k(ARMboot)ro,-(testpart)

    //file linux/drivers/mtd/maps/physmap.c

    //function init_physmap(void)

    #ifdef CONFIG_MTD_CMDLINE_PARTS

     if (parsed_nr_parts == 0) {

     int ret = parse_cmdline_partitions(mymtd,

     &parsed_parts, "physmap");

     if (ret > 0) {

     part_type = "Command Line";

     parsed_nr_parts = ret;

     }

     }

    #endif

    This will take the command line shown and create two partitions.

    dev: size erasesize name

    mtd0: 00040000 00010000 "ARMboot"

    mtd1: 001c0000 00010000 "testpart"

    If you want to specify a partition as a root fs partition, under this setup,

    you may choose to create a different layout.

     mtdparts=physmap:256k(ARMboot)ro,768k(Image),-(Rom fs)ro

    The booted partitions now look like

    dev: size erasesize name

    mtd0: 00040000 00010000 "ARMboot"

    mtd1: 000c0000 00010000 "Image"

    mtd2: 00100000 00010000 "Romfs"

    Adding a final statement to the command line

     mtdparts=physmap:256k(ARMboot)ro,768k(Image),-(Rom fs)ro root=1f:02

    Will force the system to use the Romfs flash partition.

    Specifying an absolute location

    You may want to do this in a debug situation where you want to

    update the kernel and the romfs separately.

    A quick and dirty way of doing this would be to add a

    rootaddr= option to the command line and add this to

    do_mounts.c

    This serves as a mini example of how to add a kernel command line option.

    // declare the root addr kernel cmdline option

    unsigned long root_addr= 0;

    static int __init root_add_setup(char *line)

    {

     root_addr = simple_strtol(line,NULL,0);

     printk(" set up root_addr as 0x%x\n",root_addr);

     return 1;

    

    }

    __setup("rootaddr=", root_add_setup); //psw

    Then test for root_addr to be non-zero in uclinux.c

    // near the top of the file

    extern long root_addr;

    In the mtd_init section...

    // in the function uclinux_mtd_init(void)

     int use_root_addr = 0;

     if ( root_addr != 0 ) {

     if (strncmp((char *) root_addr, "-rom1fs-", 8) == 0) {

     printk(" RA found a possible rootfs at 0x%x\n",root_addr);

     use_root_addr = 1

     } else {

     printk(" RA no rootfs at 0x%x\n",root_addr);

     }

     }

     if (strncmp((char *) &_flashstart, "-rom1fs-", 8) == 0) {

     printk(" FS found a possible rootfs at 0x%x\n",&_flashstart);

     } else {

     printk(" FS no rootfs at 0x%x\n",&_flashstart);

     }

     Having done all this it would be safe to switch to the rootfs

    specified in the command line. You would use the value in

    root_addr rather than the value in

    _flashstart in the map_priv_2

     if ( use_root_addr == 1 ) {

     mapp->map_priv_2 = (unsigned long) root_addr;

     mapp->size = PAGE_ALIGN(*((unsigned long *)((root_addr) + 8)));

     } else {

     mapp->map_priv_2 = (unsigned long) &_flashstart;

     mapp->size = PAGE_ALIGN(*((unsigned long *)((&_flashstart) + 8)));

     }

    What is an ArmTwister ?

    Check out

    ArmTwister.com

    What about cramfs

    Cramfs is an excellent way to get root file system compression.

    it occupies about 50% of the space of a normal romfs.

    It will only use memory on demand as each file is used.

    The compressed pages are kept in Flash until the kernel tries to access

    the page. Only at that time is the code extracted into Memory on a page by

    page basis.

    I only did a quick test on CramFs and if failed to run on my Kernel. It is probably something stupid I have done.

    I'll be looking at that in early Feb 2003.

    Have fun.

     Phil Wilshire SDCS System Design & Consulting Services

    

    [email=philwil@sysdcs.com]philwil@sysdcs.com[/email]

    Version 0.9: Jan 10 , 2003

    Author: Phil Wilshire: System Design & Consulting Services LLC

    Email: philwil@sysdcs.com

    Revisions: To be added

    

    

    

    

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/11445/showart_111266.html

打开APP阅读全文

FindyourRootFileSystemwithMTD

FindyourRootFileSystemwithMTD

长按识别二维码 进入IT168查看全文

请长按保存图片
{{data.thematic.text}}

相关文章

加载中...

分享到

请使用浏览器的分享功能
分享到微信等