| 781 | |
| 782 | == Installing Large Disk Images on Systems with Limited RAM |
| 783 | |
| 784 | In certain scenarios, devices with limited RAM face challenges when updating firmware or system images stored on MMC flash storage. Attempting to install large disk images directly may fail due to memory constraints. To address this limitation, a practical approach involves splitting the disk image into smaller chunks and updating the device incrementally. This wiki section documents a solution for how to install these larger than RAM disk images inside of u-boot; on Gateworks SBCs. |
| 785 | |
| 786 | === Spliting the Disk Image Into Segments |
| 787 | Before proceeding with the update process, the disk image needs to be split into smaller parts. This can be achieved using the Linux's split command, making sure to decompress the image if it's compressed. The key requirement for the process outlined here is that the file must be divided into segments that are evenly divisible by 512-byte blocks. This ensures that the subsequent calculation of block count, as demonstrated in the script in the following section. Here are the steps to split our BSP disk image as an example: |
| 788 | {{{#!bash |
| 789 | # If the image is compressed, extract it first |
| 790 | gunzip jammy-venice.img.gz |
| 791 | |
| 792 | # Split the image into parts of 500MB each |
| 793 | split -d -b 500M jammy-venice.img jammy-venice.img.part |
| 794 | }}} |
| 795 | |
| 796 | === Downloading and Installing the Segments |
| 797 | To handle the segmented installation of the disk image, we will utilize a U-Boot script specifically designed for devices with limited RAM. This script orchestrates the sequential installation of each segmented portion of the disk image. Here's how to create and use the U-Boot script: |
| 798 | |
| 799 | {{{#!bash |
| 800 | cat <<\EOF> storage_split_update |
| 801 | setenv storage_split_update 'if itest.s x == "x${splitfile}"; then |
| 802 | # Check if the ${splitfile} environment variable is set |
| 803 | echo "The following environment variables need to be set in order for this script to run:" |
| 804 | echo " splitfile - full path file name prefix of the splitted files (eg file.img for file.img.part00)" |
| 805 | exit 1 |
| 806 | fi |
| 807 | |
| 808 | # Attempt to fetch the ${splitfile} to update storage |
| 809 | echo "Attempting to fetch ${splitfile} to update storage" |
| 810 | setenv 0 0 |
| 811 | setexpr i 0 |
| 812 | setexpr offset 0 |
| 813 | |
| 814 | # Iterate over segments to write to Storage |
| 815 | while itest $i -le 99; do |
| 816 | # Add leading zero to $i if necessary |
| 817 | itest $i -gt 9 && setenv 0 |
| 818 | |
| 819 | if tftpboot $loadaddr ${splitfile}.part${0}${i}; then |
| 820 | # Calculate block count |
| 821 | setexpr blkcnt $filesize + 0x1ff |
| 822 | setexpr blkcnt $blkcnt / 0x200 |
| 823 | |
| 824 | # Write segment to Storage |
| 825 | if ${iface} write ${loadaddr} ${offset} ${blkcnt}; then |
| 826 | echo "Successfully wrote segment ${i} of ${filesize} bytes" |
| 827 | setexpr offset ${offset} + ${blkcnt} |
| 828 | setexpr i ${i} + 1 |
| 829 | setexpr rem ${i} % 0x10 |
| 830 | itest ${rem} -eq 0x0a && setexpr i ${i} + 6 |
| 831 | else |
| 832 | echo "Error writing segment ${i} of ${filesize} bytes" |
| 833 | exit 1 |
| 834 | fi |
| 835 | elif itest ${i} -eq 0; then |
| 836 | echo "ERROR: image file ${splitfile}.part${0}${i} not found or it is too big to fit in memory" |
| 837 | exit 1 |
| 838 | else |
| 839 | echo "SUCCESS: ${splitfile} was successfully written to ${iface} storage device" |
| 840 | exit 0 |
| 841 | fi |
| 842 | done' |
| 843 | EOF |
| 844 | }}} |
| 845 | |
| 846 | U-Boot requires scripts to be in a specific binary format for execution. To compile the script into this format, we use the mkimage tool, which is provided as part of the U-Boot distribution. |
| 847 | {{{#!bash |
| 848 | mkimage -A arm64 -T script -C none -d storage_split_update ustorage_split_update |
| 849 | }}} |
| 850 | |
| 851 | |
| 852 | Once you have the u-boot source-able script and your split image segments saved to your tftp server; you can now install your large disk Image onto your target board. |
| 853 | {{{#!bash |
| 854 | cp storage_split_update your_tftp_server |
| 855 | |
| 856 | cp jammy-venice.img.part* your_tftp_server |
| 857 | }}} |
| 858 | |
| 859 | === Installing |
| 860 | Before running the update script, you need to download it from your tftp server to your target board, and you need to specify the name of the split image file that you want to use. |
| 861 | {{{#!bash |
| 862 | #Set your tftp server IP |
| 863 | u-boot=> dhcp |
| 864 | u-boot=> setenv serverip <YOUR_SERVER_IP> |
| 865 | #Load the script, being sure to use full path name |
| 866 | u-boot=> tftpboot /venice/ustorage_split_update |
| 867 | u-boot=> source $loadaddr |
| 868 | #Run the script & install the segments |
| 869 | u-boot=> setenv splitfile /venice/jammy-venice.img |
| 870 | }}} |
| 871 | Depending on which storage device you want to write your Image to, you need to set the device accordingly then you can run the script. Here is the general use as well as a specific use case: |
| 872 | |
| 873 | **For MMC: |
| 874 | {{{#!bash |
| 875 | #mmc dev <device#> <hardware_partition#> |
| 876 | u-boot=> mmc dev 2 0 |
| 877 | u-boot=> setenv iface mmc |
| 878 | }}} |
| 879 | **For USB: |
| 880 | {{{#!bash |
| 881 | #usb dev <device#> <hardware_partition#> |
| 882 | u-boot=> usb stop && usb start && usb storage |
| 883 | u-boot=> usb dev 0 0 |
| 884 | u-boot=> setenv iface usb |
| 885 | }}} |
| 886 | **For NVMe: |
| 887 | {{{#!bash |
| 888 | #nvme dev <device#> |
| 889 | u-boot=> pci enum && nvme scan |
| 890 | u-boot=> nvme dev 0 |
| 891 | u-boot=> setenv iface nvme |
| 892 | }}} |
| 893 | To initiate the instillation after you have properly selected the storage device your want: |
| 894 | {{{#!bash |
| 895 | run storage_split_update |
| 896 | }}} |
| 897 | |
| 898 | Once the image download and installation process is complete, you can proceed to use the board as usual. |
| 899 | {{{#!bash |
| 900 | u-boot=> boot |
| 901 | }}} |