| 280 | |
| 281 | |
| 282 | [=#swupdate] |
| 283 | == SWUpdate |
| 284 | SWUpdate is a framework for providing firmware udpates. It is extremely flexible and provides support for many different scenarios. Because it exists as a package for buildroot it is a great choice for providing firmware updates when using a buildroot solution. |
| 285 | |
| 286 | Here we will provide an example of building an SWUpdate Over-The-Air (OTA) update for buildroot with the following considerations: |
| 287 | - Newport |
| 288 | - Symmetric Image Update (Two copies of rootfs which works well when your filesystem is relatively small compared to memory/flash space) |
| 289 | - We will not bother updating boot firmware (can be added later) |
| 290 | - We will not bother updating GSC firmware (can be added later) |
| 291 | - We will build the Gateworks 4.14 kernel for newport with a minimal kernel config |
| 292 | - We will use an uncompressed kernel image (just avoids needing to kernel a kernel.itb) |
| 293 | - We will use a modified uboot environment to handle our root partition toggling |
| 294 | |
| 295 | Here are the relevant files to add to your buildroot directory: |
| 296 | * '''configs/newport_swupdate_defconfig''': Buildroot defconfig (represents the minimal 'changes' made to buildroot default config): |
| 297 | {{{#!bash |
| 298 | BR2_aarch64=y |
| 299 | BR2_KERNEL_HEADERS_4_14=y |
| 300 | |
| 301 | # we will add some files to the rootfs from the 'overlay' subdir |
| 302 | BR2_ROOTFS_OVERLAY="overlay" |
| 303 | |
| 304 | # |
| 305 | # Kernel: |
| 306 | # we will build the gateworks linux kernel using arm64 defconfig |
| 307 | # plus some additional configs via newport_kernel_defconfig |
| 308 | BR2_LINUX_KERNEL=y |
| 309 | BR2_LINUX_KERNEL_CUSTOM_GIT=y |
| 310 | BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-newport.git" |
| 311 | BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v4.14.4-newport" |
| 312 | BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y |
| 313 | BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="newport_kernel_defconfig" |
| 314 | BR2_LINUX_KERNEL_INSTALL_TARGET=y |
| 315 | |
| 316 | # |
| 317 | # Packages: |
| 318 | # we need u-boot env tools for fw_setenv support in our scripts |
| 319 | # we need zlib for gzip support in SWUpdate |
| 320 | # we will use swupdate.config to configure SWUpdate |
| 321 | BR2_PACKAGE_UBOOT_TOOLS=y |
| 322 | BR2_PACKAGE_ZLIB=y |
| 323 | BR2_PACKAGE_SWUPDATE=y |
| 324 | BR2_PACKAGE_SWUPDATE_CONFIG="swupdate.config" |
| 325 | }}} |
| 326 | - Note that SWUpdate reuqires ZLIB if we are going to use gzip compression |
| 327 | * '''newport_kernel_defconfig''': Kernel config fragment that adds OcteonTX and newport drivers to arm64 kernel defconfig |
| 328 | {{{#!bash |
| 329 | # Additionally OcteonTX peripheral drivers |
| 330 | CONFIG_PCI_HOST_THUNDER_PEM=y |
| 331 | CONFIG_PCI_HOST_THUNDER_ECAM=y |
| 332 | CONFIG_CAN=y |
| 333 | CONFIG_CAN_MCP251X=y |
| 334 | CONFIG_THUNDER_NIC_PF=y |
| 335 | CONFIG_THUNDER_NIC_VF=y |
| 336 | CONFIG_MDIO_BITBANG=y |
| 337 | CONFIG_I2C=y |
| 338 | CONFIG_I2C_THUNDERX=y |
| 339 | CONFIG_SPI_THUNDERX=y |
| 340 | CONFIG_GPIO_THUNDERX=y |
| 341 | CONFIG_MMC_CAVIUM_THUNDERX=y |
| 342 | CONFIG_EDAC_THUNDERX=y |
| 343 | CONFIG_EXT4_FS=y |
| 344 | # Gateworks Newport GSC drivers |
| 345 | CONFIG_SENSORS_GSC=y |
| 346 | CONFIG_MFD_GSC=y |
| 347 | }}} |
| 348 | * '''overlay/etc/fw_env.config''': config file for u-boot env tools fw_setenv |
| 349 | {{{#!bash |
| 350 | # Device offset size |
| 351 | /dev/mmcblk0 0xff0000 0x8000 |
| 352 | /dev/mmcblk0 0xff8000 0x8000 |
| 353 | }}} |
| 354 | - Note that this file is whitespace sensitive |
| 355 | * '''swupdate.config''': config file to build swupdate executable which resides on your firmware and drives the update process |
| 356 | {{{#!bash |
| 357 | # We do not need MTD or LUA support |
| 358 | # CONFIG_MTD is not set |
| 359 | # CONFIG_LUA is not set |
| 360 | # We need the raw handler to image to an MMC partition |
| 361 | CONFIG_RAW=y |
| 362 | # We need the shellscript handler for our update.sh shellscript |
| 363 | CONFIG_SHELLSCRIPTHANDLER=y |
| 364 | # We need the bootloader handler to alter the u-boot environment |
| 365 | CONFIG_BOOTLOADERHANDLER=y |
| 366 | }}} |
| 367 | * '''sw-description''': part of the actual firmware OTA which describes the process and file manifest of the update image. See [https://sbabic.github.io/swupdate/sw-description.html# here] for syntax |
| 368 | {{{#!bash |
| 369 | software = |
| 370 | { |
| 371 | version = "0.1.0"; |
| 372 | description = "Firmware update for XXXXX Project"; |
| 373 | |
| 374 | /* images installed to the system */ |
| 375 | images: ( |
| 376 | { |
| 377 | filename = "rootfs.ext4.gz"; |
| 378 | device = "/dev/update"; |
| 379 | type = "raw"; |
| 380 | compressed = true; |
| 381 | } |
| 382 | ); |
| 383 | |
| 384 | scripts: ( |
| 385 | { |
| 386 | filename = "update.sh"; |
| 387 | type = "shellscript"; |
| 388 | } |
| 389 | ); |
| 390 | } |
| 391 | }}} |
| 392 | - '''update.sh''': This is the script that SWUpdate runs which we use as both a preinst and psotinst script (via cmdline). We determine the current root device and, flip it, and symlink /dev/update to the device to update to. We don't have to do the image install as we've configured SWUpdate to do that for us in sw-descrption images. |
| 393 | {{{#!bash |
| 394 | #!/bin/sh |
| 395 | |
| 396 | if [ $# -lt 1 ]; then |
| 397 | exit 0; |
| 398 | fi |
| 399 | |
| 400 | function get_current_root_device |
| 401 | { |
| 402 | for i in `cat /proc/cmdline`; do |
| 403 | if [ ${i:0:5} = "root=" ]; then |
| 404 | CURRENT_ROOT="${i:5}" |
| 405 | fi |
| 406 | done |
| 407 | } |
| 408 | |
| 409 | # ping-pong between /dev/mmcblk0p2 and /dev/mmcblk0p3 |
| 410 | # (adapt for your partitioning scheme and/or root device type) |
| 411 | function get_update_part |
| 412 | { |
| 413 | CURRENT_PART="${CURRENT_ROOT: -/dev/mmcblk0p2}" |
| 414 | if [ $CURRENT_PART = "/dev/mmcblk0p2" ]; then |
| 415 | UPDATE_PART="3"; |
| 416 | else |
| 417 | UPDATE_PART="2"; |
| 418 | fi |
| 419 | } |
| 420 | |
| 421 | function get_update_device |
| 422 | { |
| 423 | UPDATE_ROOT=${CURRENT_ROOT%?}${UPDATE_PART} |
| 424 | } |
| 425 | |
| 426 | |
| 427 | if [ $1 == "preinst" ]; then |
| 428 | # get the current root device |
| 429 | get_current_root_device |
| 430 | |
| 431 | # get the device to be updated |
| 432 | get_update_part |
| 433 | get_update_device |
| 434 | |
| 435 | # create a symlink for the update process |
| 436 | ln -sf $UPDATE_ROOT /dev/update |
| 437 | fi |
| 438 | |
| 439 | if [ $1 == "postinst" ]; then |
| 440 | # get the current root device |
| 441 | get_current_root_device |
| 442 | |
| 443 | # get the device to be updated |
| 444 | get_update_part |
| 445 | get_update_device |
| 446 | |
| 447 | # toggle rootpart between 2 and 3 |
| 448 | # we do it twice to write to both primary/secondary env |
| 449 | fw_setenv mmcbootpart $UPDATE_PART |
| 450 | fw_setenv mmcbootpart $UPDATE_PART |
| 451 | fi |
| 452 | }}} |
| 453 | |
| 454 | Once this is in place you can use the following to build: |
| 455 | {{{#!bash |
| 456 | # build buildroot rootfs |
| 457 | make newport_swupdate_defconfig |
| 458 | make |
| 459 | |
| 460 | # build swupdate image |
| 461 | cp output/images/rootfs.ext4.gz . |
| 462 | for i in sw-description update.sh rootfs.ext4.gz; do |
| 463 | echo $i; done | cpio -ov -H crc > my-software.swu |
| 464 | }}} |
| 465 | |
| 466 | Here are some one-time steps you will need to do to your boot firmware: |
| 467 | * create an MBR partition table that defines LinuxA and LinuxB partitions we will ping-pong between. Note that the FATFS must not be changed from the original boot firmware generated MBR and that we also create a general userdata partition. |
| 468 | {{{#!bash |
| 469 | # 1: 2048:30720 (15MiB) FAT12 (required by boot firmware) |
| 470 | # 2: 65536:4194304 (2GiB) LinuxA |
| 471 | # 3: 4259840:4194304 (2GiB) LinuxB |
| 472 | # 4: 19103744:4194304 (2GiB) userdata |
| 473 | wget -q https://raw.githubusercontent.com/Gateworks/bsp-newport/sdk-10.1.1.0-newport/ptgen |
| 474 | /bin/bash ptgen \ |
| 475 | -p 0x01:2048:30720 \ |
| 476 | -p 0x83:65536:4194304 \ |
| 477 | -p 0x83:4259840:4194304 \ |
| 478 | -p 0x83:19103744:4194304 \ |
| 479 | > $BUILDROOT/output/images/mbr.bin |
| 480 | }}} |
| 481 | * In U-Boot we will update the partition table: |
| 482 | {{{#!bash |
| 483 | tftpboot ${loadaddr} mbr.bin && mmc dev 0 && mmc write ${loadaddr} 0 1 # mbr is at 0 and 1 sector long |
| 484 | }}} |
| 485 | - Note that if you ever update the entire boot firmware it will over-write this partition table so you will want to take care to not overwrite that portion |
| 486 | * In U-Boot we will install the original rootfs to the first Linux partition offset (LinuxA) |
| 487 | {{{#!bash |
| 488 | tftpboot ${loadaddr} rootfs.ext4.gz && gzwrite mmc 0 ${loadaddr} ${filesize} 0x100000 0x2000000 # rootfsA is at 0x2000000 (64MiB) and we use a 1MiB buffer |
| 489 | }}} |
| 490 | * In U-Boot we will alter the env to use the '''mmcbootpart''' env variable that our update.sh manipulates after a successful update: |
| 491 | {{{#!bash |
| 492 | setenv mmcbootpart 2 |
| 493 | setenv bootcmd "setenv bootargs 'console=${console} root=/dev/mmcblk0p${mmcbootpart} rootwait rw; load mmc 0:${mmcbootpart} ${kernel_addr_r} boot/Image' && booti ${kernel_addr_r} - ${fdtcontroladdr}" |
| 494 | saveenv |
| 495 | }}} |
| 496 | - Note the single quotes around the bootargs value as we do not want U-Boot to expand the args until runtime |
| 497 | |
| 498 | After you boot to buildroot you can fetch and install the SWUpdate image with: |
| 499 | {{{#!bash |
| 500 | # bring up networking |
| 501 | udhcpc -i eth0 |
| 502 | # fetch image |
| 503 | cd /tmp |
| 504 | wget http://myserver/my-software.swu |
| 505 | swupdate -i mysoftware.swu |
| 506 | }}} |
| 507 | |
| 508 | Note that if you require support for SWUpdate to complete an install that isn't already there (for example you want to add the capability to update GSC firmware via the gsc_update utility) you will either need to a) add a static linked version of that tool to your image or b) do a 2-stage update where you add the required tools first, then use them in a future update |