# ARM Linux and UBoot - Can I make a read-only MTD writable?

I have several embedded Linux devices installed at customer sites. We have an updated Linux kernel we are prepared to deploy to these devices. The problem is that on these devices, the U-Boot arguments are specified such that:


bootargs = {....} mtdparts=atmel_nand:16M(kernel)ro,240M(rootfs)


So, I can't flash_erase to nandwrite the kernel or U-Boot over SSH because MTD0 is read-only. I can't tweak the U-Boot args without serial access. For many of the customers, taking all the devices down and serially hooking them up and interrupting U-Boot to get to the bootargs would be extremely cumbersome. Is there any way to change MTD0 from ro to rw from the kernel once it's loaded?

• Another options might be to see if your system would support using kexec while running to boot a new kernel with a different command line or configuration. – Chris Stratton May 2 '13 at 18:46
• @ChrisStratton That was an awesome idea, but sadly it's not implemented in that kernel. I am definitely looking at cross compiling kexec and distributing it with the upgrade, though, depending on how much would be involved there. – trycatch May 2 '13 at 20:14
• @ChrisStratton - I haven't been able to get kexec working - it's not built into the kernel I'm trying to upgrade, I've managed to build kexec-tools but evidently that's only to work on/with kexec if it's installed, but supposedly one can run kexec from a binary vs. built-in/module? Do you know anything about that? – trycatch May 8 '13 at 16:09

Have you considered creating a simple kernel module you can insert to make the MTD partition(s) writable? I had this same problem and was able to work around it by writing a kernel module. For simplicity, I just went ahead and had it make every MTD device writable. Basically, the module has to do something like this in its init function:

struct mtd_info *mtd;
int x;
bool keep_going = true;

for (x = 0; keep_going; x++) {
mtd = get_mtd_device(NULL, x);
if (!IS_ERR(mtd)) {
mtd->flags |= MTD_WRITEABLE;
put_mtd_device(mtd);
} else {
keep_going = false;
}
}


After inserting this module, I was successfully able to use flash_erase and nandwrite while booted in the kernel, whereas before, flash_erase was telling me: "error 13 (Permission denied)"

• Here is a kernel modul to do just that: github.com/mwarning/mtdRW – mwarning Apr 23 '18 at 11:41
• Another option, if you can modify the kernel, is to expose the MTD_WRITEABLE flag via sysfs in mtdcore.c. That way you can toggle it as necessary to keep the MTD device write-protected. – remcycles Oct 10 '18 at 16:40

Another option is to use the fw_setenv utility (built with u-boot) to modify u-boot's saved environment variables from inside Linux. That would require a reboot to run with the new bootargs, unlike loading a kernel module, but that's much less work than using a serial cable. It's also a useful tool for other situations.

Documentation is here: https://elinux.org/U-boot_environment_variables_in_linux

The main thing to know is that fw_setenv and fw_printenv use a config file to know where u-boot stores it's environment in non-volatile storage, and it has to match what u-boot was compiled for.

• This could indeed be a good solution if U-Boot is using an external configuration and if the place where it is stored is writeable. Its not uncommon for default compiled in configuration to be used however, or for platform vendors to hack around the configuration system configuring instead in code... But if it works, it may be simplest. Ideally the configuration would then get set back to have the volume read only after changes are made. – Chris Stratton Oct 5 '18 at 19:35
• That is true. The available options are very specific to your device and hardware configuration. – remcycles Oct 10 '18 at 16:39

The usual solution is

mount / -o remount,rw

• It's not the file system that's read only, but rather the implementation of a block device on top of flash memory. – Chris Stratton May 2 '13 at 18:44