AWS Graviton processors expand options for embedded Linux developers
Tags: aws
Published: 2020-09-15 03:03:19

One of the big changes underway is the migration of the Arm architecture to the desktop and to the cloud.

[Docker Desktop](https://www.docker.com/products/docker-desktop) and [QEMU](https://www.qemu.org/) can be used to build and run Linux applications targeted for the Arm architecture on x86 machines, but the developer experience is not the same as developing on an Arm computer. Using AWS Graviton processors, embedded Linux developers can perform many tasks in a shorter time with more flexibility.

Embedded Linux systems are constructed from two main elements, the Linux kernel and the root file system. The traditional development process involves cross-compiling the software on an x86 machine and then programming a storage device such as an SD card or eMMC module. Once the programming process is complete the embedded system boots and runs Linux. The Raspberry Pi is a popular example of the process, but there are many systems that operate in a similar way.

Embedded systems typically have some software that is tightly coupled to the hardware. This means that developers run the software directly on the target hardware. This changes with AWS Graviton processors. Let’s look at two examples to understand the benefits of using AWS Graviton processors for embedded Linux development.

##Using Arm file systems on EC2 instances powered by AWS Graviton processors

The [NXP S32V automotive development platform](https://www.nxp.com/design/development-boards/automotive-development-platforms/s32v-mpu-platforms:AUTO-S32V-PLATFORMS) targets vision and sensor fusion applications. The software from NXP comes in the form of a [board support package](https://www.nxp.com/design/software/embedded-software/linux-software-and-development-tools/bsp-for-s32v-and-bluebox-technology:BSP-S32V?tab=Design_Tools_Tab) (BSP) for the Arm Cortex-A53 based device. The download contains the Linux kernel images, the file system, and other artifacts such as the Linux device tree and the boot loader.

Using [Graviton-based EC2 instances](https://aws.amazon.com/ec2/graviton) it’s possible to extract the file system and use the chroot command to inspect and modify the contents of the file system. Because the S32V and AWS Graviton processors share a common Arm architecture, the applications in the file system can be executed on either system.  Let’s see how to determine which version of gcc is installed in the S32V file system and to compile and run a C language example using an EC2 instance.

First, create a new EC2 instance. The instance can be any of the instance types powered by Graviton processors including A1, T4g, M6g, C6g, or R6g. There are numerous [AWS tutorials](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html) about how to create an AWS account and use the AWS Console to configure and launch a new EC2 instance. Make sure to substitute one of the above instance types in any tutorial you use.

For this example, I created a t4g.medium running Ubuntu 18.04 with a public IP address and accessible from my desktop via ssh. 

I downloaded the S32V BSP using the link above and extracted the tar file from the package which contains the root file system. I ignored all of the other Linux kernel files and SD card images.

Copy the file system tar.gz file to the created EC2 instance and extract it. Use your key file and EC2 IP address in the commands.
```console
$ scp -i key.pem fsl-image-auto-s32v234evb.tar.gz ubuntu@<ec2-ip-address>:~/
$ ssh -i key.pem ubuntu@<ec2-ip-address>
```

After connecting to the EC2 instance setup the root file system.

```console
$ mkdir fs ; cd fs
$ tar xvf ../fsl-image-auto-s32v234evb.tar.gz ; cd ..
$ sudo chroot fs /bin/bash 
```

Now, I’m in the S32V file system and the bash prompt changes.

Check the version of gcc installed.

```console
bash-4.4# gcc --version
gcc (Linaro GCC 6.3-2017.06~dev) 6.3.1 20170509
```

Here is a simple test.c program which can be compiled and run in the chroot. Edit a new file test.c and add the contents. This can be done on the EC2 machine and copied into the root fs directory or you can run vi right in the chroot and create the file.

```c
#include <stdio.h>
#include <stdlib.h>

int main()
{
    srand(1);

    double a1[1000] __attribute__((__aligned__(16)));
    double a2[1000] __attribute__((__aligned__(16)));
    double a3[1000] __attribute__((__aligned__(16)));

    double sum = 0;

    for (int i = 0; i < 1000; i++) {
        a1[i] = (rand() % 2000) - 1000;
        a2[i] = (rand() % 2000) - 1000;
    }


    for (int i = 0; i < 1000; i++) {
        a3[i] = a1[i] * a2[i];
    }

    for (int i = 0; i < 1000; i++) {
        sum += a3[i];
    }

    printf("Sum: %f\n", sum);

    return 0;
}
```

Exit the chroot and copy test.c into the fs/ directory and re-enter the chroot. 

```console
$ cp test.c fs/home/root
$ sudo chroot fs /bin/bash
bash-4.4# cd /home/root
```

Even vi works inside of the chroot to edit test.c

```console
bash-4.4# gcc -O3 -g test.c -o test
bash-4.4# ./test
Sum: 258936.000000
```
Disassemble the program using objdump. Partial output is shown here.

```console 
bash-4.4# objdump -S test | more
    double sum = 0;

    for (int i = 0; i < 1000; i++) {
        a1[i] = (rand() % 2000) - 1000;
  4004fc:       5289ba74        mov     w20, #0x4dd3                    // #19923
{
  400500:       a9025bf5        stp     x21, x22, [sp, #32]
        a1[i] = (rand() % 2000) - 1000;
  400504:       72a20c54        movk    w20, #0x1062, lsl #16
{
  400508:       a90363f7        stp     x23, x24, [sp, #48]
  40050c:       910103b5        add     x21, x29, #0x40
  400510:       8b0303b6        add     x22, x29, x3
    srand(1);
  400514:       d2800018        mov     x24, #0x0                       // #0
        a1[i] = (rand() % 2000) - 1000;
  400518:       5280fa13        mov     w19, #0x7d0                     // #2000
    for (int i = 0; i < 1000; i++) {
  40051c:       d283e817        mov     x23, #0x1f40                    // #8000
    srand(1);
  400520:       97ffffe8        bl      4004c0 <srand@plt>
        a1[i] = (rand() % 2000) - 1000;
  400524:       97ffffdb        bl      400490 <rand@plt>
  400528:       9b347c01        smull   x1, w0, w20
  40052c:       9367fc21        asr     x1, x1, #39
  400530:       4b807c21        sub     w1, w1, w0, asr #31
  400534:       1b138020        msub    w0, w1, w19, w0
  400538:       510fa000        sub     w0, w0, #0x3e8
  40053c:       1e620000        scvtf   d0, w0
  400540:       fc386aa0        str     d0, [x21, x24]
```

It looks and feels just like a native Arm Linux computer. There is no cross-compiling and the exact tools from the S32V file system are used.

Some applications will require specific hardware and will not run correctly on a general-purpose machine, but many run as expected. It is easy to see how applications can be built and tested using the same tools and libraries provided by the BSP. Everything can be inspected before taking the time to program a board and boot it up only to find out that something was not included or does not function properly.

Docker from scratch is another way to utilize Graviton-based EC2 instances for embedded Linux development.

Exit the chroot and let's try Docker from scratch.

##Using Docker with an embedded file system 

Docker provides another way to work with file systems for embedded Linux.

Install Docker on the same t4g.medium instance and make sure it can run hello-world.

```console
$ sudo apt update
$ sudo apt upgrade -y
$ curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
$ sudo usermod -aG docker ubuntu ; newgrp docker
$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```

To build a docker image from scratch using the S32V file system create a Dockerfile with your favorite editor.

```docker
FROM scratch
ADD fsl-image-auto-s32v234evb.tar.gz /
ADD test.c /home/root
CMD /bin/bash
```

Build the docker image with the Dockerfile and the root file system in the current directory.
```console
$ docker build -t s32v -f Dockerfile .
```

Run the docker image and compile the software.
```console
$ docker run -it  s32v
bash-4.4# cd /home/root
bash-4.4# gcc -O3 -g test.c -o test
bash-4.4# ./test
Sum: 258936.000000
bash-4.4# exit
```

Docker does not save the modified image automatically when a container exits. To save the modified container get the container id and use the commit command to write a new image.

```console
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                      PORTS               NAMES
fb2481cfe668        s32v                "/bin/sh -c /bin/bash"   23 seconds ago       Exited (0) 2 seconds ago                        elastic_liskov
$ docker commit fb2481cfe668 s32v
```

Images can be saved in [Docker Hub](https://hub.docker.com/) or [AWS ECR](https://aws.amazon.com/ecr/). It's also possible to save a tar file of the image and manually copy it between machines.

```console
$ docker save s32v | gzip > s32v-fs.tgz
```

The tar file from docker save is a tar file for each image layer and could be put back into the file system we started from with the modifications made. 

Docker also makes it easy to transport images between machines and offers some ease of use compared to chroot. 

Docker and chroot combined with EC2 instances powered by Graviton processors are a powerful tool for embedded Linux developers and make the development experience for Arm easier. The many EC2 configurations make it possible to right size the number of CPUs and the size of memory for a task. Compiling large projects can be done much faster and easier than on the development board or by cross-compiling.

The techniques used with the NXP S32V root file system can be applied to most any embedded Linux development board.

##Summary

AWS Graviton processors improve the developer experience for embedded Linux systems. Commands like chroot and Docker from scratch improve the developer experience by not requiring everything to be done on a development board. The cross-compile, copy, run, debug loop is more time consuming when the build machine is x86 and the target system is Arm. The variety of A1, T4g, M6g, C6g, and R6g instances make it easy to select the right hardware for best performance. 

It’s worth thinking about how AWS Graviton processors fit into your workflow for Linux on Arm development. Now is a perfect time to experiment with the new [T4g EC2 instances](https://aws.amazon.com/ec2/instance-types/t4) and take advantage of the [free trial](https://aws.amazon.com/blogs/aws/new-t4g-instances-burstable-performance-powered-by-aws-graviton2/) available until the end of 2020.