Skip to content
Snippets Groups Projects
fw_env.c 32.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			 environment.flag_scheme);
    		return -1;
    	}
    
    #ifdef DEBUG
    
    	fprintf(stderr, "Writing new environment at 0x%llx on %s\n",
    
    		DEVOFFSET (dev_target), DEVNAME (dev_target));
    #endif
    
    	rc = flash_write_buf(dev_target, fd_target, environment.image,
    
    	if (rc < 0)
    		return rc;
    
    	if (environment.flag_scheme == FLAG_BOOLEAN) {
    		/* Have to set obsolete flag */
    		off_t offset = DEVOFFSET (dev_current) +
    			offsetof (struct env_image_redundant, flags);
    #ifdef DEBUG
    
    		fprintf(stderr,
    
    			"Setting obsolete flag in environment at 0x%llx on %s\n",
    
    			DEVOFFSET (dev_current), DEVNAME (dev_current));
    #endif
    		flash_flag_obsolete (dev_current, fd_current, offset);
    	}
    
    	return 0;
    }
    
    static int flash_read (int fd)
    {
    	int rc;
    
    
    	rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE,
    
    	if (rc != CUR_ENVSIZE)
    		return -1;
    
    }
    
    static int flash_io (int mode)
    {
    	int fd_current, fd_target, rc, dev_target;
    
    	/* dev_current: fd_current, erase_current */
    	fd_current = open (DEVNAME (dev_current), mode);
    	if (fd_current < 0) {
    		fprintf (stderr,
    			 "Can't open %s: %s\n",
    			 DEVNAME (dev_current), strerror (errno));
    		return -1;
    	}
    
    	if (mode == O_RDWR) {
    
    			/* switch to next partition for writing */
    			dev_target = !dev_current;
    			/* dev_target: fd_target, erase_target */
    			fd_target = open (DEVNAME (dev_target), mode);
    			if (fd_target < 0) {
    
    					 "Can't open %s: %s\n",
    					 DEVNAME (dev_target),
    					 strerror (errno));
    				rc = -1;
    				goto exit;
    
    		} else {
    			dev_target = dev_current;
    			fd_target = fd_current;
    
    
    		rc = flash_write (fd_current, fd_target, dev_target);
    
    
    			if (close (fd_target)) {
    
    					"I/O error on %s: %s\n",
    
    					DEVNAME (dev_target),
    
    					strerror (errno));
    
    		rc = flash_read (fd_current);
    
    exit:
    	if (close (fd_current)) {
    
    			 "I/O error on %s: %s\n",
    			 DEVNAME (dev_current), strerror (errno));
    		return -1;
    
    	return rc;
    
    }
    
    /*
     * Prevent confusion if running from erased flash memory
     */
    
    int fw_env_open(struct env_opts *opts)
    
    	int crc0, crc0_ok;
    
    	unsigned char flag0;
    
    	unsigned char flag1;
    
    	void *addr1;
    
    	struct env_image_single *single;
    	struct env_image_redundant *redundant;
    
    	if (parse_config(opts))		/* should fill envdevices */
    
    		return -1;
    
    	addr0 = calloc(1, CUR_ENVSIZE);
    
    	if (addr0 == NULL) {
    
    			"Not enough memory for environment (%ld bytes)\n",
    
    		return -1;
    
    	/* read environment from FLASH to local buffer */
    
    	environment.image = addr0;
    
    	if (HaveRedundEnv) {
    		redundant = addr0;
    		environment.crc		= &redundant->crc;
    		environment.flags	= &redundant->flags;
    		environment.data	= redundant->data;
    	} else {
    		single = addr0;
    		environment.crc		= &single->crc;
    		environment.flags	= NULL;
    		environment.data	= single->data;
    
    	dev_current = 0;
    	if (flash_io (O_RDONLY))
    		return -1;
    
    	crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
    
    		ret = env_aes_cbc_crypt(environment.data, 0,
    
    	crc0_ok = (crc0 == *environment.crc);
    
    		if (!crc0_ok) {
    
    			fprintf (stderr,
    				"Warning: Bad CRC, using default environment\n");
    
    			memcpy(environment.data, default_environment, sizeof default_environment);
    
    		flag0 = *environment.flags;
    
    		dev_current = 1;
    
    		addr1 = calloc(1, CUR_ENVSIZE);
    
    		if (addr1 == NULL) {
    
    				"Not enough memory for environment (%ld bytes)\n",
    
    			return -1;
    
    		redundant = addr1;
    
    		/*
    		 * have to set environment.image for flash_read(), careful -
    		 * other pointers in environment still point inside addr0
    		 */
    		environment.image = addr1;
    		if (flash_io (O_RDONLY))
    			return -1;
    
    		/* Check flag scheme compatibility */
    		if (DEVTYPE(dev_current) == MTD_NORFLASH &&
    		    DEVTYPE(!dev_current) == MTD_NORFLASH) {
    			environment.flag_scheme = FLAG_BOOLEAN;
    		} else if (DEVTYPE(dev_current) == MTD_NANDFLASH &&
    			   DEVTYPE(!dev_current) == MTD_NANDFLASH) {
    			environment.flag_scheme = FLAG_INCREMENTAL;
    
    		} else if (DEVTYPE(dev_current) == MTD_DATAFLASH &&
    			   DEVTYPE(!dev_current) == MTD_DATAFLASH) {
    			environment.flag_scheme = FLAG_BOOLEAN;
    
    		} else if (DEVTYPE(dev_current) == MTD_UBIVOLUME &&
    			   DEVTYPE(!dev_current) == MTD_UBIVOLUME) {
    			environment.flag_scheme = FLAG_INCREMENTAL;
    
    		} else if (DEVTYPE(dev_current) == MTD_ABSENT &&
    			   DEVTYPE(!dev_current) == MTD_ABSENT) {
    			environment.flag_scheme = FLAG_INCREMENTAL;
    
    		} else {
    			fprintf (stderr, "Incompatible flash types!\n");
    			return -1;
    
    		crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE);
    
    			ret = env_aes_cbc_crypt(redundant->data, 0,
    
    		crc1_ok = (crc1 == redundant->crc);
    		flag1 = redundant->flags;
    
    		if (crc0_ok && !crc1_ok) {
    			dev_current = 0;
    		} else if (!crc0_ok && crc1_ok) {
    			dev_current = 1;
    		} else if (!crc0_ok && !crc1_ok) {
    
    			fprintf (stderr,
    				"Warning: Bad CRC, using default environment\n");
    
    			memcpy (environment.data, default_environment,
    				sizeof default_environment);
    			dev_current = 0;
    		} else {
    			switch (environment.flag_scheme) {
    			case FLAG_BOOLEAN:
    				if (flag0 == active_flag &&
    				    flag1 == obsolete_flag) {
    					dev_current = 0;
    				} else if (flag0 == obsolete_flag &&
    					   flag1 == active_flag) {
    					dev_current = 1;
    				} else if (flag0 == flag1) {
    					dev_current = 0;
    				} else if (flag0 == 0xFF) {
    					dev_current = 0;
    				} else if (flag1 == 0xFF) {
    					dev_current = 1;
    				} else {
    					dev_current = 0;
    				}
    				break;
    			case FLAG_INCREMENTAL:
    
    				if (flag0 == 255 && flag1 == 0)
    
    					dev_current = 1;
    				else if ((flag1 == 255 && flag0 == 0) ||
    
    					 flag0 >= flag1)
    
    					dev_current = 0;
    
    				else /* flag1 > flag0 */
    					dev_current = 1;
    
    				break;
    			default:
    				fprintf (stderr, "Unknown flag scheme %u \n",
    					 environment.flag_scheme);
    				return -1;
    			}
    		}
    
    		/*
    		 * If we are reading, we don't need the flag and the CRC any
    		 * more, if we are writing, we will re-calculate CRC and update
    		 * flags before writing out
    		 */
    		if (dev_current) {
    			environment.image	= addr1;
    			environment.crc		= &redundant->crc;
    			environment.flags	= &redundant->flags;
    			environment.data	= redundant->data;
    			free (addr0);
    		} else {
    			environment.image	= addr0;
    			/* Other pointers are already set */
    
    			free (addr1);
    
    #ifdef DEBUG
    		fprintf(stderr, "Selected env in %s\n", DEVNAME(dev_current));
    #endif
    
    	return 0;
    
    static int check_device_config(int dev)
    {
    	struct stat st;
    	int fd, rc = 0;
    
    
    	if (DEVOFFSET(dev) % DEVESIZE(dev) != 0) {
    
    		fprintf(stderr, "Environment does not start on (erase) block boundary\n");
    
    		errno = EINVAL;
    		return -1;
    	}
    
    	if (ENVSIZE(dev) > ENVSECTORS(dev) * DEVESIZE(dev)) {
    		fprintf(stderr, "Environment does not fit into available sectors\n");
    		errno = EINVAL;
    		return -1;
    	}
    
    
    	fd = open(DEVNAME(dev), O_RDONLY);
    	if (fd < 0) {
    		fprintf(stderr,
    			"Cannot open %s: %s\n",
    			DEVNAME(dev), strerror(errno));
    		return -1;
    	}
    
    	rc = fstat(fd, &st);
    	if (rc < 0) {
    		fprintf(stderr, "Cannot stat the file %s\n",
    			DEVNAME(dev));
    		goto err;
    	}
    
    	if (S_ISCHR(st.st_mode)) {
    		struct mtd_info_user mtdinfo;
    		rc = ioctl(fd, MEMGETINFO, &mtdinfo);
    		if (rc < 0) {
    			fprintf(stderr, "Cannot get MTD information for %s\n",
    				DEVNAME(dev));
    			goto err;
    		}
    		if (mtdinfo.type != MTD_NORFLASH &&
    		    mtdinfo.type != MTD_NANDFLASH &&
    		    mtdinfo.type != MTD_DATAFLASH &&
    		    mtdinfo.type != MTD_UBIVOLUME) {
    			fprintf(stderr, "Unsupported flash type %u on %s\n",
    				mtdinfo.type, DEVNAME(dev));
    			goto err;
    		}
    		DEVTYPE(dev) = mtdinfo.type;
    	} else {
    
    		uint64_t size;
    
    		DEVTYPE(dev) = MTD_ABSENT;
    
    
    		/*
    		 * Check for negative offsets, treat it as backwards offset
    		 * from the end of the block device
    		 */
    		if (DEVOFFSET(dev) < 0) {
    			rc = ioctl(fd, BLKGETSIZE64, &size);
    			if (rc < 0) {
    				fprintf(stderr, "Could not get block device size on %s\n",
    					DEVNAME(dev));
    				goto err;
    			}
    
    			DEVOFFSET(dev) = DEVOFFSET(dev) + size;
    #ifdef DEBUG
    			fprintf(stderr, "Calculated device offset 0x%llx on %s\n",
    				DEVOFFSET(dev), DEVNAME(dev));
    #endif
    		}
    
    static int parse_config(struct env_opts *opts)
    
    	if (!opts)
    		opts = &default_opts;
    
    	/* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */
    
    	if (get_config(opts->config_file)) {
    
    		fprintf(stderr, "Cannot parse config file '%s': %m\n",
    
    		return -1;
    
    	DEVNAME (0) = DEVICE1_NAME;
    
    	DEVOFFSET (0) = DEVICE1_OFFSET;
    	ENVSIZE (0) = ENV1_SIZE;
    
    	/* Default values are: erase-size=env-size */
    
    	DEVESIZE (0) = ENVSIZE (0);
    
    	/* #sectors=env-size/erase-size (rounded up) */
    	ENVSECTORS (0) = (ENVSIZE(0) + DEVESIZE(0) - 1) / DEVESIZE(0);
    
    	DEVESIZE (0) = DEVICE1_ESIZE;
    
    #endif
    #ifdef DEVICE1_ENVSECTORS
    
    	ENVSECTORS (0) = DEVICE1_ENVSECTORS;
    
    	DEVNAME (1) = DEVICE2_NAME;
    
    	DEVOFFSET (1) = DEVICE2_OFFSET;
    	ENVSIZE (1) = ENV2_SIZE;
    
    	/* Default values are: erase-size=env-size */
    
    	DEVESIZE (1) = ENVSIZE (1);
    
    	/* #sectors=env-size/erase-size (rounded up) */
    	ENVSECTORS (1) = (ENVSIZE(1) + DEVESIZE(1) - 1) / DEVESIZE(1);
    
    	DEVESIZE (1) = DEVICE2_ESIZE;
    
    #endif
    #ifdef DEVICE2_ENVSECTORS
    
    	ENVSECTORS (1) = DEVICE2_ENVSECTORS;
    
    	rc = check_device_config(0);
    	if (rc < 0)
    		return rc;
    
    	if (HaveRedundEnv) {
    		rc = check_device_config(1);
    		if (rc < 0)
    			return rc;
    
    		if (ENVSIZE(0) != ENVSIZE(1)) {
    			fprintf(stderr,
    
    				"Redundant environments have unequal size");
    			return -1;
    
    	}
    
    	usable_envsize = CUR_ENVSIZE - sizeof(uint32_t);
    	if (HaveRedundEnv)
    		usable_envsize -= sizeof(char);
    
    
    		usable_envsize &= ~(AES_KEY_LENGTH - 1);
    
    
    
    #if defined(CONFIG_FILE)
    static int get_config (char *fname)
    {
    	FILE *fp;
    	int i = 0;
    	int rc;
    	char dump[128];
    
    	fp = fopen (fname, "r");
    	if (fp == NULL)
    		return -1;
    
    	while (i < 2 && fgets (dump, sizeof (dump), fp)) {
    
    		/* Skip incomplete conversions and comment strings */
    
    		if (dump[0] == '#')
    
    		rc = sscanf(dump, "%ms %lli %lx %lx %lx",
    			    &devname,
    			    &DEVOFFSET(i),
    			    &ENVSIZE(i),
    			    &DEVESIZE(i),
    			    &ENVSECTORS(i));
    
    		if (rc < 4)
    			/* Assume the erase size is the same as the env-size */
    			DEVESIZE(i) = ENVSIZE(i);
    
    
    		if (rc < 5)
    
    			/* Assume enough env sectors to cover the environment */
    			ENVSECTORS (i) = (ENVSIZE(i) + DEVESIZE(i) - 1) / DEVESIZE(i);
    
    	if (!i) {			/* No valid entries found */
    
    		return -1;