<?php /** * @file update_software.php * @brief update software on nand flash * @version 1.0 * @copyright Copyright (C) 2016 Elphel Inc. * @author Oleg Dzhimiev <oleg@elphel.com> * * @par <b>License</b>: * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ # hardcoded $UPDATE_DIR = "/var/volatile/html/update"; $NAND_PATH = "/tmp/rootfs.ro"; $FLASH_LOG = "/var/volatile/html/flash.log"; $FLASH_LOG_LINK = "var/flash.log"; $UBI_MNT = "/tmp/ubi0"; $BKP_NAME = "elphel393_backup.tar.gz"; $BKP_DIR = "/etc/elphel393"; # update files # file, expertise level, nand partition, size - see http://wiki.elphel.com/index.php?title=NAND_flash_boot_rootfs # partitions are also listed in the device tree # e[3]s for flash_erase are now calculated from sysfs and depend on the device tree # WARNING: TRY NOT TO CHANGE $UPDATE_LIST = array( array(0,"boot.bin", "/dev/mtd0","0 2"), array(0,"u-boot-dtb.img","/dev/mtd1","0 8"), array(0,"devicetree.dtb","/dev/mtd2","0 8"), array(0,"uImage", "/dev/mtd3","0 128"), array(1,"rootfs.tar.gz", "",""), //array(0,"rootfs.ubi", "/dev/mtd4","0 2048","-s 2048 -O 2048"), array(0,"rootfs.ubi", "/dev/mtd4","0 2560","-s 512 -O 512"), array(1,"rootfs.ubifs", "/dev/mtd4","/dev/ubi_ctrl -m 4","ubiupdatevol /dev/ubi0_0"), ); //respond_xml($updatedir); function lookup_name($name){ global $UPDATE_LIST; foreach($UPDATE_LIST as $e){ if ($name==$e[1]) return $e; } return false; } function verify($v){ global $UPDATE_DIR; global $NAND_PATH; global $UPDATE_LIST; $rootfs_uploaded = false; $safe_list = array(); if (is_dir($UPDATE_DIR)){ foreach(scandir($UPDATE_DIR) as $e){ $tmp = lookup_name($e); if ($tmp!=false){ if ($tmp[0]==0){ array_push($safe_list,$tmp); } } } } if (count($safe_list)==0){ $tmp = ""; foreach($UPDATE_LIST as $e){ if($e[0]==0){ switch($e[1]){ case "boot.bin": case "u-boot-dtb.img": case "devicetree.dtb": $comment = "(rarely updated)"; break; case "uImage": case "rootfs.ubi": $comment = "(frequently updated)"; break; default: $comment = ""; } $tmp .= "<li><b>${e[1]}</b> <span style='font-size:0.9em;'>${comment}</span></li>"; } } backup_note(); $msg = <<<TXT <b style='color:red;'>ERROR</b>: Files not found. Accepted files are: <ul> $tmp </ul> TXT; die($msg); }else{ $tmp = ""; foreach($safe_list as $e){ if ($e[1]=="rootfs.ubi"){ $rootfs_uploaded = true; } $tmp .= "<li>{$e[1]}</li>"; } if ($v) printf("Files to be flashed:<ul>$tmp</ul>"); } //$safe_list is ready if (is_dir($NAND_PATH)&&$rootfs_uploaded){ backup_note(); rootfs_warning_note(); } if ($v) { backup_note(); printf("<span style='color:green'>Ready for flashing.</span>"); } return $safe_list; } function rootfs_warning_note(){ printf("<b style='color:orange'>Warning</b>: To update rootfs, please boot from mmc (<a href='http://wiki.elphel.com/index.php?title=Tmp_manual#Boot'>instructions</a>). Will skip <b>rootfs.ubi</b>"); } function backup_note(){ $tmp = strrpos($_SERVER['SCRIPT_NAME'],"/"); if ($tmp==0) $base = $_SERVER['SCRIPT_NAME']; else $base = substr($_SERVER['SCRIPT_NAME'],0,$tmp+1); print("<b>NOTE</b>: If flashing rootfs, please download a backup copy of <a href='$base?cmd=backup'>/etc/elphel393</a><br/>"); } // $defaults are not used yet. No need function get_flash_erase_args($dev, $defaults){ $mtd_device = explode("/",$dev)[2]; $mtd_sysfs_path = "/sys/class/mtd/$mtd_device"; $mtd_size = intval(file_get_contents("$mtd_sysfs_path/size")); $mtd_erasesize = intval(file_get_contents("$mtd_sysfs_path/erasesize")); $mtd_size_blocks = intval($mtd_size/$mtd_erasesize); $mtd_erase_start = 0; $res = "$mtd_erase_start $mtd_size_blocks"; return $res; } function nandflash($list){ global $UPDATE_DIR; global $FLASH_LOG; global $FLASH_LOG_LINK; global $NAND_PATH; $rootfs_note = ""; foreach($list as $e){ if ($e[0]==0){ if ($e[1]!="rootfs.ubi"){ exec("flash_unlock ${e[2]} >> $FLASH_LOG"); $flash_erase_args = get_flash_erase_args($e[2],$e[3]); exec("flash_erase ${e[2]} $flash_erase_args >> $FLASH_LOG"); // -n - write without ecc //exec("nandwrite -n ${e[2]} -p $UPDATE_DIR/${e[1]} >> $FLASH_LOG"); exec("nandwrite ${e[2]} -p $UPDATE_DIR/${e[1]} >> $FLASH_LOG"); }else{ if (!is_dir($NAND_PATH)) { exec("flash_unlock ${e[2]} >> $FLASH_LOG"); $flash_erase_args = get_flash_erase_args($e[2],$e[3]); exec("flash_erase ${e[2]} $flash_erase_args >> $FLASH_LOG"); exec("ubiformat ${e[2]} -f $UPDATE_DIR/${e[1]} ${e[4]} >> $FLASH_LOG"); }else{ rootfs_warning_note(); } } } } print("Success. See/Download <a href='$FLASH_LOG_LINK'>flash.log</a>. Then power cycle."); } function backup(){ global $NAND_PATH; global $UBI_MNT; global $BKP_NAME; global $BKP_DIR; if (!is_dir($NAND_PATH)){ exec("flash_unlock /dev/mtd4"); exec("ubiattach /dev/ubi_ctrl -m 4"); if (!is_dir($UBI_MNT)) mkdir($UBI_MNT); exec("mount -t ubifs -o ro /dev/ubi0_0 $UBI_MNT"); if (is_dir("${UBI_MNT}${BKP_DIR}")){ exec("tar -czvf var/$BKP_NAME -C ${UBI_MNT}${BKP_DIR} ."); } exec("umount $UBI_MNT"); if (is_dir($UBI_MNT)) rmdir($UBI_MNT); exec("ubidetach /dev/ubi_ctrl -m 4"); }else{ //booted from nand exec("tar -czvf var/$BKP_NAME -C ${BKP_DIR} ."); } } function send_backup(){ global $BKP_NAME; header("Content-Type: application/octet-stream"); header('Content-Disposition: attachment; filename='.$BKP_NAME); print(file_get_contents("var/$BKP_NAME")); } function copy_backup(){ global $NAND_PATH; global $UBI_MNT; global $BKP_NAME; global $BKP_DIR; if (!is_dir($NAND_PATH)){ exec("flash_unlock /dev/mtd4"); exec("ubiattach /dev/ubi_ctrl -m 4"); if (!is_dir($UBI_MNT)) mkdir($UBI_MNT); exec("mount -t ubifs /dev/ubi0_0 $UBI_MNT"); /* if (!is_dir("$UBI_MNT${BKP_DIR}_bkp")) mkdir("$UBI_MNT${BKP_DIR}_bkp"); exec("tar -C ${UBI_MNT}${BKP_DIR}_bkp -xzpf var/$BKP_NAME"); */ if (is_file("var/$BKP_NAME")){ if (is_dir("$UBI_MNT${BKP_DIR}_defaults")) rmdir("$UBI_MNT${BKP_DIR}_defaults"); rename("$UBI_MNT${BKP_DIR}","$UBI_MNT${BKP_DIR}_defaults"); //restore old configs exec("mkdir -p ${UBI_MNT}${BKP_DIR}"); exec("tar -C ${UBI_MNT}${BKP_DIR} -xzpf var/$BKP_NAME"); //take package versions from defaults exec("cp ${UBI_MNT}${BKP_DIR}_defaults/packages/* ${UBI_MNT}${BKP_DIR}/packages/"); }else{ exec("cp -r $UBI_MNT${BKP_DIR} $UBI_MNT${BKP_DIR}_defaults"); } exec("sync"); exec("umount $UBI_MNT"); if (is_dir($UBI_MNT)) rmdir($UBI_MNT); exec("ubidetach /dev/ubi_ctrl -m 4"); } } function remove(){ global $UPDATE_DIR; exec("rm -rf $UPDATE_DIR/*; sync"); backup_note(); print("<b>NOTE</b>: All files have been removed from <b>$UPDATE_DIR</b>."); } $cmd = "donothing"; if (isset($_GET['cmd'])) $cmd = $_GET['cmd']; else if (isset($argv[1])) $cmd = $argv[1]; // default Content-Type is text/html - LibreJS (if turned on) can add extra tags: <html><head></head><body>response</body></html> header("Content-Type: text/plain"); switch($cmd){ case "flash": $flash_list = verify(false); backup(); nandflash($flash_list); if (isset($_GET['restore'])) copy_backup(); break; case "backup": backup(); send_backup(); break; case "restore": copy_backup(); break; case "remove": remove(); break; default: verify(true); } ?>