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...
  • Kernel panic: VFS: Unable to mount root fs on 1f:00

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 = <%s> /n",name);
	for (p = fs_names; *p; p += strlen(p)+1) {
	  printk("VFS: fs_name = <%s> /n",p);
	}
        printk("VFS: root name <%s> /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 = <%s> err= %d/n",p,err);

		switch (err) {

 

Here are the results on one of my target systems.

 

******* 
VFS: test name = </dev/root>
VFS: fs_name = <ext2> 
VFS: fs_name = <romfs>
VFS: root name <1f:02>
*******
mtdblock_open
ok
mtdblock_release
ok
VFS: tried fs_name = <ext2> err= -22  
mtdblock_open 
ok
VFS: tried fs_name = <romfs> err= 0 

This means that :

  • the kernel had two file systems in the kernel.
  • /dev/root is the chosen root device (more later).
  • mounting /dev/root failed as an ext2 filesystem
  • mounting /dev/root passed as an romfs filesystem

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.

  • Use _ebss to find the start of the romfs.
  • Use an absolute flash partition specified in the command line.
  • Use an absolute memory location specified in the command line.

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 philwil@sysdcs.com


Version 0.9:  Jan 10 , 2003
Author:       Phil Wilshire: System Design & Consulting Services LLC
Email:        philwil@sysdcs.com
Revisions:    To be added

STATUS nvrammanager_UpgradePartitions(char* upfilePath) { int file_head_len; int fd; size_t fsize; int fileLen; char* upfileBuff; size_t curoffset NM_DEBUG("upfilePath = %s\n", upfilePath); fd = open(upfilePath, O_RDWR); if (fd < 0 ) { NM_ERROR("open upfile failed!\n"); return ERROR; } fsize = nvrammanager_getFileSize(upfilePath); fileLen = 0; upfileBuff = (char *)mmap(NULL, fsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); memcpy(&fileLen, upfileBuff, sizeof(int)); fileLen = ntohl(fileLen); file_head_len = calFSOffset(upfileBuff, fileLen, upfilePath); FILE* fp = fopen(upfilePath, "rb"); if (!fp) { perror("can't open upgrade file"); return -1; } curoffset = file_head_len + IMAGE_SIZE_BASE; fseek(fp, curoffset, SEEK_CUR); while (!feof(fp)) { char *pBuf = NULL; unsigned char fileMd5Check[16]; unsigned char *pMd5Key = (unsigned char *)malloc(16 * sizeof(unsigned char)); size_t data_size = 0; int idx0 = -1; int idx = -1; char cmd[256]; int age0 = -1; int age1 = -1; int primaryValueRun = -1; // int primaryValueUp = -1; char bootpathRun[256]; char bootpathUp[256]; char mtd_device[32]; char mtd_name[32]; // size_t pagesize = -1; char tmppath[256]; // get MTD header PARTITIONS_UP_HEAD header; if (fread(&header, sizeof(header), 1, fp) != 1) { if (feof(fp)) break; perror("get header fail"); fclose(fp); return -2; } printf("name:%s;\n size:%u;\n type:%s;\n UPmethod:%d;\n",header.name,header.size,header.type,header.UPmethod); curoffset += sizeof(PARTITIONS_UP_HEAD); // get partition name and size snprintf(mtd_device, sizeof(mtd_device), "%s", header.name); // printf("mtd_name:%s\n", mtd_device); data_size = header.size; // printf("Size: %zu\n", data_size); // MD5 check pBuf = (char *)malloc(header.size + 16); memcpy(fileMd5Check, header.md5, 16); for (int i = 0; i < 16; i++) { if(i == 15){printf("%0x- \n", fileMd5Check[i]);} // print by 0x else{printf("%0x- ", fileMd5Check[i]);} } memcpy(pMd5Key, md5ImageKey, 16); // fseek(fp, -16L, SEEK_CUR); memcpy(pBuf, pMd5Key, 16); fread(pBuf + 16, 1, header.size, fp); if (!md5_verify_digest(fileMd5Check, pBuf, 16 + header.size)) { fprintf(stderr, "md5 verify error!\n"); /* restore checksum */ memcpy(pBuf, fileMd5Check, 16); return -100; } free(pBuf); if (add_mtd_suffix(mtd_device, mtd_name, sizeof(mtd_name))) { printf("add suffix success: %s -> %s\n", mtd_device, mtd_name); } else { printf("add suffix fail: %s\n", mtd_device); } // start MTD partition upgrade if ( strcmp(header.type, "MTD") == 0 && header.UPmethod == 1 ) { idx0 = mtd_find_idx_copy(mtd_device); idx = mtd_find_idx_copy(mtd_name); if(idx){ snprintf(tmppath, sizeof(tmppath), "/sys/class/mtd/mtd%d/writesize", idx); // pagesize = read_mtd_writesize(tmppath); // printf("pagesize:%zu", pagesize); // write upgrade data snprintf(cmd, sizeof(cmd), "dd if=%s bs=1 skip=%zu count=%zu 2>/dev/null | mtd write - /dev/mtd%d", upfilePath, curoffset, data_size, idx); if(system(cmd)){ fprintf(stderr, "MTD write failed\n"); return -1; } } else if(idx0){ snprintf(tmppath, sizeof(tmppath), "/sys/class/mtd/mtd%d/writesize", idx0); // pagesize = read_mtd_writesize(tmppath); // printf("pagesize:%zu", pagesize); // write upgrade data snprintf(cmd, sizeof(cmd), "dd if=%s bs=1 skip=%zu count=%zu 2>/dev/null | mtd write - /dev/mtd%d", upfilePath, curoffset, data_size, idx0); if(system(cmd)){ fprintf(stderr, "MTD write failed\n"); return -1; } } else{ } } else if( strcmp(header.type, "UBI") == 0 && header.UPmethod == 0 ){ // ; if (nm_upgradeUbiImgCopy(upfilePath, curoffset, data_size, mtd_name) != OK) { NM_ERROR("Fail to write root content to %s!", mtd_name); // goto error; return -1; } } curoffset += data_size; // modify primaryboot age0 = get_bootconfig_age("bootconfig0"); age1 = get_bootconfig_age("bootconfig1"); const char *Up_config = (age0 <= age1) ? "bootconfig0" : "bootconfig1"; const char *Run_config = (age0 > age1) ? "bootconfig0" : "bootconfig1"; snprintf(bootpathUp, sizeof(bootpathUp), "/proc/boot_info/%s", Up_config); snprintf(bootpathRun, sizeof(bootpathRun), "/proc/boot_info/%s", Run_config); // memmove(mtd_device + 2, mtd_device, strlen(mtd_device) + 1); // +1 包括 null terminator // memcpy(mtd_device, "0:", 2); // primaryValueUp = get_primaryboot_value(bootpathUp, mtd_device); primaryValueRun = get_primaryboot_value(bootpathRun, mtd_device); idx0 = mtd_find_idx_copy(mtd_device); idx = mtd_find_idx_copy(mtd_name); printf("idx_device:%d, idx_name:%d\n", idx0, idx); if(idx0 == idx && primaryValueRun == 1){ toggle_primaryboot(bootpathUp,mtd_device); } else if(idx0 != idx && primaryValueRun == 0){ toggle_primaryboot(bootpathUp,mtd_device); } } free(upfileBuff); fclose(fp); return 0; } 优化上述代码
11-12
STATUS nm_upgradeFwupPartitions(int offset, const char *upfilePath, int nFileBytes) { int age0 = 0; int age1 = 0; char bootpathUp[256]; size_t curoffset; FILE* fp; char *pBuf = NULL; unsigned char fileMd5Check[16]; unsigned char *pMd5Key = NULL; size_t data_size = 0; int idx0 = -1; int idx = -1; char cmd[256]; char mtd_device[32]; char mtd_name[32]; int primaryValueRun = -1; PARTITIONS_UP_HEAD header; const char *Up_config = NULL; const int FLAG_SIZE = 4; char file_flag[FLAG_SIZE]; // 新升级文件标识 // const char *Run_config = NULL; pMd5Key = (unsigned char *)malloc(16 * sizeof(unsigned char)); if (!pMd5Key) { NM_ERROR("Memory allocation failed"); return -3; } age0 = get_bootconfig_age("bootconfig0"); age1 = get_bootconfig_age("bootconfig1"); Up_config = (age0 <= age1) ? "bootconfig0" : "bootconfig1"; // Run_config = (age0 > age1) ? "bootconfig0" : "bootconfig1"; snprintf(bootpathUp, sizeof(bootpathUp), "/proc/boot_info/%s", Up_config); fp = fopen(upfilePath, "rb"); if (!fp) { perror("can't open upgrade file"); free(pMd5Key); return -1; } curoffset = file_system_offset + offset; fseek(fp, curoffset, SEEK_CUR); // 读取文件开头的标志 if (fread(file_flag, 1, FLAG_SIZE, fp) != FLAG_SIZE) { perror("Failed to read file flag"); fclose(fp); return -1; } curoffset += FLAG_SIZE; if (memcmp(file_flag, "new", FLAG_SIZE) != 0) { printf("original upfile detected\n"); if (nm_checkBootAlter() == 0) { NM_DEBUG("Boot from %s, upgrade to %s", MTD_NAME_ROOTFS_0, MTD_NAME_ROOTFS_1); if (nm_upgradeUbiImg(upfilePath, curoffset - FLAG_SIZE, nFileBytes - file_system_offset, MTD_NAME_ROOTFS_1) != OK) { NM_ERROR("Fail to write root content to %s!", MTD_NAME_ROOTFS_1); return -1; } nm_addBootAlter(); } else { NM_DEBUG("Boot from %s, upgrade to %s", MTD_NAME_ROOTFS_1, MTD_NAME_ROOTFS_0); if (nm_upgradeUbiImg(upfilePath, curoffset - FLAG_SIZE, nFileBytes - file_system_offset, MTD_NAME_ROOTFS_0) != OK) { NM_ERROR("Fail to write root content to %s", MTD_NAME_ROOTFS_0); return -1; } nm_removeBootAlter(); } } else{ while (!feof(fp)) { memset(&header, 0, sizeof(header)); // 确保清零 if (fread(&header, sizeof(header), 1, fp) != 1) { if (feof(fp)) break; perror("get header fail"); fclose(fp); free(pMd5Key); return -2; } printf("name:%s;\n size:%u;\n type:%s;\n UPmethod:%d;\n",header.name,header.size,header.type,header.UPmethod); curoffset += sizeof(PARTITIONS_UP_HEAD); // 重置变量 memset(cmd, 0, sizeof(cmd)); memset(mtd_device, 0, sizeof(mtd_device)); memset(mtd_name, 0, sizeof(mtd_name)); idx0 = -1; idx = -1; primaryValueRun = -1; // get partition name and size snprintf(mtd_device, sizeof(mtd_device), "%s", header.name); data_size = header.size; // MD5 check pBuf = (char *)malloc(header.size + 16); if (!pBuf) { NM_ERROR("Memory allocation failed"); free(pMd5Key); fclose(fp); return -1; } memcpy(fileMd5Check, header.md5, 16); memcpy(pMd5Key, md5ImageKey, 16); memcpy(pBuf, pMd5Key, 16); fread(pBuf + 16, 1, header.size, fp); if (!md5_verify_digest(fileMd5Check, pBuf, 16 + header.size)) { fprintf(stderr, "md5 verify error!\n"); /* restore checksum */ memcpy(pBuf, fileMd5Check, 16); free(pBuf); fclose(fp); free(pMd5Key); return -100; } if (add_mtd_suffix(mtd_device, mtd_name, sizeof(mtd_name))) { printf("add suffix success: %s -> %s\n", mtd_device, mtd_name); } else { printf("add suffix fail: %s\n", mtd_device); } // start MTD partition upgrade if ( strcmp(header.type, "MTD") == 0 && header.UPmethod == 1 ) { idx0 = mtd_find_idx(mtd_device); idx = mtd_find_idx(mtd_name); if(idx != -1){ // write upgrade data snprintf(cmd, sizeof(cmd), "dd if=%s bs=1 skip=%zu count=%zu 2>/dev/null | mtd write - /dev/mtd%d", upfilePath, curoffset, data_size, idx); if(system(cmd)){ fprintf(stderr, "MTD write failed\n"); return -1; } primaryValueRun = get_primaryboot_value(bootpathUp, mtd_device); // upgrade primaryboot if(idx0 == idx && primaryValueRun == 1){ toggle_primaryboot(bootpathUp,mtd_device); } else if(idx0 != idx && primaryValueRun == 0){ toggle_primaryboot(bootpathUp,mtd_device); } } else if(idx0 != -1){ // write upgrade data snprintf(cmd, sizeof(cmd), "dd if=%s bs=1 skip=%zu count=%zu 2>/dev/null | mtd write - /dev/mtd%d", upfilePath, curoffset, data_size, idx0); if(system(cmd)){ fprintf(stderr, "MTD write failed\n"); free(pBuf); fclose(fp); free(pMd5Key); return -1; } } else{ printf("partition name error!\n"); free(pBuf); fclose(fp); free(pMd5Key); return -1; } } else if( strcmp(header.type, "UBI") == 0 && header.UPmethod == 0 ){ if (nm_upgradeUbiImg(upfilePath, curoffset, data_size, mtd_name) != OK) { NM_ERROR("Fail to write content to UBI %s!", mtd_name); // goto error; free(pBuf); fclose(fp); free(pMd5Key); return -1; } } curoffset += data_size; free(pBuf); pBuf = NULL; } } free(pMd5Key); fclose(fp); return OK; } 优化上述代码,将资源释放统一放到后方
最新发布
11-28
// SPDX-License-Identifier: GPL-2.0-only #include <linux/module.h> #include <linux/sched.h> #include <linux/ctype.h> #include <linux/fd.h> #include <linux/tty.h> #include <linux/suspend.h> #include <linux/root_dev.h> #include <linux/security.h> #include <linux/delay.h> #include <linux/genhd.h> #include <linux/mount.h> #include <linux/device.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/initrd.h> #include <linux/async.h> #include <linux/fs_struct.h> #include <linux/slab.h> #include <linux/ramfs.h> #include <linux/shmem_fs.h> #include <linux/nfs_fs.h> #include <linux/nfs_fs_sb.h> #include <linux/nfs_mount.h> #include <linux/raid/detect.h> #include <uapi/linux/mount.h> #include "do_mounts.h" int root_mountflags = MS_RDONLY | MS_SILENT; static char * __initdata root_device_name; static char __initdata saved_root_name[64]; static int root_wait; dev_t ROOT_DEV; static int __init load_ramdisk(char *str) { pr_warn("ignoring the deprecated load_ramdisk= option\n"); return 1; } __setup("load_ramdisk=", load_ramdisk); static int __init readonly(char *str) { if (*str) return 0; root_mountflags |= MS_RDONLY; return 1; } static int __init readwrite(char *str) { if (*str) return 0; root_mountflags &= ~MS_RDONLY; return 1; } __setup("ro", readonly); __setup("rw", readwrite); #ifdef CONFIG_BLOCK struct uuidcmp { const char *uuid; int len; }; /** * match_dev_by_uuid - callback for finding a partition using its uuid * @dev: device passed in by the caller * @data: opaque pointer to the desired struct uuidcmp to match * * Returns 1 if the device matches, and 0 otherwise. */ static int match_dev_by_uuid(struct device *dev, const void *data) { const struct uuidcmp *cmp = data; struct hd_struct *part = dev_to_part(dev); if (!part->info) goto no_match; if (strncasecmp(cmp->uuid, part->info->uuid, cmp->len)) goto no_match; return 1; no_match: return 0; } /** * devt_from_partuuid - looks up the dev_t of a partition by its UUID * @uuid_str: char array containing ascii UUID * * The function will return the first partition which contains a matching * UUID value in its partition_meta_info struct. This does not search * by filesystem UUIDs. * * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be * extracted and used as an offset from the partition identified by the UUID. * * Returns the matching dev_t on success or 0 on failure. */ static dev_t devt_from_partuuid(const char *uuid_str) { dev_t res = 0; struct uuidcmp cmp; struct device *dev = NULL; struct gendisk *disk; struct hd_struct *part; int offset = 0; bool clear_root_wait = false; char *slash; cmp.uuid = uuid_str; slash = strchr(uuid_str, '/'); /* Check for optional partition number offset attributes. */ if (slash) { char c = 0; /* Explicitly fail on poor PARTUUID syntax. */ if (sscanf(slash + 1, "PARTNROFF=%d%c", &offset, &c) != 1) { clear_root_wait = true; goto done; } cmp.len = slash - uuid_str; } else { cmp.len = strlen(uuid_str); } if (!cmp.len) { clear_root_wait = true; goto done; } dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid); if (!dev) goto done; res = dev->devt; /* Attempt to find the partition by offset. */ if (!offset) goto no_offset; res = 0; disk = part_to_disk(dev_to_part(dev)); part = disk_get_part(disk, dev_to_part(dev)->partno + offset); if (part) { res = part_devt(part); put_device(part_to_dev(part)); } no_offset: put_device(dev); done: if (clear_root_wait) { pr_err("VFS: PARTUUID= is invalid.\n" "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n"); if (root_wait) pr_err("Disabling rootwait; root= is invalid.\n"); root_wait = 0; } return res; } /** * match_dev_by_label - callback for finding a partition using its label * @dev: device passed in by the caller * @data: opaque pointer to the label to match * * Returns 1 if the device matches, and 0 otherwise. */ static int match_dev_by_label(struct device *dev, const void *data) { const char *label = data; struct hd_struct *part = dev_to_part(dev); if (part->info && !strcmp(label, part->info->volname)) return 1; return 0; } #endif /* * Convert a name into device number. We accept the following variants: * * 1) <hex_major><hex_minor> device number in hexadecimal represents itself * no leading 0x, for example b302. * 2) /dev/nfs represents Root_NFS (0xff) * 3) /dev/<disk_name> represents the device number of disk * 4) /dev/<disk_name><decimal> represents the device number * of partition - device number of disk plus the partition number * 5) /dev/<disk_name>p<decimal> - same as the above, that form is * used when disk name of partitioned disk ends on a digit. * 6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the * unique id of a partition if the partition table provides it. * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero- * filled hex representation of the 32-bit "NT disk signature", and PP * is a zero-filled hex representation of the 1-based partition number. * 7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to * a partition with a known unique id. * 8) <major>:<minor> major and minor number of the device separated by * a colon. * 9) PARTLABEL=<name> with name being the GPT partition label. * MSDOS partitions do not support labels! * 10) /dev/cifs represents Root_CIFS (0xfe) * * If name doesn't have fall into the categories above, we return (0,0). * block_class is used to check if something is a disk name. If the disk * name contains slashes, the device name has them replaced with * bangs. */ dev_t name_to_dev_t(const char *name) { char s[32]; char *p; dev_t res = 0; int part; #ifdef CONFIG_BLOCK if (strncmp(name, "PARTUUID=", 9) == 0) { name += 9; res = devt_from_partuuid(name); if (!res) goto fail; goto done; } else if (strncmp(name, "PARTLABEL=", 10) == 0) { struct device *dev; dev = class_find_device(&block_class, NULL, name + 10, &match_dev_by_label); if (!dev) goto fail; res = dev->devt; put_device(dev); goto done; } #endif if (strncmp(name, "/dev/", 5) != 0) { unsigned maj, min, offset; char dummy; if ((sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2) || (sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3)) { res = MKDEV(maj, min); if (maj != MAJOR(res) || min != MINOR(res)) goto fail; } else { res = new_decode_dev(simple_strtoul(name, &p, 16)); if (*p) goto fail; } goto done; } name += 5; res = Root_NFS; if (strcmp(name, "nfs") == 0) goto done; res = Root_CIFS; if (strcmp(name, "cifs") == 0) goto done; res = Root_RAM0; if (strcmp(name, "ram") == 0) goto done; if (strlen(name) > 31) goto fail; strcpy(s, name); for (p = s; *p; p++) if (*p == '/') *p = '!'; res = blk_lookup_devt(s, 0); if (res) goto done; /* * try non-existent, but valid partition, which may only exist * after revalidating the disk, like partitioned md devices */ while (p > s && isdigit(p[-1])) p--; if (p == s || !*p || *p == '0') goto fail; /* try disk name without <part number> */ part = simple_strtoul(p, NULL, 10); *p = '\0'; res = blk_lookup_devt(s, part); if (res) goto done; /* try disk name without p<part number> */ if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') goto fail; p[-1] = '\0'; res = blk_lookup_devt(s, part); if (res) goto done; fail: return 0; done: return res; } EXPORT_SYMBOL_GPL(name_to_dev_t); static int __init root_dev_setup(char *line) { strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1; } __setup("root=", root_dev_setup); static int __init rootwait_setup(char *str) { if (*str) return 0; root_wait = 1; return 1; } __setup("rootwait", rootwait_setup); static char * __initdata root_mount_data; static int __init root_data_setup(char *str) { root_mount_data = str; return 1; } static char * __initdata root_fs_names; static int __init fs_names_setup(char *str) { root_fs_names = str; return 1; } static unsigned int __initdata root_delay; static int __init root_delay_setup(char *str) { root_delay = simple_strtoul(str, NULL, 0); return 1; } __setup("rootflags=", root_data_setup); __setup("rootfstype=", fs_names_setup); __setup("rootdelay=", root_delay_setup); static void __init get_fs_names(char *page) { char *s = page; if (root_fs_names) { strcpy(page, root_fs_names); while (*s++) { if (s[-1] == ',') s[-1] = '\0'; } } else { int len = get_filesystem_list(page); char *p, *next; page[len] = '\0'; for (p = page-1; p; p = next) { next = strchr(++p, '\n'); if (*p++ != '\t') continue; while ((*s++ = *p++) != '\n') ; s[-1] = '\0'; } } *s = '\0'; } static int __init do_mount_root(const char *name, const char *fs, const int flags, const void *data) { struct super_block *s; struct page *p = NULL; char *data_page = NULL; int ret; if (data) { /* init_mount() requires a full page as fifth argument */ p = alloc_page(GFP_KERNEL); if (!p) return -ENOMEM; data_page = page_address(p); /* zero-pad. init_mount() will make sure it's terminated */ strncpy(data_page, data, PAGE_SIZE); } ret = init_mount(name, "/root", fs, flags, data_page); if (ret) goto out; init_chdir("/root"); s = current->fs->pwd.dentry->d_sb; ROOT_DEV = s->s_dev; printk(KERN_INFO "VFS: Mounted root (%s filesystem)%s on device %u:%u.\n", s->s_type->name, sb_rdonly(s) ? " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); out: if (p) put_page(p); return ret; } void __init mount_block_root(char *name, int flags) { struct page *page = alloc_page(GFP_KERNEL); char *fs_names = page_address(page); char *p; char b[BDEVNAME_SIZE]; scnprintf(b, BDEVNAME_SIZE, "unknown-block(%u,%u)", MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); get_fs_names(fs_names); retry: for (p = fs_names; *p; p += strlen(p)+1) { int err = do_mount_root(name, p, flags, root_mount_data); switch (err) { case 0: goto out; case -EACCES: case -EINVAL: continue; } /* * Allow the user to distinguish between failed sys_open * and bad superblock on root device. * and give them a list of the available devices */ printk("VFS: Cannot open root device \"%s\" or %s: error %d\n", root_device_name, b, err); printk("Please append a correct \"root=\" boot option; here are the available partitions:\n"); printk_all_partitions(); #ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT printk("DEBUG_BLOCK_EXT_DEVT is enabled, you need to specify " "explicit textual name for \"root=\" boot option.\n"); #endif panic("VFS: Unable to mount root fs on %s", b); } if (!(flags & SB_RDONLY)) { flags |= SB_RDONLY; goto retry; } printk("List of all partitions:\n"); printk_all_partitions(); printk("No filesystem could mount root, tried: "); for (p = fs_names; *p; p += strlen(p)+1) printk(" %s", p); printk("\n"); panic("VFS: Unable to mount root fs on %s", b); out: put_page(page); } #ifdef CONFIG_ROOT_NFS #define NFSROOT_TIMEOUT_MIN 5 #define NFSROOT_TIMEOUT_MAX 30 #define NFSROOT_RETRY_MAX 5 static int __init mount_nfs_root(void) { char *root_dev, *root_data; unsigned int timeout; int try, err; err = nfs_root_data(&root_dev, &root_data); if (err != 0) return 0; /* * The server or network may not be ready, so try several * times. Stop after a few tries in case the client wants * to fall back to other boot methods. */ timeout = NFSROOT_TIMEOUT_MIN; for (try = 1; ; try++) { err = do_mount_root(root_dev, "nfs", root_mountflags, root_data); if (err == 0) return 1; if (try > NFSROOT_RETRY_MAX) break; /* Wait, in case the server refused us immediately */ ssleep(timeout); timeout <<= 1; if (timeout > NFSROOT_TIMEOUT_MAX) timeout = NFSROOT_TIMEOUT_MAX; } return 0; } #endif #ifdef CONFIG_CIFS_ROOT extern int cifs_root_data(char **dev, char **opts); #define CIFSROOT_TIMEOUT_MIN 5 #define CIFSROOT_TIMEOUT_MAX 30 #define CIFSROOT_RETRY_MAX 5 static int __init mount_cifs_root(void) { char *root_dev, *root_data; unsigned int timeout; int try, err; err = cifs_root_data(&root_dev, &root_data); if (err != 0) return 0; timeout = CIFSROOT_TIMEOUT_MIN; for (try = 1; ; try++) { err = do_mount_root(root_dev, "cifs", root_mountflags, root_data); if (err == 0) return 1; if (try > CIFSROOT_RETRY_MAX) break; ssleep(timeout); timeout <<= 1; if (timeout > CIFSROOT_TIMEOUT_MAX) timeout = CIFSROOT_TIMEOUT_MAX; } return 0; } #endif void __init mount_root(void) { #ifdef CONFIG_ROOT_NFS if (ROOT_DEV == Root_NFS) { if (!mount_nfs_root()) printk(KERN_ERR "VFS: Unable to mount root fs via NFS.\n"); return; } #endif #ifdef CONFIG_CIFS_ROOT if (ROOT_DEV == Root_CIFS) { if (!mount_cifs_root()) printk(KERN_ERR "VFS: Unable to mount root fs via SMB.\n"); return; } #endif #ifdef CONFIG_BLOCK { int err = create_dev("/dev/root", ROOT_DEV); if (err < 0) pr_emerg("Failed to create /dev/root: %d\n", err); mount_block_root("/dev/root", root_mountflags); } #endif } /* * Prepare the namespace - decide what/where to mount, load ramdisks, etc. */ void __init prepare_namespace(void) { if (root_delay) { printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); ssleep(root_delay); } /* * wait for the known devices to complete their probing * * Note: this is a potential source of long boot delays. * For example, it is not atypical to wait 5 seconds here * for the touchpad of a laptop to initialize. */ wait_for_device_probe(); md_run_setup(); if (saved_root_name[0]) { root_device_name = saved_root_name; if (!strncmp(root_device_name, "mtd", 3) || !strncmp(root_device_name, "ubi", 3)) { mount_block_root(root_device_name, root_mountflags); goto out; } ROOT_DEV = name_to_dev_t(root_device_name); if (strncmp(root_device_name, "/dev/", 5) == 0) root_device_name += 5; } if (initrd_load()) goto out; /* wait for any asynchronous scanning to complete */ if ((ROOT_DEV == 0) && root_wait) { printk(KERN_INFO "Waiting for root device %s...\n", saved_root_name); while (driver_probe_done() != 0 || (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) msleep(5); async_synchronize_full(); } mount_root(); out: devtmpfs_mount(); init_mount(".", "/", NULL, MS_MOVE, NULL); init_chroot("."); } static bool is_tmpfs; static int rootfs_init_fs_context(struct fs_context *fc) { if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) return shmem_init_fs_context(fc); return ramfs_init_fs_context(fc); } struct file_system_type rootfs_fs_type = { .name = "rootfs", .init_fs_context = rootfs_init_fs_context, .kill_sb = kill_litter_super, }; void __init init_rootfs(void) { if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] && (!root_fs_names || strstr(root_fs_names, "tmpfs"))) is_tmpfs = true; } 详细解析一下
10-23
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值