==================================================== 1. RAUC Overview ==================================================== RAUC (Robust Auto-Update Controller) is a lightweight, open-source framework designed for secure and reliable software updates in embedded Linux systems. It supports A/B update schemes, which provide redundancy by maintaining two copies of the root filesystem, allowing rollback in case of a failed update. RAUC ensures atomic updates, meaning that the update process is either fully completed or rolled back without leaving the system in a partial state. It uses cryptographic signatures to verify the integrity and authenticity of update bundles, making it suitable for critical systems where robustness and security are paramount. In CCLinux 4.x RAUC is used for handling the bootloader, system and application updates. Both USB updates and OTA updates are supported. This documentation aims to cover the most common use-cases when using RAUC with CCLinux, but for more details please also look at the complete documentation at: https://rauc.readthedocs.io/en/latest/index.html ==================================================== 2. Changes between CC Linux 3 and CC Linux 4 ==================================================== In earlier versions of CCLinux, the file system was designed with a main and a rescue partition: .. code-block:: bash CC Linux 3 +-------------------+ | Boot | +-------------------+ | Rescue | | (160 MB) | +-------------------+ | Main | | (1 GB) | +-------------------+ | Data | | (6 GB) | +-------------------+ With CC Linux 4, a new partition scheme is introduced, that makes the system designed for secure and reliable software updates. With an A/B setup for both root file system and A/B for application partition, this will change the method for updates (partition sizes can be different depending on available flash size configurations and hardware). .. code-block:: bash CC Linux 4 +-------------------+ | Boot A | +-------------------+ | Boot B | +-------------------+ | OS A | | (1.3 GB) | +-------------------+ | OS B | | (1.3 GB) | +-------------------+ | App A | | (1.3 GB) | +-------------------+ | App B | | (1.3 GB) | +-------------------+ | Data | | (2.7+ GB) | +-------------------+ | |--> Available for "Data", depending on flash size in the unit: - Docker containers - Temporary storage of files - Application log files - Application Configuration files This partition layout enables OS A and its corresponding application partition (appfs A) to be active, while OS B and appfs B is inactive. Since the rootfs partition is the parent of the appfs, switching to a different appfs also requires changing the active rootfs to match. .. code-block:: bash +-------+-------+ | rootfs| rootfs| | A | B | +-------+-------+ | appfs | appfs | | A | B | +-------+-------+ A new update is written to the inactive partition, allowing it to be performed on a running system without disrupting normal operation. If the update fails, the system remains fully functional, and the update can be retried. ==================================================== 3. Read-only file systems ==================================================== In previous versions of CCLinux, there was one OS filesystem. It was read-only with an overlay filesystem (OverlayFS) applied, making it possible to write, delete or modify any system file in the OS. Custom application data was placed in /opt folder where the default user 'ccs' had write access. For CC Linux 4, this configuration has changed. Both the root file system and the application partition are now read-only, and can only be modified with RAUC updates. This change is needed for the atomic update process, and also required by other RAUC functionality. The only location that is writable by the application or ccs user is /data partition. This is the location for temporary or permanent data, where the application can log, and is also used for the /etc overlay to be able to modify certain config files. ==================================================== 4. Update procedure ==================================================== A system using RAUC can be updated from a RAUC bundle or flashed using uuu. A RAUC bundle is a compressed filesystem containing the software update. It typically includes the system images (e.g., root filesystem, bootloader, application data) and metadata such as cryptographic signatures for integrity and authenticity verification. Bundles are signed and verified by RAUC to ensure secure delivery and application of the update. These bundles are applied atomically, meaning the update either fully succeeds or the system reverts to its previous state, preventing partial or corrupted installations. CrossControl will deliver update bundles containing: * Bootloader * Root filesystem * Application filesystem * OS Update (bootloader + root filesystem) * System update (OS Update + Application update) ==================================================== 5. Signature ==================================================== RAUC uses the **OpenSSL** library for cryptographic signing to ensure the integrity and authenticity of update bundles. When creating a bundle, a private key is used to sign the bundle, generating a cryptographic signature. This signature is included in the bundle's metadata. On the CCPilot display, RAUC uses the corresponding public key, stored in a certificate at /data/rauc/certs/ folder, to verify the signature before applying the update. If the signature is valid, it ensures: * Integrity: The bundle has not been tampered with or corrupted during transmission. * Authenticity: The update came from a trusted source (i.e., the entity that holds the private key). If the verification fails, RAUC will reject the bundle, preventing the installation of potentially harmful or unauthorized software. This mechanism helps to secure the update process. ==================================================== 6. CrossControl Demo keys ==================================================== For testing purposes, CCLinux 4.x includes a pre-installed "demo key". This public/private key pair is provided for developers and is used to generate the demo certificate that comes with the default OS installation. While the demo key can be used to create update bundles for installing application software via RAUC, it is crucial to replace it with a custom key and certificate in production environments. Since the demo key is publicly accessible, using it in production poses a security risk. A unique, customer-generated key and certificate should be used to ensure system security. The BSP has the demo keys used in the factory build located in */cclinux-bsp/meta-cc/meta-PLATFORM/recipes-core/rauc/*. As an example, BSPs bundle recipes located in *meta-cc/meta-PLATFORM/recipes-core/bundles/* define the demo key locations as follows: .. code-block:: RAUC_KEY_FILE = "${OEROOT}/../meta-cc/meta-bsp-v700/recipes-core/rauc/files/development-1.key.pem" RAUC_CERT_FILE = "${OEROOT}/../meta-cc/meta-bsp-v700/recipes-core/rauc/files/development-1.cert.pem" The demo certificate is also included in the CC Linux SDK, in the /etc/rauc target sysroot folder. As an example with default SDK installation location: .. code-block:: bash /opt/cclinux/4.1/sysroots/cortexa35-poky-linux/etc/rauc/ca.cert.pem The developer.key and developer.cert is needed to sign the bundle. The files are available further down in this document. A more detailed example of how to create update bundles is available in the "Update bundle creation" section. .. note:: CrossControl Demo keys have been updated in CCLinux 4.2. If updating development devices using demo keys from 4.0 or 4.1, please use uuu instead to update the device. ==================================================== 7. Update bundle explained ==================================================== A RAUC bundle consists of the file system(s) to be installed, a manifest that lists the images to install and contains options and meta-information, and possible scripts to run before, during or after installation. A bundle may also contain files not referenced in the manifest, such as scripts or archives that are referenced by files that are included in the manifest. To pack this together, these contents are added into a SquashFS image. This provides good compression while allowing the bundle to be mounted without having to unpack it on the target system. This eliminates the need for intermediate storage. Update bundles can have three different formats: plain, verity and crypt, with different supported features: --------------------------------------- 7.1. Plain --------------------------------------- * The original and simplest format. * No built-in security or integrity measures. * Vulnerable to tampering and corruption. * NOT supported in CC Linux 4 due to low security. --------------------------------------- 7.2. Verity --------------------------------------- * Adds integrity protection using Linux's dm-verity module.   * Verifies the integrity of the bundle's payload before installation.   * Protects against unauthorized modifications and ensures the bundle's authenticity.   --------------------------------------- 7.3. Crypt --------------------------------------- * Builds upon the verity format by adding encryption.   * Encrypts the bundle's payload using symmetric encryption.   * The encryption key is stored in the manifest, which is itself encrypted asymmetrically.   * Provides confidentiality in addition to integrity protection. --------------------------------------- 7.4. Hooks --------------------------------------- RAUC hooks are custom scripts or commands that can be executed at various points during the update process, providing flexibility for handling specific tasks before, during, or after an update. These hooks allow for the behavior of RAUC to be customized beyond its default operations, making it adaptable to a wide range of system requirements. Hooks are defined in the update bundle and must be specified in the bundle’s Manifest file. All hooks are handled by a common executable that must be included in the bundle. Hooks allow the author of a bundle to add or replace functionality for the installation of a specific bundle. This can be useful for performing additional migration steps, checking for specific previously installed bundle versions or for manually handling updates of images RAUC cannot handle natively. Common usage includes Install Hooks, for example the "pre-install" or "post-install" hook. See RAUC documentation for a complete guide: https://rauc.readthedocs.io/en/latest/using.html?highlight=slot%20hooks#slot-hooks ==================================================== 8. Rootfs + Appfs in one bundle ==================================================== Currently, RAUC has no way to ensure compatibility between rootfs and appfs when installing a bundle containing only an image for one of them. Either always build bundles containing images for all required slots or ensure that incompatible updates are not installed outside of RAUC. To solve this, a bundle would need to contain the metadata (size and hash) for the missing bundle and RAUC would need to verify the state of those slots before installing the bundle. Therefore, it is not recommended to update only appfs in a bundle. This will not work as expected, because according to the RAUC slot configuration in /etc/rauc/system.conf, rootfs.0 (the A configuration) is the parent of appfs.0. RAUC writes updates to the inactive slots, so if you update only the application part, the new appfs will be written to the inactive slot. For the updated appfs to be used, the system must also switch to the corresponding inactive rootfs slot. .. code-block:: bash [slot.rootfs.0] device=/dev/mmcblk1p2 type=ext4 bootname=A [slot.appfs.0] device=/dev/mmcblk1p4 type=ext4 parent=rootfs.0 ... The easiest way to ensure this is to always include the rootfs image that your application has been tested with into the bundle, and this is also the approach recommended by RAUC. This ensures the update package works and RAUC can check each slot before downloading and writing so as to not overwrite identical images (if the latest version of for example rootfs is already installed in the target slot). ==================================================== 9. Slot skipping ==================================================== In the metadata created and added into the manifest.raucm file when an update bundle is created, RAUC will add size and SHA256 hash for each slot defined in the update. This is used by the adaptive update mechanism to determine if a slot has to be updated or not. If the SHA256 hash for rootfs or appfs in the bundle is identical to the hash of the already installed slots on the target device, then that slot is not written to flash. ==================================================== 10. Extract a bundle ==================================================== During development it can be useful to extract an existing bundle. A common example might be the official CrossControl system update bundle. This can be extracted by a developer to get the latest rootfs system .ext4 image, to be bundled together with a customer application into customer specific system bundle. You need to have the SDK environment setup and have sourced the environment, this assumes that you are at the root of SDK environment. To extract the bundle signed with CrossControl demo key, the demo certificate is needed: .. code-block:: bash [SDK]$ rauc extract -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=/opt/cclinux/4.1/sysroots/cortexa35-poky-linux/etc/rauc/ca.cert.pem /tmp/extracted-system-bundle The content of '/tmp/extracted-system-bundle' are the images included in the bundle above and can be added to other bundles if needed. ================================================================ 11. Replace CrossControl demo key with a customer generated key ================================================================ The certificate used (/etc/rauc/ca.cert.pem) is generated with the publicly available demo key provided in this manual, and anyone with this information can create install packages that can be installed in the default CCLinux 4.x image. Each customer should generate their own keys and replace the sign certificate in /etc/rauc folder on the displays. If you want to first test the bundling process with demo certificates and keys, you can skip to **14. Update bundle creation**. Key handling is described in more detail in the RAUC documentation: https://rauc.readthedocs.io/en/latest/advanced.html#security ==================================================== 12. Resigning bundles with customer keys ==================================================== CrossControl will provide update bundles for bootloader and rootfs (and the combined system bundle) signed with the demo key. RAUC allows for a bundle signature to be replaced. .. code-block:: bash [SDK]$ rauc resign --cert= --key= --keyring= It verifies the bundle against the given keyring, strips the old signature and attaches a new one based on the customer key and cert files provided. For more information, see: https://rauc.readthedocs.io/en/latest/advanced.html?highlight=resign#resigning-bundles ==================================================== 13. Add encryption to bundles ==================================================== RAUC supports encrypting the bundle. The implementation of the crypt bundle format is based on the verity bundle format (which uses Linux’s dm-verity module). It works by symmetrically encrypting the bundle payload and using Linux’s dm-crypt module to decrypt this on-demand. The symmetric encryption key is contained in the manifest, which itself is (asymmetrically) encrypted to a set of recipients. Like the verity format, the crypt format can also be used with HTTP streaming. One of the most critical aspects in encryption is the protection of the private key. In contrast to verification, where the private signing key is only used on the build host and can be well protected, for encryption a private decryption key must be stored securely on the target device. Depending on the threat model and the level of security required, it might be sufficient to have the key stored as plain PEM file on the target. This may be relevant if the access to the transport medium (USB stick, public server) is much easier than the access to the device's storage. However, for a security-sensitive application, the private key requires better protection. In i.MX8 the CAAM module can be used for secure access of the encryption key using the PKCS#11 API, but in this first release of CCLinux 4, this is not supported yet. More information: https://pengutronix.de/en/blog/2022-03-31-tutorial-rauc-bundle-encryption-using-meta-rauc.html ==================================================== 14. Update bundle creation ==================================================== Update bundles for bootloader and root filesystem (OS) are provided by CrossControl. These are built automatically from the Yocto build system for each release of CCLinux OS. Customers can create appfs update bundles, which is explained in the following section. ------------------------------------------------ 14.1. Create customer application systemd script ------------------------------------------------ In CCLinux 4, it is not possible to modify systemd startup scripts in /lib/systemd/system folder, but systemd will include /appfs/lib/systemd/system/ folder into the search path for systemd scripts. This makes it possible for customers to include automatic startup of applications by adding this script directly into the appfs update bundle. Note that adding this .service file with linking will override systemctl enable / disable behavior. You can create your application.service file with the help of the **CC Linux Software Guide** "*6.2 Systemd Management*". An example layout for appfs: .. code-block:: bash /appfs/lib/systemd/system/application.service # Symbolic link to your application.service file /appfs/lib/systemd/system/multi-user.target.wants/application.service It is also possible from the appfs update bundle to modify the content of /etc/systemd/system folder to add scripts there using the Rauc Hook file. Then it is possible to execute commands like "systemctl enable myApp.service", or enable/disable system services. --------------------------------------------------- 14.2. Create an appfs image file with customer data --------------------------------------------------- Since the application partition, called appfs, is an ext4 file system that will be written in one atomic write, it is most efficient if is prepared as a .ext4 or .img file. One example of how this can be done follows: Create a folder that should contain your application/project. In this example .. code-block:: bash mkdir v700-demo Enter this folder. Create appfs root folder to be used to prepare the update package .. code-block:: bash cd v700-demo mkdir appfs_root Copy binaries to appfs_root. In this example we will also extract the linx-qt68 archive into the appfs_root directory. .. code-block:: bash cp /home/ccs/qt/myApplication appfs_root tar -xf linx-qt68-v700-ccl40_6.8.0_aarch64.tar.gz -C appfs_root Create an empty appfs image file with the size needed for the package. This size must not be larger than the appfs partition size of this device. .. code-block:: bash dd if=/dev/zero of=appfs.ext4 bs=100M count=8 This command creates an 800MB file using eight 100MB blocks. Note, the actual block division makes no difference in this case. Create an ext4 file system in the image file .. code-block:: bash mkfs.ext4 appfs.ext4 Create a temp folder for mounting the appfs image .. code-block:: bash mkdir temp_appfs Mount the image to be able to copy files to it .. code-block:: bash sudo mount appfs.ext4 temp_appfs Copy all content of update folder to the image .. code-block:: bash sudo cp -rd appfs_root/* temp_appfs Unmount the image .. code-block:: bash sudo umount temp_appfs The appfs.ext4 image file now contains all files that will be installed by the update bundle. See the next section for how to create a bundle with this .ext4 file. ------------------------------------------------------------------------------------- 14.3. Create a Manifest file with information about the update bundle to be created ------------------------------------------------------------------------------------- RAUC will look for a file named "manifest.raucm" in the working folder. Create this folder and add a "manifest.raucm" file with the following contents: .. code-block:: bash [update] compatible=CrossControl V700 version=0.1 description=Appfs demo update package [bundle] format=verity [image.appfs] filename=appfs.ext4 Each CrossControl device has its own identifier, defined in /etc/rauc/system.conf. The CCpilot V700 is for example identified with the string "CrossControl V700". This has to be set in the manifest file so that RAUC knows that the bundle is compatible with a specific device. If you'd like to include a rootfs in the bundle (which is recommended), also add the following: .. code-block:: bash [image.rootfs] filename=ccpilot-v700-release-v700.ext4 The ccpilot-v700-release-v700.ext4 can be extracted from the official CrossControl RAUC bundles for CC Linux 4. --------------------------------------- 14.4. Create a bundle --------------------------------------- The RAUC binary is included in the device SDK. This is used to create an update bundle. To be able to sign the bundle, the CA certificate, developer key and developer certificate are also needed. Private keys should always be protected. For testing purposes, a demo certificate has been added to CCLinux 4.x and a demo developer key and cert are made publicly available. If you have created your own keys and resigned required bundles, use them here. .. code-block:: bash [SDK]$ rauc bundle -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=/opt/cclinux/4.1/sysroots/cortexa35-poky-linux/etc/rauc/ca.cert.pem --key=development-1.key.pem --cert=development-1.cert.pem .raucb Where refers to the name of the RAUC working folder containing manifest.raucm and the other files to include in the bundle. Make sure to remove all not needed files or and directories within your RAUC working folder. All files in the folder will be included in your bundle and folders may also prevent RAUC from creating the bundle. If you get an error message when trying to create the bundle, check that all paths are correct and that keys and certificates are correct and valid. The error message you will get from RAUC will not always point out the root cause of the failure. Also note that you will need a reasonably new Linux installation to execute the RAUC command. After successful bundling process, the created \*.raucb file can be installed on the target device, which is explained in the upcoming section. Demo versions: Developer key (development-1.key.pem): .. code-block:: bash -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDqfNpOnEE5Bzj8 0fRgClqDrY2hQynwphHSKrj8Gw5WXC9ndvgcjgLvqUtYiAFeHlAsqZEUyZIt+xth 7HBDIWTccZ1M427AgG2H7EQ1qdV95NuYDqMmw0b60D42DLAmZda1/IB6S/kGLEk7 VWLeoZXdkhzZVedeGEtB4HoyGU4DoMCDWtlW/D2o4RTMb1a2OkBdYK4vS+Xx9pKE MjsxV0xdO9t7Ps2QSmMExF0UfHn0m7bbs8zPFTh8OdB9WwGVF7Wz5bPFBFSVZMij VR7Vf37DalTLQj7SYyYDKqqJZ2Q/j+pp9vkepIpKFmQzmzWpiR9UouVKobKQnm5A KC0FyPbpAgMBAAECggEAAYPKt78J61PE/hofn2a1uLvMak4oJVQ3UYGh5gwJMszf 6AXUWr/enDx+Xn0rcBeXSkCSrb0THw2sGrz3qTFcKNHpk5t1BdSWkI4ZzzRwiwKJ WiE/E9MnYFuryTbaBPd7tHuD+3ZF7rCmzPWzYK+Py6mD2vITf2+9/bgU5gG0l7Cl Hw5D4on9jf51t68LRThYlSRIPzbxU3M2bBzk958Q6GbXVw8CRMwkgpeQEUV2Q4My yUqZhM7hHAYZJFOxbcfdq3uxpHxiQvGIXdvciiYNqY+dt5i44rVytoUoJ/c5kUPl GbyfBp3mbhADHdTIuVoRTl3G3mBAwn31yy+56jYF8QKBgQD7nUvFDgqObPlG8qZ5 oYdo6S+wPYCVC0iBZcfb20vfcU6iN3CyuAkwpZ7Qb2DpkNw9iNoM1gqDsPSF37lO ExMeEWv91VzWeeb2fj0pskoNSm7o17rP/zeWqyV8igwuGqbhHBhIqpuyim1BEiB/ BuywyRnZxehjkTDwUPS72bt5sQKBgQDukyMlCBfy0JGWSS/pPb7T1VJkNdsDRDqA e5Gz53JGxYfygQsNvB2jTVcvxy2IF0ffN0GQj/eiLv2J2TcgIvuguGa9KLQd1+S6 jZALgQbY4HAuOPNztDTAuAOPulhlFlhzkelqF7d8sY/IwKRTigV48J2Jf6lt/We5 29bxUsnmuQKBgQCYegrnoImvI8SwRO2zebqYTe9zSuH38D2BBM/noO5KutxfG1+1 Pid7jnI+d7Bv+jQtKhiRfMgkZADae+hMsux75SzfRwQaiQb4yyl/cibEBR5PBiSU VWI3cQGnhfS5n7cPZbollxYBTTfGHhkJQYX/3+5FdVRNujfA+h5FFD4zEQKBgQCT 0Iwc3Jfh9MHPSIcrzvrVyrzio8+PhY1tn6IVwhe2q5pgcP2FRAFk9X1Id/LI+Auq zFSOGfUHAnBKIVAbVDFtUfMtzfNS7jmkMCOITSgN5Xj72oNUM9nfceObAIzWDPM+ n6/r8MfZtTXt3xWxQLnFReeFkphKMpA6wm+5U18O2QKBgDiKiUm94fEr+mxJKaXc af99c2n/i8pxJX/OuShDCnfgzP4Ob3MaGteRNjXtYy0z9Mio3/IPWcU9YCGTr1/r q5rS3xWU0tUEwxStd7wgn/jTdR2qPTD/WSdRDlqc9Kf1ddqs1lGBGmyl+8Lf/2sO /kmjUPdHqjyDSiPmF+b7u5H4 -----END PRIVATE KEY----- Developer cert (development-1.cert.pem): .. code-block:: bash Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: O=CrossControl Test Org, CN=CrossControl Test Org rauc CA Development Validity Not Before: Jan 1 00:00:00 1970 GMT Not After : Dec 31 23:59:59 9999 GMT Subject: O=CrossControl Test Org, CN=CrossControl Test Org Development-1 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:ea:7c:da:4e:9c:41:39:07:38:fc:d1:f4:60:0a: 5a:83:ad:8d:a1:43:29:f0:a6:11:d2:2a:b8:fc:1b: 0e:56:5c:2f:67:76:f8:1c:8e:02:ef:a9:4b:58:88: 01:5e:1e:50:2c:a9:91:14:c9:92:2d:fb:1b:61:ec: 70:43:21:64:dc:71:9d:4c:e3:6e:c0:80:6d:87:ec: 44:35:a9:d5:7d:e4:db:98:0e:a3:26:c3:46:fa:d0: 3e:36:0c:b0:26:65:d6:b5:fc:80:7a:4b:f9:06:2c: 49:3b:55:62:de:a1:95:dd:92:1c:d9:55:e7:5e:18: 4b:41:e0:7a:32:19:4e:03:a0:c0:83:5a:d9:56:fc: 3d:a8:e1:14:cc:6f:56:b6:3a:40:5d:60:ae:2f:4b: e5:f1:f6:92:84:32:3b:31:57:4c:5d:3b:db:7b:3e: cd:90:4a:63:04:c4:5d:14:7c:79:f4:9b:b6:db:b3: cc:cf:15:38:7c:39:d0:7d:5b:01:95:17:b5:b3:e5: b3:c5:04:54:95:64:c8:a3:55:1e:d5:7f:7e:c3:6a: 54:cb:42:3e:d2:63:26:03:2a:aa:89:67:64:3f:8f: ea:69:f6:f9:1e:a4:8a:4a:16:64:33:9b:35:a9:89: 1f:54:a2:e5:4a:a1:b2:90:9e:6e:40:28:2d:05:c8: f6:e9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 21:1C:40:F5:35:E1:AC:D1:D6:01:F2:29:29:BD:3A:0B:58:24:51:06 X509v3 Authority Key Identifier: keyid:BF:50:C0:00:37:34:CF:2B:D0:12:FB:EA:34:EB:04:15:D6:BC:52:9F DirName:/O=CrossControl Test Org/CN=CrossControl Test Org rauc CA Development serial:01 X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: critical Code Signing Signature Algorithm: sha256WithRSAEncryption Signature Value: b1:34:6b:4c:4b:de:b2:db:14:7f:70:58:bb:03:0e:92:8d:90: a2:39:95:b3:4e:cd:e6:88:ba:0c:02:79:cc:28:94:e9:b5:8d: ee:10:15:a2:e9:52:4b:b5:c6:6a:e6:3d:9d:b3:11:fb:94:00: 30:5b:e5:75:fb:c3:bf:a6:3b:f2:75:10:a7:8e:0f:7d:1a:c8: 57:c4:ef:d4:b9:d6:21:9e:64:06:33:ba:e6:b5:df:c3:9b:ae: 9d:04:c2:1b:23:fe:95:0a:0a:be:58:aa:29:0e:bd:1e:3f:aa: 7b:68:b8:1d:17:3a:b7:89:e8:37:84:0d:73:6f:af:92:b7:94: 6e:71:d2:f6:da:ae:62:92:82:f3:db:4c:01:b9:fc:e1:84:2c: 4d:b1:de:4f:63:6b:f6:0a:1a:55:87:2e:20:dc:43:10:24:18: 4f:41:3f:03:57:ac:d9:17:18:cc:71:69:ce:11:a1:34:af:c1: c7:6a:3c:90:db:57:83:66:dd:2e:9e:93:3e:0f:45:8c:cd:2e: 0c:ae:ca:ee:87:f4:eb:dd:69:77:17:c3:d5:90:15:5f:0d:5e: 71:53:08:16:1f:3a:32:44:a2:47:b0:0b:7f:98:eb:d4:92:2d: f4:e7:b8:83:66:cc:81:32:3d:eb:fb:ed:b6:57:12:7a:b0:f6: c6:e7:79:e0 -----BEGIN CERTIFICATE----- MIID8zCCAtugAwIBAgIBAjANBgkqhkiG9w0BAQsFADBUMR4wHAYDVQQKDBVDcm9z c0NvbnRyb2wgVGVzdCBPcmcxMjAwBgNVBAMMKUNyb3NzQ29udHJvbCBUZXN0IE9y ZyByYXVjIENBIERldmVsb3BtZW50MCAXDTcwMDEwMTAwMDAwMFoYDzk5OTkxMjMx MjM1OTU5WjBOMR4wHAYDVQQKDBVDcm9zc0NvbnRyb2wgVGVzdCBPcmcxLDAqBgNV BAMMI0Nyb3NzQ29udHJvbCBUZXN0IE9yZyBEZXZlbG9wbWVudC0xMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6nzaTpxBOQc4/NH0YApag62NoUMp8KYR 0iq4/BsOVlwvZ3b4HI4C76lLWIgBXh5QLKmRFMmSLfsbYexwQyFk3HGdTONuwIBt h+xENanVfeTbmA6jJsNG+tA+NgywJmXWtfyAekv5BixJO1Vi3qGV3ZIc2VXnXhhL QeB6MhlOA6DAg1rZVvw9qOEUzG9WtjpAXWCuL0vl8faShDI7MVdMXTvbez7NkEpj BMRdFHx59Ju227PMzxU4fDnQfVsBlRe1s+WzxQRUlWTIo1Ue1X9+w2pUy0I+0mMm AyqqiWdkP4/qafb5HqSKShZkM5s1qYkfVKLlSqGykJ5uQCgtBcj26QIDAQABo4HT MIHQMB0GA1UdDgQWBBQhHED1NeGs0dYB8ikpvToLWCRRBjB8BgNVHSMEdTBzgBS/ UMAANzTPK9AS++o06wQV1rxSn6FYpFYwVDEeMBwGA1UECgwVQ3Jvc3NDb250cm9s IFRlc3QgT3JnMTIwMAYDVQQDDClDcm9zc0NvbnRyb2wgVGVzdCBPcmcgcmF1YyBD QSBEZXZlbG9wbWVudIIBATAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIFoDAWBgNV HSUBAf8EDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAsTRrTEvestsU f3BYuwMOko2QojmVs07N5oi6DAJ5zCiU6bWN7hAVoulSS7XGauY9nbMR+5QAMFvl dfvDv6Y78nUQp44PfRrIV8Tv1LnWIZ5kBjO65rXfw5uunQTCGyP+lQoKvliqKQ69 Hj+qe2i4HRc6t4noN4QNc2+vkreUbnHS9tquYpKC89tMAbn84YQsTbHeT2Nr9goa VYcuINxDECQYT0E/A1es2RcYzHFpzhGhNK/Bx2o8kNtXg2bdLp6TPg9FjM0uDK7K 7of0691pdxfD1ZAVXw1ecVMIFh86MkSiR7ALf5jr1JIt9Oe4g2bMgTI96/vttlcS erD2xud54A== -----END CERTIFICATE----- --------------------------------------- 14.5 Add your own keys/PKI --------------------------------------- The script `openssl-ca.sh` allows you to create a certificate and key that you can use for a test-setup of RAUC. This script can be found in the RAUC sources. To generate the required files, execute ./openssl-ca.sh This will generate a folder `openssl-ca/` that contains the generated openssl configuration file and a development certificate and key. You will need to securely store these certificates and keys. Of course you can use keys and certificates from your own PKI. To use them in your BSP in order to sign and verify bundles, you have to: 1) Make the target rauc package use the generated keyring file: Copy the keyring file `dev/ca.cert.pem` into your projects meta layer under `meta-cc/meta-<$platform>/recipes-core/rauc/files` and add a simple rauc .bbappend file `./meta-cc/meta-<$platform>/recipes-core/rauc/rauc_%.bbappend` that adds the keyring file to the rauc package search path: FILESEXTRAPATHS:prepend := "${THISDIR}/files:" 2) Make your bundle recipe use the generated key and certificate: Copy the key file `dev/private/development-1.key.pem` and the cert file `dev/development-1.cert.pem` to your projects meta layer in a global `files` folder. In your bundle recipe, let the variables `RAUC_KEY_FILE` and `RAUC_CERT_FILE` point to these key and cert files: RAUC_KEY_FILE = "${OEROOT}/meta-/files/development-1.key.pem" RAUC_CERT_FILE = "${OEROOT}/meta-/files/development-1.cert.pem" Now you are able to build and sign bundles that contain a rootfs that will allow RAUC to verify the content of further bundles. --------------------------------------- 14.6 Switch keyring/certificates --------------------------------------- Create new certificates and keys for your PKI, or use the ones you already have. To easily differentiate things we refer to this newly created PKI as "production", as opposed to the existing "development" keys. Move the generated keys and certificates, to your main Yocto build directory root. Or use the openssl-ca.sh script that comes with the RAUC sources. NOTE: Be careful where you store the private keys! These should in no way be made publicly available. E.g. do not store the private keys in a public Git repository. Otherwise, unauthorized entities could create RAUC bundles that can be installed on your target system! Now, a RAUC bundle must be created that contains the new "production" CA keyring in its root filesystem but is still signed by the "development" CA. With this, the system is converted from a "development" into a "production" system. To achieve this, exchange the file ca.cert.pem installed by the RAUC recipe in the Yocto sources. Edit the rauc_%.bbappend for your specific platform to switch the keyring. i.e. you make sure you point to the correct RAUC_KEYRING_FILE = "${OEROOT}/../../openssl-ca/ca.cert.pem" Build the same RAUC bundle as before, now with the exchanged keyring: .. code-block:: make <$platform>-os-update-bundle Install the resulting RAUC bundle as usual. The target now has the image with the "production" keyring installed in its other slot. Reboot to take into use the new RAUC setup. All future RAUC bundles for the "production" system must now also be signed by the "production" CA. To achieve this you need to change the key and certificate to your newly generated "production" ones in the bundle recipe: ./meta-cc/<$/recipes-core/bundles/ccpilot-<$platform>-bootloader-update-bundle.bb See 14.5 Add your own keys/PKI for the details. Rebuild the RAUC bundle and install. This new bundle, and any future bundles can now be installed on your "production" system and have been fully migrated away from the "development" system. This means that from now only bundles signed by the "production" CA can be installed on the target. Alternatively you can manually add the certificates to your device. We use a directory /data/rauc/certs/ You can add your own certificate there. To do this you need to get the x509 hash of your certificate and then add it to the directory specified earlier with as name .0 i.e. # openssl x509 -hash -in new-cert.pem 2944ab5e Thus the new file will be 2944ab5e.0 --------------------------------------- 14.7 Encrypted RAUC bundles --------------------------------------- Start by creating some keys to encrypt your bundle .. code-block:: openssl ecparam -name prime256v1 -genkey -noout -out private-key-crypt-bundle.pem openssl ec -in private-key-crypt-bundle.pem -pubout -out public-key-crypt-bundle.pem openssl req -new -x509 -key private-key-crypt-bundle.pem -out recipient-cert.pem -days 365250 -subj "/O=RAUC/CN=RAUC bundle crypt" By hand Create the bundle with the regular cert and key, but remember to change the manifest.raucb from verity to crypt .. code-block:: [SDK]$ rauc bundle -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --cert=cc-dev-cert.pem --key=cc-key.pem test/data/ cc-ca-bundle.raucb Then encrypt .. code-block:: [SDK]$ rauc encrypt -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=cc-ca.pem --to recipient-cert.pem cc-ca-bundle.raucb cc-encrypted.raucb Verify .. code-block:: [SDK]$ rauc info -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=cc-ca.pem --key=private-key-crypt-bundle.pem cc-encrypted.raucb In Yocto Replace verity with crypt in ./meta-cc/meta-v1x00/recipes-core/bundles/ccpilot-${PLATFORM}-bootloader-update-bundle.bb:RAUC_BUNDLE_FORMAT = "verity" ./meta-cc/meta-v1x00/recipes-core/bundles/ccpilot-${PLATFORM}-rootfs-update-bundle.bb:RAUC_BUNDLE_FORMAT = "verity" ./meta-cc/meta-v1x00/recipes-core/bundles/ccpilot-${PLATFORM}-system-update-bundle.bb:RAUC_BUNDLE_FORMAT = "verity" ./meta-cc/meta-v1x00/recipes-core/bundles/ccpilot-${PLATFORM}-appfs-update-bundle.bb:RAUC_BUNDLE_FORMAT = "verity" ./meta-cc/meta-v1x00/recipes-core/bundles/ccpilot-${PLATFORM}-os-update-bundle.bb:RAUC_BUNDLE_FORMAT = "verity" rebuild the needed bundle .. code-block:: make $PLATFORM-os-update-bundle .. code-block:: [SDK]$ rauc info -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=v1x00-cert.pem ~/projects/cclinux/binaries/v1x00/CCpilot-v1x00-4.1.0.0-1-g4c3e6842-virt-os-update-bundle.raucb rauc-Message: 18:23:13.245: Reading bundle: /home/phdeswer/projects/cclinux/binaries/v1x00/CCpilot-v1x00-4.1.0.0-1-g4c3e6842-virt-os-update-bundle.raucb rauc-Message: 18:23:13.247: Verifying bundle signature... rauc-Message: 18:23:13.247: Verified inline signature by 'O = Test Org, CN = Test Org Development-1' Compatible: 'CrossControl V1X00' Version: '1.0' Description: 'ccpilot-v1x00-os-update-bundle version 1.0-r0' Build: '20250821143342' Hooks: '' Bundle Format: crypt [unencrypted CMS] Crypt Key: '' Verity Salt: '8ae5dc84384027d39d0001035879ce692c25854bf4564525b1480802fb48ec06' Verity Hash: '3d7448862c6cfc008402ce093ba6d105bb50f3b04198f0ef1171dd53310c096f' Verity Size: 2109440 Manifest Hash: 'a2868a7cfafb048470d4b178bdc47b821a92b44415030c434be1ac99aebdc260' 2 Images: [bootloader] Filename: imx-boot.img Checksum: 44d3f8342e3d09c9d05c9a93d3145302496183fc3e49628e06a08db60925d5d1 Size: 1.1 MB (1,120,296 bytes) Hooks: [rootfs] Filename: ccpilot-v1x00-release-v1x00.ext4 Checksum: c7d4a8070f93e50854068050f042d3b88bcbb9022651e5e0145eb8784d33d904 Size: 1.1 GB (1,110,740,992 bytes) Hooks: Adaptive: block-hash-index Certificate Chain: 0 Subject: O = Test Org, CN = Test Org Development-1 Issuer: O = Test Org, CN = Test Org rauc CA Development SPKI sha256: 28:AF:42:68:ED:FB:14:24:20:1F:79:02:44:CE:68:98:C0:75:D7:A6:E2:D7:CB:92:5C:CE:EE:23:6F:61:B7:1B Not Before: Jan 1 00:00:00 1970 GMT Not After: Dec 31 23:59:59 9999 GMT 1 Subject: O = Test Org, CN = Test Org rauc CA Development Issuer: O = Test Org, CN = Test Org rauc CA Development SPKI sha256: 07:5B:28:48:6D:61:F4:4F:8E:84:9F:3C:CF:3C:21:97:9B:9D:4F:30:D0:6C:C5:87:A2:A5:F5:45:F1:44:A7:46 Not Before: Jan 1 00:00:00 1970 GMT Not After: Dec 31 23:59:59 9999 GMT Now you have an unencrypted bundle, ready to be encrypted. This must be performed manually as there is no crypt support in yocto. .. code-block:: [SDK]$ rauc encrypt -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=cc-ca.pem --to recipient-cert.pem CCpilot-${PLATFORM}-${REVISION}-virt-os-update-bundle.raucb cc-encrypted.raucb Now you can verify that it is correctly encrypted as follows: .. code-block:: [SDK]$ rauc info -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --keyring=cc-ca.pem --key=private-key-crypt-bundle.pem cc-encrypted.raucb rauc-Message: 12:07:06.307: Reading bundle: /tmp/cc-encrypted.raucb rauc-Message: 12:07:06.308: Decrypting signature... rauc-Message: 12:07:06.309: Verifying bundle signature... rauc-Message: 12:07:06.309: Verified inline signature by 'O = Test Org, CN = Test Org Development-1' Compatible: 'CrossControl V1X00' Version: '1.0' Description: 'ccpilot-v1x00-os-update-bundle version 1.0-r0' Build: '20250617080611' Hooks: '' Bundle Format: crypt [encrypted CMS] Crypt Key: '' Verity Salt: '77fc067d493ba2feb37329c6faeec4f386d16d0be26a58a7086a2e73a80fddb4' Verity Hash: '102344dcc71e3684c8d747da21728e40eb1968f466a88c80f9e399d6d9d4b401' Verity Size: 2109440 Manifest Hash: '89be66623035ecf57134113d0a2884aa1459f59c44698be67afb43c8a80e9fd7' 2 Images: [bootloader] Filename: imx-boot.img Checksum: 44d3f8342e3d09c9d05c9a93d3145302496183fc3e49628e06a08db60925d5d1 Size: 1.1 MB (1,120,296 bytes) Hooks: [rootfs] Filename: ccpilot-v1x00-release-v1x00.ext4 Checksum: 63d9751de803afe92af8f00be90fd4e29e34750576d1bd062435b211578751a7 Size: 1.1 GB (1,110,659,072 bytes) Hooks: Adaptive: block-hash-index Certificate Chain: 0 Subject: O = Test Org, CN = Test Org Development-1 Issuer: O = Test Org, CN = Test Org rauc CA Development SPKI sha256: 19:01:4A:46:52:35:66:95:CC:D9:93:D2:74:8D:54:A7:D7:54:EF:A0:30:9C:0D:3E:86:70:93:C0:08:36:B1:E9 Not Before: Jan 1 00:00:00 1970 GMT Not After: Dec 31 23:59:59 9999 GMT 1 Subject: O = Test Org, CN = Test Org rauc CA Development Issuer: O = Test Org, CN = Test Org rauc CA Development SPKI sha256: 02:9C:F8:8B:AD:5D:5E:6B:54:A1:C3:25:91:BF:8E:66:A7:39:5D:25:6F:0F:94:FA:D5:C9:E4:69:DA:7C:D9:42 Not Before: Jan 1 00:00:00 1970 GMT Not After: Dec 31 23:59:59 9999 GMT Now to enable encrypted bundle support you need to add the right certificate and key on the device and configure things in the /etc/rauc/system.conf .. code-block:: [encryption] key=private-key-crypt-bundle.pem cert=cc-ca-cert.pem ==================================================== 15. Update using RAUC Bundles ==================================================== There are several methods available for updating the target device using RAUC bundles. These methods are described in detail in this section. .. note:: After an installation attempt, detailed logs can be found at */data/rauc/install.log*. --------------------------------------- 15.1. USB update --------------------------------------- CCLinux 4 has a built-in feature to look for a *single* rauc update bundle on a USB stick. When a USB stick is inserted into a CCpilot display, a script is executed and it will look for \*.raucb package. If only one package is found, RAUC will check the signature of it compared to the installed certificate in '/etc/rauc/ca.cert.pem', and if the bundle passes the verification check, it will then be installed. The USB update script also has built-in protection to prevent the update from being retriggered after the reboot if the identical update file used for last update is detected on the USB stick during bootup or if inserted again. The /usr/bin/cc-update script logic will save a md5sum of the last update bundle installed using USB to /data and compare this value with the next USB update file. If identical, the update is not performed. It is also possible to disable the USB update functionality if RAUC updates are handled by the application using RAUC API instead. Modify the file /etc/cc-update-config.sh file and set: .. code-block:: bash DO_USB_UPDATE=0 Copy the \*.raucb file to the root directory of a USB drive and insert it into the target device. After a short delay (up to ~20 seconds), the device will emit a short beep and the LED will begin blinking *yellow*, indicating that the update process has started. Once the installation completes successfully, the LED will turn *green* and a longer beep will sound. After an succesfull update, the device needs to be rebooted to apply the changes. By default the cc-update script will automatically reboot, but if you wish to disable this behaviour, modify the file /etc/cc-update-config.sh file and set: .. code-block:: bash DO_UPDATE_AUTO_REBOOT=0 If the update fails, the LED will blink *red* with falling tone beeps. After a short while, it will switch back to *green* and automatically retry the installation. .. note:: The USB drive should be kept connected the whole update process until the device starts reboot, since removing it before completion could cause issues with the update procedure. ----------------------------------------------------- 15.2 Manual update using scp (secure copy protocol) ----------------------------------------------------- The RAUC update bundle can be transferred to the device over an SSH connection using the **scp** tool, as shown below: .. code-block:: $ scp update-appfs.raucb ccs@v700: The bundle can then be installed using the rauc install command: .. code-block:: $ rauc install update-appfs.raucb CC Linux 4 also includes a utility called **cc-update**, which is primarily used to automatically install bundles from a USB stick. It can also be used for manual installations. An example command is: .. code-block:: $ cc-update install update-appfs.raucb After the update is successfully completed, the system will reboot automatically. --------------------------------------- 15.3. OTA updates --------------------------------------- RAUC supports updates from locations other than USB, and also a http(s) address. CCLinux itself has no built-in feature to update from a web address, but customers can include this feature into their applications or scripts. The same "rauc install " command is used if it is a local path or http(s) location. CCLinux 4.x has support for running fleet management clients like qBee, Hawkbit and other commercial alternatives, but no specific solution is pre-built into the OS image. Please contact CrossControl to discuss these alternatives if needed. --------------------------------------- 15.4. Adaptive updates --------------------------------------- RAUC does not use binary delta updates. Instead RAUC has developed adaptive updates, a way to optimize the download size of bundles by enabling selective updates without needing a specific prior version. Adaptive updates use existing data on the target from the previous version or incomplete installations to reduce what needs to be downloaded. This is achieved through block-based adaptive updates with a method called block-hash-index. In this approach, an index file with hashes of each 4kiB data block in the image is created during bundle generation. During installation, RAUC compares each block's hash in the target slots to those in the bundle, downloading only unmatched blocks. Adaptive updates work best with HTTP streaming, and is enabled by default on CCLinux 4.x. --------------------------------------- 15.5. Casync updates --------------------------------------- RAUC also supports adaptive updates with **casync** setup, which uses casync index files instead of full images in its bundles. This approach significantly reduces bundle size, as only the small index files are included. The actual image data is stored as individual chunks in a single repository, which can serve multiple images and versions for different systems. Using casync requires converting bundles and setting up a separate chunk repository on the server side. Using casync is a two-step process, and first a normal rauc bundle is created. Then as a second step, the 'rauc convert' command is used to generate a casync bundle and a chunk store folder. .. code-block:: bash [SDK]$ rauc convert -c "$SDKTARGETSYSROOT"/etc/rauc/system.conf --cert= --key= --keyring= conventional-bundle.raucb casync-bundle.raucb The casync bundle is much smaller that the original bundle, but the chunk store has to be accessible on target for the update to be performed, either by path or http. It is possible to add combine chunks from several different update packages into the same chunk store. ==================================================== 16. Explaining the /data partition in CCLinux 4 ==================================================== The /data partition is where a customer application can write to. This can be used for database handling, logging, configuration files and more. But it is also the location for several OS-specific features, and must be handled with care to not corrupt the system. The /data partition is automatically created on the first boot of CCLinux 4.x. It occupies the remaining free space on the flash drive after the rootfs A/B and appfs A/B partitions are set up. This is the only location where the default user, ccs, has write permissions, and this is the location of the user home directory. Additionally, it's used by RAUC to store information about filesystem block hash indexes, which are essential for adaptive updates. To allow for modifications to configuration files in the read-only rootfs, an overlay filesystem is used. The overlay files are located in the /data/cc-etc folder. This means that any changes made to configuration files in the /etc directory will be stored in this overlay. If your system has Docker enabled, containers, images, and related configuration files will be stored in the /data partition. Keep in mind that Docker can consume a significant amount of flash storage, especially when working with large containers. Therefore, it's essential to ensure that there's sufficient free space on /data to accommodate the specific Docker usage. --------------------------------------- 16.1. /etc overlay --------------------------------------- CCLinux 4.x uses a read-only filesystem on the rootfs partition. This means it is not possible to remove, add or modify any of the content in the Linux OS. Most configuration files in Linux are located in the /etc folder, so to be able to customize functionality such as IP address, firewall settings, passwords, etc, an OverlayFS is added on top of /etc folder. The OverlayFS works so that it combines the content of a lower layer and upper layer in the running system. The lower layer is the original /etc folder from CCLinux 4.x, providing the base configuration of CCLinux. The upper layer contains user modification and any changes made to /etc will be stored in the /data/cc-etc folder. OverlayFS merges these two directories, allowing for the modification of configuration files while preserving the read-only system. A factory reset command using ccsettingsconsole will remove the overlay and the OS is restored to the original configuration. By using RAUC hook script files, it is possible to modify the content of /etc folder to do customer specific modifications to the file system. Such modifications can for example be to set a static IP address or replace the CrossControl demo RAUC certificate with a customer specific one. --------------------------------------- 16.2. /data/rauc --------------------------------------- This folder is used by the system to store RAUC related configuration files and the installation log files. For adaptive updates, RAUC creates a block-hash-index of both rootfs and appfs, and stores the hash tables in this folder. This hash index is used by RAUC for OTA updates to determine if a block needs to be downloaded or not. There is no need to manually modify anything in this folder. ==================================================== 17. Advanced use cases ==================================================== ---------------------------------------------- 17.1. Manually change active rootfs partition ---------------------------------------------- In some cases, it may be useful to manually switch the active boot configuration. To boot into the alternate configuration, mark the other boot slot as active and then reboot the system. .. code-block:: bash rauc status mark-active [booted | other] sudo reboot ------------------------------------------------------- 17.2. Create an update bundle that only execute scripts ------------------------------------------------------- It’s possible to create an update bundle that doesn’t modify any partitions. In this case, only pre-install or post-install scripts will be executed. This can be useful for applying configuration changes or performing setup tasks without altering the system image. This example will execute a command only. Create a manifest.raucm file containing: .. code-block:: bash [update] compatible=CrossControl V700 [hooks] install=hook.sh ------------------------------------------------ 17.3. Unlock /appfs for development and testing ------------------------------------------------ It is possible to unlock /appfs partition during development. This is not suitable for production environments, but while application development is in progress, the possibility to debug and deploy applications on target is useful. By default, the /appfs partition is mounted read-only. To remount it as read-write, use the following command: .. code-block:: bash sudo mount -o remount,rw /appfs ... do changes ... sudo mount -o remount,ro /appfs A unit with a clean install of CCLinux 4.x comes with a minimal and empty /appfs partition. To add files to the unit, you can either create an update bundle as described above, or—during early development—you may choose to overwrite the minimal image with a larger one that fully utilizes the partition. The following steps will remove the original appfs image and create a new one suitable for development: First check the boot status to see if the mounted /appfs is using /dev/mmcblk1p4 or /dev/mmcblk1p5 .. code-block:: bash rauc status Then unmount .. code-block:: bash sudo umount /appfs You can now re-create the appfs image to a larger version. This command will for example create an 800MB image, big enough to fit Qt runtime and application data: .. code-block:: bash sudo dd if=/dev/zero of=/dev/mmcblk1p4 bs=100M count=8 The new image has to be of format 'ext4': .. code-block:: bash sudo mkfs.ext4 /dev/mmcblk1p4 The new image written can now be mounted again as a new /appfs .. code-block:: bash sudo mount /dev/mmcblk1p4 /appfs ------------------------------------------ 17.4. RAUC rollback on installation errors ------------------------------------------ By default in CCLinux, RAUC is configured to attempt booting the newly installed system up to five times. Specifically, the system must successfully reach the rauc-mark-good.service during the boot process. If this target is not reached within five attempts, the system will revert to the previously working slot, and the newly installed slot will be marked as "bad" according to RAUC's terminology. Important: The bootloader is responsible for tracking the number of failed boot attempts. If the bootloader itself becomes corrupted or its configuration stored in eMMC is damaged, rollback may not be possible—even if the system is severely broken. You can check the status of slots via the D-Bus API or from the command line. Here’s a command-line example showing a failed boot to slot B (rootfs.1), which triggered a rollback to slot A: Default pretty print in shell for easier viewing: .. code-block:: bash rauc status Output: .. code-block:: bash === System Info === Compatible: CrossControl V1X00 Variant: Booted from: rootfs.0 (A) === Bootloader === Activated: rootfs.0 (A) === Slot States === [bootloader.0] (/dev/mmcblk1, boot-emmc, inactive) ○ [rootfs.1] (/dev/mmcblk1p3, ext4, inactive) bootname: B boot status: bad [appfs.1] (/dev/mmcblk1p5, ext4, inactive) ⏺ [rootfs.0] (/dev/mmcblk1p2, ext4, booted) bootname: A mounted: / boot status: good [appfs.0] (/dev/mmcblk1p4, ext4, active) mounted: /appfs Machine-parseable output is also possible from command line. Output is evaluable shell variables or json: .. code-block:: bash rauc status --output-format=shell rauc status --output-format=json rauc status --output-format=json-pretty ------------------------------------------ 17.5. Yocto recipe for RAUC bundles ------------------------------------------ Yocto support for using RAUC is provided by the **meta-rauc** layer. Here is an example yocto rauc bundle recipe for '***CCpilot-v700-4.x.x.x-system-update-bundle.bb***' .. code-block:: DESCRIPTION = "V700 system update bundle generator" inherit bundle RAUC_BUNDLE_COMPATIBLE = "CrossControl V700" RAUC_BUNDLE_SLOTS = "bootloader rootfs appfs" RAUC_BUNDLE_FORMAT = "verity" # Slot bootloader RAUC_SLOT_bootloader = "imx-boot" RAUC_SLOT_bootloader[file] = "imx-boot.img" RAUC_SLOT_bootloader[depends] = "imx-boot:do_deploy" # Slot rootfs RAUC_SLOT_rootfs = "ccpilot-v700-release" RAUC_SLOT_rootfs[fstype] = "ext4" RAUC_SLOT_rootfs[adaptive] = "block-hash-index" # Slot appfs RAUC_SLOT_appfs = "ccpilot-v700-appfs" RAUC_SLOT_appfs[fstype] = "ext4" RAUC_SLOT_appfs[adaptive] = "block-hash-index" RAUC_KEY_FILE = "${OEROOT}/../meta-cc/meta-bsp-v700/recipes-core/rauc/files/development-1.key.pem" RAUC_CERT_FILE = "${OEROOT}/../meta-cc/meta-bsp-v700/recipes-core/rauc/files/development-1.cert.pem" ------------------------------------------ 17.6. Rauc system.conf file ------------------------------------------ The system.conf configuration file, located in the root file system, defines the number and type of available slots, as well as the location of the keyring file used for verifying update bundles. This file is loaded from /etc/rauc/ and is essential for validating the storage locations of update images. Each board type requires a tailored configuration to match its specific hardware layout and update strategy. Below is an example of what a ***system.conf*** file might look like for an example platform: .. code-block:: [system] compatible=CrossControl V700 bootloader=uboot data-directory=/data/rauc bundle-formats=-plain [keyring] directory=/data/rauc/certs/ check-purpose=codesign [log.install-log] filename=install.log events=install format=readable max-size=100K max-files=5 [slot.bootloader.0] device=/dev/mmcblk1 type=boot-emmc install-same=false [slot.rootfs.0] device=/dev/mmcblk1p2 type=ext4 bootname=A install-same=false [slot.rootfs.1] device=/dev/mmcblk1p3 type=ext4 bootname=B install-same=false [slot.appfs.0] device=/dev/mmcblk1p4 type=ext4 parent=rootfs.0 install-same=false [slot.appfs.1] device=/dev/mmcblk1p5 type=ext4 parent=rootfs.1 install-same=false The system.conf file is generally the same across all platforms that use the U-Boot bootloader. However, for the Extreme line of displays, which use GRUB instead of U-Boot, the configuration differs only in the bootloader section. The rest of the file remains consistent across platforms. .. code-block:: [system] compatible=CrossControl X1200 bootloader=grub grubenv=/efi/EFI/BOOT/grubenv data-directory=/data/rauc bundle-formats=-plain [slot.efi.0] device=/dev/mmcblk0 type=boot-gpt-switch region-start=1M region-size=256M install-same=false ------------------------------------------ 17.7. Utils needed by RAUC ------------------------------------------ The RAUC binary relies on specific firmware utilities in the kernel to configure bootloader environment variables after a system update. These utilities are consistent across all platforms using U-Boot as the bootloader, with the only exception being the Extreme line displays, which use GRUB instead. **Table 1** shows the kernel utils needed for different platforms. .. list-table:: Table 1: Kernel Utilities needed by RAUC :widths: 30 30 :header-rows: 1 * - V700/V1x00/Vx10/Yukon - X1200 * - fw_setenv / fw_printenv - grub-editenv .. note:: 'fw-setenv' and 'fw-printenv' are the uboot fw utils will be installed into the target by the libubootenv package. Make sure libubootenv image ends up in the image. .. note:: 'grub-editenv' will be installed into the target by default by the Grub bootloader. ------------------------------------------ 17.8. Firmware environment config ------------------------------------------ The environmental variables for either U-Boot or Grub will end up in the boot partition on the target. 1. The U-Boot fw utils (fw_setenv / fw_printenv) will look for config file 'fw_env.config' file in the /etc directory where to set or edit the U-Boot environmental variables. 2. The Grub util (grub_editenv) will look for 'grubenv' file in the /efi/EFI/BOOT directory of the boot partition to set or edit the Grub environmental variables. ------------------------------------------ 17.9. RAUC boot script ------------------------------------------ To support redundant booting into Slot A or B, manual boot scripting is required. This script is identical across all devices using U-Boot (V700, V1x00, Vx10, Yukon), but differs for Extreme line displays, which use GRUB as their bootloader. The scripts for U-Boot and GRUB vary in how they store and modify environment variables. When properly configured, these variables can be accessed both from the bootloader and from Linux userspace. The default RAUC U-Boot and Grub boot selection implementation requires a boot script using a specific set of variables that are persisted to the environment as stateful slot selection information. .. note:: The boot script variables will be different for U-Boot and Grub. ------------------------------------------ 17.9.1 RAUC boot script for U-Boot ------------------------------------------ The U-Boot Rauc boot script will be implemented into the board header file in the uboot source as a patch. .. code-block:: "test -n "${BOOT_ORDER}" || env set BOOT_ORDER "A B"; " "test -n "${BOOT_A_LEFT}" || env set BOOT_A_LEFT 3; " "test -n "${BOOT_B_LEFT}" || env set BOOT_B_LEFT 3; " "env set rauc_slot; " "env set mmcpartroot; " "env set mmcroot; " "for BOOT_SLOT in "${BOOT_ORDER}"; do " "if test "x${mmcpartroot}" != "x"; then " "; " "elif test "x${BOOT_SLOT}" = "xA"; then " "if itest ${BOOT_A_LEFT} -gt 0; then " "setexpr BOOT_A_LEFT ${BOOT_A_LEFT} - 1; " "echo "Booting valid RAUC slot A, ${BOOT_A_LEFT} attempts remaining"; " "setenv rauc_slot "A"; " "setenv mmcpartroot 2; " "setenv mmcroot /dev/mmcblk1p2; " "fi; " "elif test "x${BOOT_SLOT}" = "xB"; then " "if itest ${BOOT_B_LEFT} -gt 0; then " "setexpr BOOT_B_LEFT ${BOOT_B_LEFT} - 1; " "echo "Booting valid RAUC slot B, ${BOOT_B_LEFT} attempts remaining"; " "setenv rauc_slot "B"; " "setenv mmcpartroot 3; " "setenv mmcroot /dev/mmcblk1p3; " "fi; " "fi; " "done; " "if test -n "${mmcpartroot}"; then " "run mmcrootargs; " "run mmcargs; " "saveenv; " "else " "echo "No valid RAUC slot found. Resetting attempts to 3"; " "setenv BOOT_A_LEFT 3; " "setenv BOOT_B_LEFT 3; " "saveenv; " "reset; " "fi; " ------------------------------------------------------------------ 17.9.2 RAUC boot script for Grub ------------------------------------------------------------------ The Grub RAUC boot script will be implemented in the 'grub.cfg' file of grub-bootconf which will end up in the boot partition */efi/EFI/BOOT/* .. code-block:: serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 # default boot to Main A default=0 # timeout 1 second timeout=1 set ORDER="A B" set A_OK=0 set B_OK=0 set A_TRY=0 set B_TRY=0 set RAUC_SLOT=A load_env --file=(hd0,1)/EFI/BOOT/grubenv # select bootable slot for SLOT in $ORDER; do if [ "$SLOT" == "A" ]; then INDEX=0 OK=$A_OK TRY=$A_TRY A_TRY=1 fi if [ "$SLOT" == "B" ]; then INDEX=1 OK=$B_OK TRY=$B_TRY B_TRY=1 fi if [ "$OK" -eq 1 -a "$TRY" -eq 0 ]; then default=$INDEX break fi done # reset booted flags if [ "$default" -eq 0 ]; then if [ "$A_OK" -eq 1 -a "$A_TRY" -eq 1 ]; then A_TRY=0 fi if [ "$B_OK" -eq 1 -a "$B_TRY" -eq 1 ]; then B_TRY=0 fi fi save_env --file=(hd0,1)/EFI/BOOT/grubenv A_TRY A_OK B_TRY B_OK ORDER FACTORY_RESET ROOTARGS="rootwait ro rootfstype=ext4 device=/dev/mmcblk0 rwdevice=/dev/mmcblk0p6 rwfstype=ext4 rwreset=no" BOOTARGS="console=ttyS5,115200 net.ifnames=0 panic=60 quiet intel_iommu=off" menuentry "Main A (OK=$A_OK TRY=$A_TRY)"{ linux (hd0,2)/boot/bzImage LABEL=root_A root=/dev/mmcblk0p2 $ROOTARGS $BOOTARGS rauc.slot=A initrd (hd0,2)/boot/microcode.cpio set RAUC_SLOT=A save_env --file=(hd0,1)/EFI/BOOT/grubenv RAUC_SLOT } menuentry "Main B (OK=$B_OK TRY=$B_TRY)"{ linux (hd0,3)/boot/bzImage LABEL=root_B root=/dev/mmcblk0p3 $ROOTARGS $BOOTARGS rauc.slot=B initrd (hd0,3)/boot/microcode.cpio set RAUC_SLOT=B save_env --file=(hd0,1)/EFI/BOOT/grubenv RAUC_SLOT } ==================================================== 18. Additional Security Measures ==================================================== The default configuration of CC Linux 4 requires additional hardening steps to meet production-level security standards, depending on the specific use case. For detailed guidance, please refer to the CC Linux Security Manual, which outlines the recommended procedures for securing the system. ==================================================== 19. Using RAUC D-Bus API ==================================================== A Qt based example application for handling USB or OTA updates is available on request. ======================================================= 20. How to generate customer specific RAUC signing keys ======================================================= Step 1: Install Git, Fuse2fs and a recent version of Rauc to a Linux machine Use a Linux distribution with a recent version (1.12 or later) of Rauc. (Ubuntu 25.04 or later, Debian 13 or later, etc.) .. code-block:: sudo apt-get update && sudo apt-get upgrade && sudo apt-get install git rauc fuse2fs Step 2: Clone repository and navigate to the correct directory .. code-block:: git clone https://github.com/crosscontrol-open-source/cclinux-sign-example.git cd cclinux-sign-example Step 3: Add your company name and a unique identifier (`id`) to the customer database: Modify the file `customer-pki/customers.json` Step 4: Generate the signing certificates and **securely back them up**: .. code-block:: scripts/customer.py --gen_customers **BACKUP YOUR SIGNING CERTIFICATE AND PRIVATE KEY** generated in the directory `customer-pki/customer-`