hnvram: fix hnvram partition write flushing
On Chimera with Linux 4.4, hnvram write operations are failing:
NPAPID1600X0002# hnvram -w PLATFORM_NAME=GFCH100
ioctl(hnvram, BLKFLSBUF, 0): Operation not permitted
ioctl(hnvram, BLKFLSBUF, 0): Operation not permitted
[HMX_NVRAM_SetField] field(16)-PLATFORM_NAME error(00000006) : HMX_NVRAM_Write
Unable to write PLATFORM_NAME
This happens because:
1. On Chimera, hnvram partition is on eMMC
2. On Linux 4.4, mmc_blk_ioctl() does not allow write buffer flushes
for MMC partitions:
/*
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
* whole block device, not on a partition. This prevents overspray
* between sibling partitions.
*/
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
return -EPERM;
}
Fix:
When hnvram lives on MMC partition, flush the entire MMC device.
Change-Id: Idea3c35c8670c78212e940bc9417ab04080d10c2
diff --git a/libupgrade/hmx_upgrade_flash.c b/libupgrade/hmx_upgrade_flash.c
index ce9268c..86833af 100644
--- a/libupgrade/hmx_upgrade_flash.c
+++ b/libupgrade/hmx_upgrade_flash.c
@@ -104,8 +104,6 @@
/************************ static variables *************************/
/*******************************************************************/
/* Start static variable */
-int fd_nvram = -1;
-
/* End static variable */
/*******************************************************************/
@@ -116,101 +114,148 @@
{
int ret;
void *buf;
+ char *rpath;
+ char *p;
+ int fd_nvram;
if (libupgrade_verbose) printf("[%s] offset %08lx, size %d, data = %02x\n",
__FUNCTION__, offset, size, data[0] );
buf = malloc(size);
- if(!buf) {
+ if (!buf) {
perror("malloc");
return -1;
}
- if ( fd_nvram < 0 ) {
- fd_nvram = open(TARGET_NVRAM_MTD, (O_RDWR | O_SYNC) );
- if( fd_nvram < 0 ) {
- perror(TARGET_NVRAM_MTD);
- free(buf);
- return -1;
- }
+ rpath = realpath(TARGET_NVRAM_MTD, NULL);
+ if (!rpath) {
+ perror("realpath");
+ free(buf);
+ return -1;
+ }
+
+ fd_nvram = open(rpath, (O_RDWR | O_SYNC));
+ if (fd_nvram < 0) {
+ ret = -1;
+ perror(rpath);
+ goto out_free;
}
ret = lseek(fd_nvram, offset, SEEK_SET);
- if(ret < 0) {
+ if (ret < 0) {
perror("lseek");
- free(buf);
- return -1;
+ goto out_close;
+ }
+
+ ret = read(fd_nvram, buf, size);
+ if (ret < 0) {
+ perror("read");
+ goto out_close;
}
/* Only write to flash if data is different from what is already there. */
- ret = read(fd_nvram, buf, size);
- if(ret < 0) {
- perror("read");
- free(buf);
- return -1;
+ if (ret == size && !memcmp(data, buf, size)) {
+ ret = 0;
+ goto out_close;
}
- if (ret!=size || memcmp(data, buf, size)) {
- ret = lseek(fd_nvram, offset, SEEK_SET);
- if(ret < 0) {
- perror("lseek");
- free(buf);
- return -1;
- }
- ret = write(fd_nvram, data, size);
- if(ret < 0) {
- perror("write");
- free(buf);
- return -1;
+ ret = lseek(fd_nvram, offset, SEEK_SET);
+ if (ret < 0) {
+ perror("lseek");
+ goto out_close;
+ }
+
+ ret = write(fd_nvram, data, size);
+ if (ret < 0) {
+ perror("write");
+ goto out_close;
+ }
+
+ /*
+ * If hnvram is on MMC partition, we cannot flush the partition.
+ * Instead, we need to flush the entire MMC device.
+ * For example, GFCH100 hnvram partition is at /dev/mmcblk0p1,
+ * so we need to flush /dev/mmcblk0.
+ */
+ p = strstr(rpath, "mmcblk");
+ if (p) {
+ p = strchr(p + 6, 'p');
+ if (p) {
+ *p = '\0';
+ close(fd_nvram);
+ fd_nvram = open(rpath, (O_RDWR | O_SYNC));
+ if (fd_nvram < 0) {
+ ret = -1;
+ perror(rpath);
+ goto out_free;
+ }
}
}
- /* Writes to /dev/mtdblockX are cached indefinitely until the last file
+ /*
+ * Writes to /dev/mtdblockX are cached indefinitely until the last file
* descriptor is closed. Flush this cache after writing. The user depends on
* data being physically written to flash when this function returns.
- * Requires CAP_SYS_ADMIN. */
+ * Requires CAP_SYS_ADMIN.
+ */
ret = ioctl(fd_nvram, BLKFLSBUF, 0);
- if(ret < 0) {
+ if (ret < 0) {
perror("ioctl(hnvram, BLKFLSBUF, 0)");
- free(buf);
- return -1;
+ goto out_close;
}
+ ret = 0;
+
+out_close:
+ close(fd_nvram);
+out_free:
+ free(rpath);
free(buf);
- return 0;
+ return ret;
}
int HMX_UPGRADE_NVRAM_Read(unsigned long offset, unsigned char * data,
unsigned int size )
{
+ char *rpath;
+ int fd_nvram;
int ret;
if (libupgrade_verbose) printf("[%s] offset %08lx, size %d, data = %02x\n",
__FUNCTION__, offset, size, data[0] );
- /* */
- if ( fd_nvram < 0 ) {
- fd_nvram = open(TARGET_NVRAM_MTD, (O_RDWR | O_SYNC) );
- if( fd_nvram < 0 ) {
- perror(TARGET_NVRAM_MTD);
- return -1;
- }
- }
-
-
- ret = lseek(fd_nvram, offset, SEEK_SET);
- if(ret < 0) {
- perror("lseek");
+ rpath = realpath(TARGET_NVRAM_MTD, NULL);
+ if (!rpath) {
+ perror("realpath");
return -1;
}
- ret = read(fd_nvram, data, size);
- if(ret < 0) {
- perror("read");
- return ret;
+ fd_nvram = open(rpath, (O_RDWR | O_SYNC));
+ if (fd_nvram < 0) {
+ ret = -1;
+ perror(rpath);
+ goto out_free;
}
- return 0;
+ ret = lseek(fd_nvram, offset, SEEK_SET);
+ if (ret < 0) {
+ perror("lseek");
+ goto out_close;
+ }
+
+ ret = read(fd_nvram, data, size);
+ if (ret < 0) {
+ perror("read");
+ goto out_close;
+ }
+
+ ret = 0;
+
+out_close:
+ close(fd_nvram);
+out_free:
+ free(rpath);
+ return ret;
}