Skip to content

Add ARM64 host support#152

Open
GabrielSSAlmeida wants to merge 4 commits into
torizon:trixiefrom
GabrielSSAlmeida:TCB-882
Open

Add ARM64 host support#152
GabrielSSAlmeida wants to merge 4 commits into
torizon:trixiefrom
GabrielSSAlmeida:TCB-882

Conversation

@GabrielSSAlmeida

Copy link
Copy Markdown

What changed

  • Dockerfile: Added IMAGE_ARCH usage comment and fixed Go binary download in skopeo-builder to use the correct architecture.
  • Kernel backend: Added ARM64 host support for building kernel modules. For ARM64→ARM64 builds, a custom toolchain built with crosstool-ng is used. For ARM64→ARM32, the arm-gnu-toolchain-13.3 aarch64 variant is used, fetched directly from ARM's official releases — following the same convention already in place for x86_64 hosts.
  • GitLab CI/CD: Extended pipeline to build, test, and publish the ARM64 variant across all stages. ARM64 deploy jobs are now active.
  • GitHub Actions: Added ARM64 lint and build/test jobs running on ubuntu-24.04-arm runners.

Toolchain decision

ARM's official releases do not provide a native ARM64→ARM64 toolchain for GCC 13.3, which is the version used in TorizonOS Scarthgap 7.x.y. Three approaches were considered to solve this:

  • gnu-devtools-for-arm — ARM's current recommended build tool
  • Linaro ABE — ARM's legacy build tool, used at the time GCC 13.3 was released
  • crosstool-ng — a versatile and flexible toolchain builder

gnu-devtools-for-arm was the first attempt. However, this tool does not support Canadian Cross builds (x86→ARM64→ARM64), which is the required setup to cross-compile the toolchain from an x86 host. Due to this limitation, this approach was discarded.

Linaro ABE was the second attempt, as it was the tool used when GCC 13.3 was originally released. However, it has been fully discontinued and is actively discouraged. At this point, it is not even possible to clone the repository from official sources, so this approach was also dropped.

crosstool-ng was the final choice. Being a highly versatile tool, it fits the build scenario perfectly. To avoid any compatibility issues, the toolchain was built as close as possible to the versions described in the ARM GNU Toolchain 13.3.Rel1 release notes. This approach proved efficient and functional, with validation tests described in the next section.

Testing

The TCB integration tests were run locally and almost all passed on the first run.

verdin-imx8mp-14773124:~/torizoncore-builder/tests/integration# TCB_CUSTOM_IMAGE=torizoncore-builder:local TCB_DEVICE=10.22.1.154 TCB_MACHINE=verdin-imx8mp DEVICE_PASSWORD=1 TCB_REPORT=1 ./run.sh
Using custom TorizonCore Builder image [torizoncore-builder:local].
Checking connection with device 10.22.1.154...OK! Found machine verdin-imx8mp.
Test cases using image torizon-docker-verdin-imx8mp-Tezi_7.7.0-devel-20260625+build.483.tar for machine verdin-imx8mp.
Starting integration tests...

Unpacking Toradex Easy Installer image.
Unpacking TorizonCore Toradex Easy Installer image.
Importing OSTree revision 5aed2758dc618e9b36bfc4c2abf1714a9644d7c3bc4938020fb56be62e381add from local repository...
1321 metadata, 10377 content objects imported; 660.9 MB content written
0 metadata, 0 content objects imported; 0 bytes content written
Unpacked OSTree from Toradex Easy Installer image:
  Commit checksum: 5aed2758dc618e9b36bfc4c2abf1714a9644d7c3bc4938020fb56be62e381add
  TorizonCore Version: 7.7.0-devel-20260625+build.483
error: Key file does not have group ?ex-integrity?
Image has NO composefs (root filesystem protection) support.
Running locally with tee to save report...
1..205
ok 1 Check if versions of file and installed on device matches in 2617ms
ok 2 torizoncore-builder: run without parameters in 4434ms
ok 3 torizoncore-builder: get software version in 4128ms
ok 4 torizoncore-builder: deprecated --bundle-directory in 4324ms
ok 5 torizoncore-builder: hidden --storage-directory parameter in 4211ms
ok 6 images: check help output in 4224ms
ok 7 images provision: basic offline-provisioning (standalone) in 47649ms
ok 8 images provision: basic offline-provisioning ("build" command) in 456588ms
ok 9 images provision: basic online-provisioning in 52595ms
ok 10 images provision: basic online-provisioning ("build" command) in 606273ms
ok 11 images provision: add containers to provisioned image in 22103ms
ok 12 images provision: provision image already having containers in 21891ms
ok 13 images provision: customize image already provisioned in 166567ms
ok 14 images unpack: run without parameters in 4540ms
ok 15 images unpack: check help output in 4154ms
ok 16 images unpack: check empty storage in 3703ms
ok 17 images unpack: unpack non-existent image in 4213ms
ok 18 images unpack: unpack image from tar file in 58199ms
ok 19 images unpack: unpack image from directory in 64615ms
ok 20 images unpack: unpack image from zip file with no top-level directory in 98705ms
ok 21 images unpack: keep only /storage/toolchain directory in storage in 64148ms
ok 22 images download: run without parameters in 4329ms
ok 23 images download: check help output in 4203ms
ok 24 images download: download image based on device in 86580ms
ok 25 images serve: check help output in 4404ms
ok 26 images serve: check if 'image_list.json' file exists in 4214ms
ok 27 images serve: check zeroconf tezi service response. in 17920ms
ok 28 images serve: check if 'image_list.json' is being served. in 11853ms
ok 29 images serve: check if 'image_list.json' is reachable by device. in 13706ms
ok 30 secboot: check help output in 4367ms
ok 31 secboot sign-bootloader-hab: check help output in 4300ms
ok 32 secboot sign-bootloader-hab: run without parameters in 4198ms
ok 33 secboot sign-bootloader-hab: attempt to sign kernel FIT without images unpack in 7436ms
ok 34 secboot sign-bootloader-hab: invalid parameters in 84649ms
ok 35 secboot sign-bootloader-hab: image with unsupported kernel format in 65050ms
ok 36 secboot sign-bootloader-hab: machine not compatible with HAB in 70379ms
ok 37 secboot sign-bootloader-hab: sign HAB image with 2048-bit RSA keys in 47ms # skip signed image not found
ok 38 secboot sign-bootloader-hab: sign HAB image with 1024-bit RSA keys, CA flag not set in 46ms # skip signed image not found
ok 39 secboot sign-bootloader-hab: sign HAB image with P-384 ECDSA keys in 45ms # skip signed image not found
ok 40 secboot sign-bootloader-hab: sign HAB image with P-521 ECDSA keys, CA flag not set in 46ms # skip signed image not found
ok 41 secboot sign-kernel: check help output in 4240ms
ok 42 secboot sign-kernel: run without parameters in 4186ms
ok 43 secboot sign-kernel: attempt to sign kernel FIT without images unpack in 7587ms
ok 44 secboot sign-kernel: invalid parameters or bad input in 82619ms
ok 45 secboot sign-kernel: image with unsupported kernel format in 63762ms
ok 46 secboot sign-kernel: unsupported machine in 73454ms
ok 47 secboot sign-kernel: sign with test key in 43ms # skip signed image not found
ok 48 union: run without parameters in 4475ms
ok 49 union: check help output in 4228ms
ok 50 union: invalid changes directory in 60493ms
ok 51 union: create branch without images unpack in 7543ms
ok 52 union: create branch using --changes-directory in 87898ms
ok 53 union: create branch using multiple --changes-directory in 90316ms
ok 54 union: create branch using storage and check credentials in 128280ms
ok 55 union: create branch using --changes-directory and check credentials in 121730ms
ok 56 union: create branch using --changes-directory and check credentials for symbolic links in 97276ms
ok 57 deploy: run without parameters in 4225ms
ok 58 deploy: check help output in 4229ms
ok 59 deploy: deploy changes to Tezi image without images unpack in 9175ms
ok 60 deploy: deploy changes to Tezi image in 169105ms
ok 61 deploy: deploy changes to device without images unpack in 7075ms
ok 62 deploy: deploy changes to device in 106776ms
ok 63 deploy: check with --image-autoinstall in 625003ms
ok 64 deploy: check with --image-autoreboot in 334108ms
ok 65 isolate: run without parameters in 4947ms
ok 66 isolate: check help output in 4193ms
ok 67 isolate: create output directory when it doesn't exist and --changes-directory is used in 9441ms
ok 68 isolate: remove the output directory when it exists and both, --changes-directory and --force are used in 6995ms
ok 69 isolate: don't remove the output directory if it exists, --changes-directory is used and --force was not passed in 5259ms
ok 70 isolate: don't remove the output directory if 'storage' is being used and --force was not passed in 67387ms
ok 71 isolate: remove the output directory if 'storage' is being used and --force was passed in 73861ms
ok 72 isolate: isolate changes using workdir in 75296ms
ok 73 isolate: isolate changes using storage in 71062ms
ok 74 isolate: isolate changes and save credentials using storage in 84704ms
ok 75 isolate: isolate changes and save credentials using --changes-directory in 80819ms
ok 76 isolate: isolate symbolic links using storage in 11964ms
ok 77 isolate: isolate symbolic links using --changes-directory in 8713ms
ok 78 dt: run without parameters in 4216ms
ok 79 dt: check help output in 4257ms
ok 80 dt: checkout device tree overlays directory without images unpack in 5049ms
ok 81 dt: checkout device tree overlays directory in 61383ms
ok 82 dt: checkout updates device tree directory in 69800ms
ok 83 dt: check currently enabled device tree without images unpack in 7524ms
ok 84 dt: check currently enabled device tree in 59885ms
ok 85 dt: apply device tree in the image without images unpack in 7641ms
ok 86 dt: apply device tree in the image in 66564ms
ok 87 dt: deploy device tree in the device in 66743ms
ok 88 dto: run without parameters in 4192ms
ok 89 dto: check help output in 4180ms
ok 90 dto list: run command without images unpack in 7767ms
ok 91 dto list: show compatible overlays in 97069ms
ok 92 dto apply: run command without images unpack in 7504ms
ok 93 dto apply: check proper application of overlays in 84184ms
ok 94 dto status: run command without images unpack in 7496ms
ok 95 dto status: check currently applied overlays in 78444ms
ok 96 dto remove: run command without images unpack in 7531ms
ok 97 dto remove: check removal of specific overlays (non-FIT) in 83195ms
ok 98 dto remove: check removal of specific overlays (FIT) in 40ms # skip kernel is not in FIT format
ok 99 dto remove: check removal of all overlays in 87737ms
ok 100 dto: apply overlay and deploy it on the device in 123704ms
ok 101 dto: remove overlay from the device in 133449ms
ok 102 build: check help output in 4328ms
ok 103 build: template creation in 12844ms
ok 104 build: tcbuild with error detected by YAML parser in 4244ms
ok 105 build: tcbuild with multiple validation errors in 4801ms
ok 106 build: tcbuild with variables not passed in 12916ms
ok 107 build: tcbuild with variables not passed (input section) in 5379ms
ok 108 build: re-signing of bootloader (HAB) and kernel FIT image in 47ms # skip signed image not found
ok 109 build: full customization checked on host in 171377ms
ok 110 build: full customization checked on device in 225541ms
ok 111 build: check autoinstall, autoreboot and accept-licence in 218001ms
ok 112 build: check overlays clearing in 645155ms
ok 113 build: check basic container bundling in 206202ms
ok 114 build: check container bundling with secure registry in 182137ms
ok 115 bundle: check help output in 5017ms
ok 116 bundle: check removed parameters in 16935ms
ok 117 bundle: check output directory overwriting in 72151ms
ok 118 bundle: check --file parameter in 8676ms
ok 119 bundle: check --platform parameter in 91135ms
ok 120 bundle: check without --bundle-directory parameter in 19427ms
ok 121 bundle: check with --bundle-directory parameter in 35789ms
ok 122 bundle: check double dollar sign in compose file in 67742ms
ok 123 bundle: check --dind-param parameter in 15455ms
ok 124 bundle: check --dind-env parameter in 19218ms
ok 125 bundle: check environment variable forwarding in 27029ms
ok 126 bundle: check registry pattern in 25378ms
ok 127 bundle-registry: check with secure registries without authentication in 62261ms
ok 128 bundle-registry: check with secure registry with authentication in 56908ms
ok 129 bundle-registry: check with with all registries in 36695ms
ok 130 combine: check if image directory has a valid tezi image in 33702ms
ok 131 combine: run with the deprecated --image-directory switch in 37132ms
ok 132 combine: run with the deprecated --output-directory switch in 40291ms
ok 133 combine: check without --bundle-directory parameter in 45956ms
not ok 134 combine: check with --bundle-directory parameters in 47959ms
# (from function `assert_success' in file bats/bats-assert/src/assert_success.bash, line 42,
#  in test file /root/torizoncore-builder/tests/integration/testcases/combine.bats, line 135)
#   `assert_success' failed
#
# -- command failed --
# status : 255
# output (7 lines):
#   Creating Docker Container bundle...
#   NOTE: TCB no longer expands environment variables present in the compose file.
#
#   Starting DIND container
#   Using Docker host "tcp://127.0.0.1:47985"
#   Stopping DIND container
#   The script could not access the TLS certificates which have been created by the Docker in Docker instance. Make sure /workdir/certs_20260625165803_042837.tmp is a shared location between this script and the Docker host.
# --
#
ok 135 combine: check with --image-autoinstall in 61350ms
ok 136 combine: check with --image-autoreboot in 53278ms
ok 137 kernel: run without parameters in 4323ms
ok 138 kernel: check help output in 4259ms
ok 139 kernel build_module: run without images unpack in 8428ms
not ok 140 kernel build_module: check success cases in 75034ms
# (from function `assert_success' in file bats/bats-assert/src/assert_success.bash, line 42,
#  in test file /root/torizoncore-builder/tests/integration/testcases/kernel.bats, line 97)
#   `assert_success' failed
# Unpacking Toradex Easy Installer image.
# Unpacking TorizonCore Toradex Easy Installer image.
# Importing OSTree revision 5aed2758dc618e9b36bfc4c2abf1714a9644d7c3bc4938020fb56be62e381add from local repository...
# 1321 metadata, 10377 content objects imported; 660.9 MB content written
# 0 metadata, 0 content objects imported; 0 bytes content written
# Unpacked OSTree from Toradex Easy Installer image:
#   Commit checksum: 5aed2758dc618e9b36bfc4c2abf1714a9644d7c3bc4938020fb56be62e381add
#   TorizonCore Version: 7.7.0-devel-20260625+build.483
# Building kernel module in 'source_dir0' with args ''.
#
# -- command failed --
# status : 255
# output (4 lines):
#   A toolchain is required to build the module.
#   Downloading toolchain from https://sources.toradex.com/tcb/toolchains/crosstool-ng-13.3-aarch64-aarch64-unknown-linux-gnu.tar.xz.
#   Please wait this could take a while...
#   The requested toolchain could not be downloaded
# --
#
ok 141 kernel {set,get,clear}_custom_args: check basic errors in 78108ms
ok 142 kernel {set,get,clear}_custom_args: check unsupported bootargs passing in 78529ms
ok 143 kernel {set,get,clear}_custom_args: check success cases in 91320ms
ok 144 ostree: run without parameters in 4273ms
ok 145 ostree: check help output in 4233ms
ok 146 ostree serve: check help output in 4167ms
ok 147 ostree serve: run without images unpack in 7582ms
ok 148 ostree serve: serve repo from storage in 65108ms
ok 149 ostree serve: serve repo from storage and check if it's reachable by device in 70898ms
ok 150 ostree serve: serve repo from external directory in 69412ms
ok 151 ostree serve: serve repo from external directory and check if it's reachable by device in 69515ms
ok 152 platform push: check help output in 4290ms
ok 153 platform push: multibyte characters in arguments in 16887ms
ok 154 platform push: control characters in arguments in 16901ms
ok 155 platform push: docker-compose canonicalization errors in 21033ms
ok 156 platform push: docker-compose canonicalization with canonical compose files in 4273ms
ok 157 platform push: docker-compose canonicalization (DockerHub without authentication) in 9209ms
ok 158 platform push: docker-compose canonicalization (DockerHub with authentication) in 34ms # skip DockerHub credentials not set
ok 159 platform push: docker-compose canonicalization (GCR without authentication) in 5710ms
ok 160 platform push: docker-compose canonicalization (DockerHub with required authentication) in 32ms # skip not implemented
ok 161 platform push: docker-compose canonicalization (GCR with required authentication) in 32ms # skip not implemented
ok 162 platform push: docker-compose canonicalization (OCI images) in 23131ms
ok 163 platform push: docker-compose canonicalization (insecure registry) in 5122ms
ok 164 platform push: docker-compose canonicalization (secure registry without authentication) in 5649ms
ok 165 platform push: docker-compose canonicalization (secure registry with authentication) in 6554ms
ok 166 platform provisioning-data: offline-provisioning in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 167 platform provisioning-data: online-provisioning in 38ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 168 platform provisioning-data: online+offline-provisioning in 38ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 169 platform push: test push with docker-compose files in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 170 platform push: test push with generic package files in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 171 platform push: test push with TorizonCore images in 39ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 172 platform lockbox: test advanced registry access in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 173 platform lockbox: generate lockbox from 32-bit Docker+OCI images in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 174 platform lockbox: generate lockbox from 64-bit Docker+OCI images in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 175 platform lockbox: generate lockbox from mixed single/multi-platform Docker+OCI images in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 176 platform lockbox: generate lockbox from multi-platform Docker+OCI images in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 177 platform lockbox: fail when referencing same image by different names in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 178 platform lockbox: check --dind-param parameter in 38ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 179 platform lockbox: check --dind-env parameter in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 180 platform static-delta: generate static delta without pushing to platform in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 181 platform push: fuse errors in 37ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 182 platform push: test push with fuses packages in 38ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 183 platform push-bootloader: errors cases in 36ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 184 platform push-bootloader: success cases in 38ms # skip TCB_OTA_CREDENTIALS_PWD not set
ok 185 push: check deprecated in 4229ms
ok 186 splash: run without parameters in 4176ms
ok 187 splash: check help output in 4178ms
ok 188 splash: run with the deprecated --image switch in 4289ms
ok 189 splash: run with the deprecated --work-dir switch in 4210ms
ok 190 splash: create splash initramfs without images unpack in 7510ms
ok 191 splash: create splash in 72138ms
ok 192 splash-config: run without parameters in 4212ms
ok 193 splash-config: check help output in 4227ms
ok 194 splash-config set: without parameters in 4275ms
ok 195 splash-config set: check help output in 4322ms
ok 196 splash-config set: without images unpack in 7544ms
ok 197 splash-config set: file not found in 59987ms
ok 198 splash-config set: standard success case in 78386ms
ok 199 splash-config dump: check help output in 4194ms
ok 200 splash-config dump: without images unpack in 7545ms
ok 201 splash-config dump: success case default configuration in 60582ms
ok 202 splash-config dump: success case custom configuration in 79713ms
ok 203 splash-config dump: success case output to file in 80281ms
ok 204 ubootenv: check help output in 4237ms
ok 205 ubootenv fuses: basic tests in 71420ms

The tests that did not pass were not due to bugs in the implementation, but rather limitations of the current test setup: either the new crosstool-ng toolchain is not yet available for download at https://sources.toradex.com/tcb/toolchains/ so the tests could not fetch it automatically, or the test environment used Docker-in-Docker (DinD), which conflicted with tests that also rely on DinD internally.

To validate the kernel module build functionality manually, the toolchain was placed directly in /storage/toolchain and the relevant test was run manually. The full process can be seen here:

verdin-imx8mp-14773124:~/torizoncore-builder# torizoncore-builder images download --remote-host 10.22.1.154 --remote-password 1
Storage not empty. Delete current image before continuing? [y/N] y
Downloading image from: https://artifacts.toradex.com/artifactory/torizoncore-oe-prerelease-frankfurt/scarthgap-7.x.y/nightly/483/verdin-imx8mp/torizon/torizon-docker/oedeploy/torizon-docker-verdin-imx8mp-Tezi_7.7.0-devel-20260625+build.483.tar

The download may take some time. Please wait...
Download Complete!

Unpacking Toradex Easy Installer image.
Unpacking TorizonCore Toradex Easy Installer image.
Importing OSTree revision 5aed2758dc618e9b36bfc4c2abf1714a9644d7c3bc4938020fb56be62e381add from local repository...
1321 metadata, 10377 content objects imported; 660.9 MB content written                                                                                                
0 metadata, 0 content objects imported; 0 bytes content written                                                                                                        
Unpacked OSTree from Toradex Easy Installer image:
  Commit checksum: 5aed2758dc618e9b36bfc4c2abf1714a9644d7c3bc4938020fb56be62e381add
  TorizonCore Version: 7.7.0-devel-20260625+build.483
verdin-imx8mp-14773124:~/torizoncore-builder# mkdir /var/lib/docker/volumes/storage/_data/toolchain
verdin-imx8mp-14773124:~/torizoncore-builder# cp /root/crosstool-ng-13.3-aarch64-aarch64-unknown-linux-gnu.tar.xz /var/lib/docker/volumes/storage/_data/toolchain
verdin-imx8mp-14773124:~/torizoncore-builder# cd /var/lib/docker/volumes/storage/_data/toolchain
verdin-imx8mp-14773124:/var/lib/docker/volumes/storage/_data/toolchain# tar -xJf crosstool-ng-13.3-aarch64-aarch64-unknown-linux-gnu.tar.xz 
verdin-imx8mp-14773124:/var/lib/docker/volumes/storage/_data/toolchain# cd /root/torizoncore-builder/
verdin-imx8mp-14773124:~/torizoncore-builder# torizoncore-builder kernel build_module hello-mod
make: Entering directory '/workdir/hello-mod'
make -C /storage/linux M=/workdir/hello-mod
make[1]: Entering directory '/storage/linux'
  CC [M]  /workdir/hello-mod/hello.o
  MODPOST /workdir/hello-mod/Module.symvers
  CC [M]  /workdir/hello-mod/hello.mod.o
  LD [M]  /workdir/hello-mod/hello.ko
make[1]: Leaving directory '/storage/linux'
make: Leaving directory '/workdir/hello-mod'

Kernel module(s) successfully built and ready to deploy.
All kernel module(s) have been built and prepared.
verdin-imx8mp-14773124:~/torizoncore-builder# torizoncore-builder union --subject "Hello Mod" custom-kernel-mod
Applying changes from STORAGE/kernel.
Commit 1947819242f0db15e3730d509dae57e9407ecfc76572710d68b395a8b9e408f8 has been generated for changes and is ready to be deployed.
verdin-imx8mp-14773124:~/torizoncore-builder# torizoncore-builder deploy --remote-host 10.22.1.154 --remote-password 1 custom-kernel-mod
WARNING: Beware that artifacts not managed by OSTree (e.g. bootloader, container images, platform provisioning data, fuse values, U-Boot environment) will not be deployed by this operation.
Pulling OSTree with ref custom-kernel-mod (checksum 1947819242f0db15e3730d509dae57e9407ecfc76572710d68b395a8b9e408f8) from local archive repository...
Starting http server to serve OSTree.
OSTree server listening on "localhost:35639".
f3aa43f0ee75f956748fee1a4bc056f4a71eec763efa2c2f132b5ebfc4b82d45
'verdin-imx8mp'
Starting OSTree pull on the device...
Deploying new OSTree on the device...
Deploying successfully finished.
Please reboot the device to boot into the new deployment.

Target machine:

torizon@verdin-imx8mp-15247109:~$ sudo modprobe hello
Password: 
torizon@verdin-imx8mp-15247109:~$ dmesg | grep Hello
[   50.653128] Hello World!
torizon@verdin-imx8mp-15247109:~$ lsmod | grep hello
hello                  12288  0

Limitations / Future work

Secure Boot was not addressed in this ticket and remains unchanged.

AVAL tests were not configured for ARM64. Tests requiring physical devices depend on internal runners that are not available for ARM64, and no changes were made on that side. The following test jobs remain unimplemented:

  • test-common-torizon-* ARM64 variants
  • test-torizoncore ARM64 variant — externally triggered by Jenkins via BUILD_MACHINE, requiring separate Jenkins-side changes to select the ARM64 image
  • WSL test ARM64 variants — WSL is Windows-only with no ARM64 runner equivalent

Most of the Dockerfile already supports ARM64 through the IMAGE_ARCH
build argument. This commit adds a comment at the top explaining how
to use IMAGE_ARCH for ARM64 builds, and adapts the Go installation in
the skopeo-builder stage to download the correct binary for the target
architecture

Related-to: TCB-882
Signed-off-by: Gabriel Almeida <gabriel.almeida@toradex.com>

# Toolchain versions used when the host is aarch64 and the target is also aarch64.
# These are built with crosstool-ng and have a different naming convention.
IMAGE_MAJOR_TO_GCC_MAP_AARCH64_NATIVE = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the time of writing Torizon 6 is still supported. Should we provide a toolchain in this case as well? Or was it already decided that we only support Torizon 7?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or was it already decided that we only support Torizon 7?

As far as I know nothing was decided yet. I think we should have the arm64 host toolchains available for Torizon OS 6 as well given that it is currently supported.

# Toolchain versions used when the host is aarch64 and the target is also aarch64.
# These are built with crosstool-ng and have a different naming convention.
IMAGE_MAJOR_TO_GCC_MAP_AARCH64_NATIVE = {
7: "crosstool-ng-13.3"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this toolchain coming from exactly? Is it part of the TCB container somehow? I don't see in the download sources with the other toolchains: https://sources.toradex.com/tcb/toolchains/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a point that I forgot to explain in my PR description. I haven't put the toolchain on https://sources.toradex.com/tcb/toolchains/ yet, because I didn't know if you guys would accept this method. So, just for my local test, I put the toolchain locally in the Docker's /storage. Do you want me to upload this toolchain before continuing this review?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the toolchain reliably works I don't have any objections.

But I'll let @lucas-akira and @rborn-tx chime in here as well with their thoughts.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it's a bit concerning to publish and use a toolchain that was built on someone's machine with an unknown setup. Since the toolchain was built by Toradex, it means we'd be responsible for it.

Say a user complains about some setting used for building the toolchain. We would have trouble to generate a new version only changing that setting since we wouldn't know the original settings.

Given the above, if the only option is to build the toolchain ourselves, then I think we should set up a new CI project for building and possibly publishing a toolchain with a well-tracked setup.

This is of course not a small topic we should discuss.

Cc @griloHBG

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an extra piece of information regarding your concern: I used Claude Code to build a crosstool-ng setup using Docker to standardize this toolchain build. When I started this project, I wasn't expecting to improve or maintain it, but after some improvements, it could be a good starting point for the CI project mentioned, at least to see all the versions I used.

Comment thread tcbuilder/backend/kernel.py Outdated
Gabriel Almeida added 2 commits July 1, 2026 09:04
To support building kernel modules on an ARM64 host, a new native
toolchain built with crosstool-ng was introduced. The existing
arm-gnu-toolchain-13.3 is not compatible with ARM64 native builds,
so a dedicated toolchain was built matching the following package
versions: gcc 13.3, linux kernel 4.20.17, binutils 2.42, glibc 2.38,
gdb 14.2, gmp 6.2.1, mpfr 4.2.1, mpc 1.3.1 and isl 0.26.

When the host is ARM64 and the target is ARM32, the existing
arm-gnu-toolchain cross-compiler is used (e.g.
arm-gnu-toolchain-13.3.rel1-aarch64-arm-none-linux-gnueabihf),
following the same naming convention already in place for x86_64 hosts.
No additional toolchain is needed for this case.

Related-to: TCB-882
Signed-off-by: Gabriel Almeida <gabriel.almeida@toradex.com>
Extend the pipeline to natively build, test, and publish the ARM64
variant of torizoncore-builder, following the ARM64 host support
added to the Dockerfile and kernel backend.

Changes cover the pre-build lint, build-multiarch, test, deploy, and
deploy-multiarch stages. All multiarch manifest jobs now include both
amd64 and arm64. Previously commented-out ARM64 deploy jobs for the
early-access and internal release tracks are now active.

Not implemented:
- test-common-torizon-* ARM64 variants

- test-torizoncore ARM64 variant: this job is externally triggered
  by Jenkins via BUILD_MACHINE and requires separate Jenkins-side
  changes to select the ARM64 image

- WSL test ARM64 variants: WSL is Windows-only with no ARM64 runner
  equivalent

Related-to: TCB-882
Signed-off-by: Gabriel Almeida <gabriel.almeida@toradex.com>
Add ARM64 env vars (ARM64_RUNNER, ARM64_ARCH, ARM64_IMAGE_NAME) with
ARM64_RUNNER centralizing the runner label for easy future updates. Also
add lint-python-arm64 job running pylint inside a native ARM64 container
and add arm64-build-test-torizoncore6 job to validate the ARM64 TCB image
build and integration tests on ubuntu-24.04-arm runners

Related-to: TCB-882
Signed-off-by: Gabriel Almeida <gabriel.almeida@toradex.com>
@@ -239,10 +264,13 @@ def download_toolchain(toolchain, toolchain_path, version_gcc):
"""Download toolchain from online if it doesn't already exist"""

url_prefix = "https://sources.toradex.com/tcb/toolchains/"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't the main purpose of this PR, but I think it would be nice to have URL fallback options when downloading the toolchains.

I imagined something like this: try to download the toolchain from our servers, and if that's not possible for whatever reason e.g. outage, then try to download the toolchain from the official ARM GNU toolchain URL. If that fails, then raise an exception.

This can be implemented for currently supported images (Torizon OS 6 and 7).

Comment on lines +95 to +119
lint-python-arm64:
runs-on: ${ARM64_RUNNER}
steps:
- name: Checkout PR merge branch
uses: actions/checkout@v4

- name: Build dev container tcbuilder-dev-arm64
run: docker build . -f torizoncore-builder.Dockerfile
--build-arg IMAGE_ARCH=linux/arm64/v8
--target tcbuilder-dev
--tag tcbuilder-dev-arm64

# Run the code linter disabling only docstring related warnings, use this run as exit code
# IMPORTANT: when running locally, DO NOT pass these --disable arguments to the program
- name: Run pylint
run: docker run --rm -v ${GITHUB_WORKSPACE}:/work -w /work tcbuilder-dev-arm64
pylint --output-format=colorized
--disable=missing-module-docstring
--disable=missing-class-docstring
--disable=missing-function-docstring
--disable=fixme
$(find tcbuilder/ -type f -name "*.py") *.py || echo "exit_code=$?" >> ${GITHUB_ENV}
- name: Pylint result
run: exit ${exit_code}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to run the Python linter on an arm64 host? Its results should be independent from the host architecture, so I don't see the need for this job.

Comment thread .gitlab-ci.yml
Comment on lines +138 to +145
lint-python-arm64:
extends: .lint-python-base
tags:
- arm64
variables:
ARCH_SUFFIX: arm64
IMAGE_ARCH: linux/arm64/v8
DEV_CACHE_TAG: tcbuilder-dev-arm64-latest

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same point I made on the corresponding GitHub Actions entry: I don't think we need to run the linters on a different host architecture.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I'll remove it.

# Toolchain versions used when the host is aarch64 and the target is also aarch64.
# These are built with crosstool-ng and have a different naming convention.
IMAGE_MAJOR_TO_GCC_MAP_AARCH64_NATIVE = {
7: "crosstool-ng-13.3"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it's a bit concerning to publish and use a toolchain that was built on someone's machine with an unknown setup. Since the toolchain was built by Toradex, it means we'd be responsible for it.

Say a user complains about some setting used for building the toolchain. We would have trouble to generate a new version only changing that setting since we wouldn't know the original settings.

Given the above, if the only option is to build the toolchain ourselves, then I think we should set up a new CI project for building and possibly publishing a toolchain with a well-tracked setup.

This is of course not a small topic we should discuss.

Cc @griloHBG

Comment on lines 121 to 140
toolchain_path = os.path.join(os.path.dirname(linux_src), "toolchain")
host = _host_arch()
if arch == "arm":
c_c = "arm-none-linux-gnueabihf-"
toolchain = os.path.join(
toolchain_path, f"{version_gcc}-x86_64-arm-none-linux-gnueabihf/bin")
toolchain_path, f"{version_gcc}-{host}-arm-none-linux-gnueabihf/bin")
elif arch == "arm64":
c_c = "aarch64-none-linux-gnu-"
toolchain = os.path.join(
toolchain_path, f"{version_gcc}-x86_64-aarch64-none-linux-gnu/bin")
if host == "aarch64":
# On aarch64 hosts, use a crosstool-ng-built native toolchain.
version_gcc = IMAGE_MAJOR_TO_GCC_MAP_AARCH64_NATIVE.get(image_major_version)
assert version_gcc, "Unable to determine native aarch64 GCC toolchain version"
c_c = "aarch64-unknown-linux-gnu-"
toolchain = os.path.join(
toolchain_path, f"{version_gcc}-{host}-aarch64-unknown-linux-gnu/bin")
else:
c_c = "aarch64-none-linux-gnu-"
toolchain = os.path.join(
toolchain_path, f"{version_gcc}-{host}-aarch64-none-linux-gnu/bin")
else:
assert False, "build_module: Unhandled architecture"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea for consideration:

Now that we have two dimensions being used to select the toolchain, maybe we could refactor this code to be more declarative. Instead of having logic to define the cross-compiler prefix and toolchain names we could have a 2-dimensional variable in this form:

CROSS_COMPILERS = {
    ("<host-arch>", "<target-arch>"): ("<c_c>", "<toolchain>")
}

where <host-arch> comes from host and <target-arch> comes from arch (I'd actually rename that variable to target.

I think this would be helpful for readability, but before settling on that let me ask @jsrc27 and @lucas-akira opinions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that if the custom-built toolchain is needed, we would need to somehow maintain it.

But what could we do to no have this burden? We can discuss in a meeting.

But one idea: whatif we use a newer toolchain version, which IIRC has both ARM64->ARM32 and ARM64->ARM64 (the missing one) available as pre-built?

Which issues could happen?

Comment thread .gitlab-ci.yml
Comment on lines +502 to +505
# ---
# ARM64 host test variants
# Each job mirrors its amd64 counterpart but pulls torizoncore-builder-arm64.
# ---

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the majority of TCB's code is independent of the host architecture, I'd guess we are not adding much value by mirroring these various tests from amd64.

Maybe we could consider something more focused such as running the "host tests" for a single image and then only the kernel build_module command (and other commands that we know could be impacted by the host arch) with various image combinations? This should save energy, time and money 😉...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense. I'll update it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants