update_nand.php 8.2 KB
Newer Older
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
1
<?php
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/**
 * @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/>.
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
22
*/
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36

# 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
37

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
38
# e[3]s for flash_erase are now calculated from sysfs and depend on the device tree
39 40

# WARNING: TRY NOT TO CHANGE
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
41 42 43 44 45 46
$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", "",""),
47 48
  //array(0,"rootfs.ubi",    "/dev/mtd4","0 2048","-s 2048 -O 2048"),
  array(0,"rootfs.ubi",    "/dev/mtd4","0 2560","-s 2048 -O 2048"),
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
  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;
  
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
67 68
  $rootfs_uploaded = false;
  
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
  $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){
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97
        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>";
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
98 99 100 101 102 103 104 105 106 107 108 109 110
      }
    }
    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){
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
111 112 113
      if ($e[1]=="rootfs.ubi"){
        $rootfs_uploaded = true;
      }
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
114 115 116 117 118
      $tmp .= "<li>{$e[1]}</li>";
    }
    if ($v) printf("Files to be flashed:<ul>$tmp</ul>");
  }
  //$safe_list is ready
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
119
  if (is_dir($NAND_PATH)&&$rootfs_uploaded){
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
120
    backup_note();
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
121
    rootfs_warning_note();
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
122
  }
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
123 124 125 126 127
  if ($v) {
        backup_note();
        printf("<span style='color:green'>Ready for flashing.</span>");
  }
  
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
128 129 130
  return $safe_list;
}

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
131 132 133 134
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>");
} 

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
135 136 137 138 139 140 141 142 143
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/>");
}

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
// $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;

}

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
159 160 161 162
function nandflash($list){
  global $UPDATE_DIR;
  global $FLASH_LOG;
  global $FLASH_LOG_LINK;
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
163 164 165
  global $NAND_PATH;
  
  $rootfs_note = "";
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
166 167 168
  
  foreach($list as $e){
    if ($e[0]==0){
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
169 170
      if ($e[1]!="rootfs.ubi"){
        exec("flash_unlock ${e[2]} >> $FLASH_LOG");
171 172
        $flash_erase_args = get_flash_erase_args($e[2],$e[3]);
        exec("flash_erase ${e[2]} $flash_erase_args >> $FLASH_LOG");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
173
        exec("nandwrite -n ${e[2]} -p $UPDATE_DIR/${e[1]} >> $FLASH_LOG");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
174 175 176
      }else{
        if (!is_dir($NAND_PATH)) {
          exec("flash_unlock ${e[2]} >> $FLASH_LOG");
177 178
          $flash_erase_args = get_flash_erase_args($e[2],$e[3]);
          exec("flash_erase ${e[2]} $flash_erase_args >> $FLASH_LOG");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
179 180 181 182 183
          exec("ubiformat ${e[2]} -f $UPDATE_DIR/${e[1]} ${e[4]} >> $FLASH_LOG");
        }else{
          rootfs_warning_note();
        }
      }
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
184 185
    }
  }
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
186
  print("Success. See/Download <a href='$FLASH_LOG_LINK'>flash.log</a>. Then power cycle.");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
187 188 189 190 191 192 193 194 195 196 197 198 199
}

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");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
200 201 202
    if (is_dir("${UBI_MNT}${BKP_DIR}")){
      exec("tar -czvf var/$BKP_NAME -C ${UBI_MNT}${BKP_DIR} .");
    }
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
203 204 205 206 207 208 209
    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} .");
  }
210 211 212 213
}

function send_backup(){
  global $BKP_NAME;
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
214 215 216 217 218
  header("Content-Type: application/octet-stream");
  header('Content-Disposition: attachment; filename='.$BKP_NAME);
  print(file_get_contents("var/$BKP_NAME"));
}

219
function copy_backup(){
220 221 222 223 224 225 226 227 228
  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);
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
229
    exec("mount -t ubifs /dev/ubi0_0 $UBI_MNT");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
230
    /*
231
    if (!is_dir("$UBI_MNT${BKP_DIR}_bkp")) mkdir("$UBI_MNT${BKP_DIR}_bkp");
232
    exec("tar -C ${UBI_MNT}${BKP_DIR}_bkp -xzpf var/$BKP_NAME");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
233 234 235 236 237 238
    */
    
    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
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
239
    	exec("mkdir -p ${UBI_MNT}${BKP_DIR}");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
240
    	exec("tar -C ${UBI_MNT}${BKP_DIR} -xzpf var/$BKP_NAME");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
241 242
    	//take package versions from defaults
    	exec("cp ${UBI_MNT}${BKP_DIR}_defaults/packages/* ${UBI_MNT}${BKP_DIR}/packages/");
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
243 244 245 246
    }else{
    	exec("cp -r $UBI_MNT${BKP_DIR} $UBI_MNT${BKP_DIR}_defaults");
    }
    
247 248 249 250 251 252 253
    exec("sync");
    exec("umount $UBI_MNT");
    if (is_dir($UBI_MNT)) rmdir($UBI_MNT);
    exec("ubidetach /dev/ubi_ctrl -m 4");
  }  
}

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
254 255 256 257 258 259 260 261 262 263 264 265 266
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];

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
267 268 269
// 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");
  
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
270 271 272
switch($cmd){
  case "flash":
    $flash_list = verify(false);
273
    backup();
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
274
    nandflash($flash_list);
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
275
    if (isset($_GET['restore'])) copy_backup();
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
276 277 278
    break;
  case "backup":
    backup();
279
    send_backup();
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
280
    break;
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
281 282 283
  case "restore":
  	copy_backup();
  	break;
Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
284 285 286 287 288 289 290
  case "remove":
    remove();
    break;
  default:
    verify(true);
}

Oleg Dzhimiev's avatar
Oleg Dzhimiev committed
291
?>