resources: Add arm-ubuntu disk image resources

This disk image is built based on Ubuntu's aarch64 cloud image
with essential processes only. The disk image can also be used
with gem5 on kvm-enabled ARM host.

Signed-off-by: Hoa Nguyen <hoanguyen@ucdavis.edu>
Change-Id: I591451d4dad15a6ca8579080d5ea02012aacd07c
Signed-off-by: Kaustav Goswami <kggoswami@ucdavis.edu>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5-resources/+/58550
Tested-by: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
diff --git a/src/arm-ubuntu/README.md b/src/arm-ubuntu/README.md
new file mode 100644
index 0000000..42d782d
--- /dev/null
+++ b/src/arm-ubuntu/README.md
@@ -0,0 +1,183 @@
+---
+title: Linux arm-ubuntu image
+tags:
+    - arm
+    - fullsystem
+layout: default
+permalink: resources/arm-ubuntu
+shortdoc: >
+    Resources to build a generic arm-ubuntu disk image.
+author: ["Hoa Nguyen", "Kaustav Goswami"]
+---
+
+This document provides instructions to create the "arm-ubuntu" image and
+points to the gem5 component that would work with the disk image. The
+arm-ubuntu disk image is based on Ubuntu's server cloud image for
+arm available at (https://cloud-images.ubuntu.com/focal/current/).
+The `.bashrc` file would be modified in such a way that it executes
+a script passed from the gem5 configuration files (using the `m5 readfile`
+instruction).
+
+The instructions for bringing up QEMU emulation are based on
+[Ubuntu's Wiki](https://wiki.ubuntu.com/ARM64/QEMU),
+and the instructions for creating a cloud disk image are based on
+[this guide](https://gist.github.com/oznu/ac9efae7c24fd1f37f1d933254587aa4).
+More information about cloud-init can be found
+[here](https://cloudinit.readthedocs.io/en/latest/topics/examples.html).
+
+We assume the following directory structure while following the instructions
+in this README file:
+
+```
+arm-ubuntu/
+  |___ gem5/                                   # gem5 source code (to be cloned here)
+  |
+  |___ disk-image/
+  |      |___ aarch64-ubuntu.img               # The ubuntu disk image should be downloaded and modified here
+  |      |___ shared/                          # Auxiliary files needed for disk creation
+  |      |      |___ serial-getty@.service     # Auto-login script
+  |      |___ arm-ubuntu/
+  |             |___ cloud.txt                 # the cloud config, to be created
+  |             |___ gem5_init.sh              # The script to be appended to .bashrc on the disk image
+  |             |___ post-installation.sh      # The script manipulating the disk image
+  |             |___ arm-ubuntu.json           # The Packer script
+  |
+  |
+  |___ README.md                               # This README file
+```
+
+## Building the disk image
+
+This requires an ARM cross compiler to be installed. The disk image is a 64-bit
+ARM 64 (aarch64) disk iamge. Therefore, we only focus on the 64-bit version of
+the cross compiler. It can be installed by:
+
+```sh
+sudo apt-get install aarch64-linux-gnu-g++-10 aarch64-linux-gnu-gcc-10
+```
+
+In order to build the ARM based Ubuntu disk-image for with gem5, build the m5
+utility in `gem5/util/m5` using the following:
+
+```sh
+git clone https://gem5.googlesource.com/public/gem5
+cd gem5/util/m5
+scons build/arm64/out/m5
+```
+
+Troubleshooting: You may need to edit the SConscript to point to the correct
+cross compiler.
+```
+...
+main['CXX'] = '${CROSS_COMPILE}g++-10'
+...
+```
+
+# Installing QEMU for aarch64
+
+On the host machine,
+
+```sh
+sudo apt-get install qemu-system-arm qemu-efi
+```
+
+# Installing cloud utilities
+
+On the host machine,
+
+```sh
+sudo apt-get install cloud-utils
+```
+
+# Downloading the cloud disk image
+
+In the `arm-ubuntu/disk-image/` directory,
+
+```sh
+wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img
+```
+
+# Booting the disk image using QEMU
+
+## Making a cloud config file
+
+First, we need a cloud config that will have the authorization information
+keys for logging in the machine.
+
+In the `arm-ubuntu/disk-image/arm-ubuntu` directory, create a `cloud.txt` file
+with the following content,
+
+```sh
+#cloud-config
+users:
+  - name: ubuntu                            <- change this name to the current user (use `whoami`)
+    ssh-authorized-keys:
+      - ssh-rsa AAAAABBCCCCCCCrNJfweeeeee   <- insert the rsa key here (typically `cat ~/.ssh/id_rsa.pub`)
+    sudo: ['ALL=(ALL) NOPASSWD:ALL']
+    groups: sudo
+    shell: /bin/bash
+    homedir: /home/ubuntu                   <- change this to the home directory of `whoami`
+```
+
+* Note: Do not leave stray spaces in the `cloud.txt` file.
+
+More information about generating an ssh rsa key is available
+[here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key).
+You can ignore the GitHub email address part.
+
+## Booting the cloud disk image with the cloud config file
+
+```sh
+dd if=/dev/zero of=flash0.img bs=1M count=64
+dd if=/usr/share/qemu-efi/QEMU_EFI.fd of=flash0.img conv=notrunc
+dd if=/dev/zero of=flash1.img bs=1M count=64
+cloud-localds --disk-format qcow2 cloud.img arm-ubuntu/cloud.txt
+wget https://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd
+qemu-system-aarch64 \
+    -smp 2 \
+    -m 1024 \
+    -M virt \
+    -cpu cortex-a57 \
+    -bios QEMU_EFI.fd \
+    -nographic \
+    -device virtio-blk-device,drive=image \
+    -drive if=none,id=image,file=focal-server-cloudimg-arm64.img \
+    -device virtio-blk-device,drive=cloud \
+    -drive if=none,id=cloud,file=cloud.img \
+    -netdev user,id=user0 -device virtio-net-device,netdev=eth0 \
+    -netdev user,id=eth0,hostfwd=tcp::5555-:22
+```
+
+## Manipulating the disk image
+
+While the qemu instance is still running, we will use Packer to connect
+to the virtual machine and manipulate the disk image. Before doing that, we
+need to add the private key using `ssh-add`.
+
+```sh
+ssh-add ~/.ssh/id_rsa
+```
+
+This process is automated. In the `arm-ubuntu/disk-image/` directory,
+
+```sh
+chmod +x build.sh
+./build.sh
+```
+
+`build.sh` also verifies the cloud.txt and modifies the arm-ubuntu.json
+accordingly. The packer script, executed by `build.sh` disables systemd. In
+case you need to enable systemd stuff, remove the last two provisioners from
+the arm-ubuntu.json file.
+
+Note that after executing the packer script, you will not be able to emulate
+this disk image in qemu.
+
+## Preparing the disk image for gem5
+
+We need to finalize the image before we can use it with gem5. This is done by:
+
+```sh
+qemu-img convert -f qcow2 -O raw focal-server-cloudimg-arm64.img arm64-ubuntu-focal-server.img
+rm focal-server-cloudimg-arm64.img
+```
diff --git a/src/arm-ubuntu/disk-image/arm-ubuntu/arm-ubuntu.json b/src/arm-ubuntu/disk-image/arm-ubuntu/arm-ubuntu.json
new file mode 100644
index 0000000..ef38793
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/arm-ubuntu/arm-ubuntu.json
@@ -0,0 +1,83 @@
+{
+    "_author": "Hoa Nguyen <hoanguyen@ucdavis.edu>, Kaustav Goswami <kggoswami@ucdavis.edu>",
+    "_license": "Copyright (c) 2022 The Regents of the University of California. SPDX-License-Identifier: BSD 3-Clause",
+    "builders": [
+        {
+            "type": "null",
+            "ssh_host": "localhost",
+            "ssh_port": "5555",
+            "ssh_username": "ubuntu",
+            "ssh_agent_auth": true,
+            "ssh_ciphers":  ["aes128-gcm@openssh.com", "chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr"],
+            "ssh_certificate_file": "~/.ssh/id_rsa",
+            "ssh_clear_authorized_keys": true
+        }
+    ],
+    "provisioners": [
+        {
+            "type": "file",
+            "source": "arm-ubuntu/init.gem5",
+            "destination": "/home/ubuntu/"
+        },
+        {
+            "type": "shell",
+            "inline": "cd /home/ubuntu; chmod 775 init.gem5"
+        },
+        {
+            "type": "file",
+            "source": "../gem5/util/m5/build/arm64/out/m5",
+            "destination": "/home/ubuntu/"
+        },
+        {
+            "type": "file",
+            "source": "arm-ubuntu/gem5_init.sh",
+            "destination": "/home/ubuntu/gem5_init.sh"
+        },
+        {
+            "type": "shell",
+            "inline": "cd /home/ubuntu; chmod +x gem5_init.sh"
+        },
+        {
+            "type": "file",
+            "source": "shared/serial-getty@.service",
+            "destination": "/home/ubuntu/serial-getty@.service"
+        },
+        {
+            "type": "file",
+            "source": "arm-ubuntu/post-installation.sh",
+            "destination": "/home/ubuntu/post-installation.sh"
+        },
+        {
+            "type": "shell",
+            "inline": "cd /home/ubuntu; chmod +x post-installation.sh; sudo ./post-installation.sh; rm post-installation.sh"
+        },
+        {
+            "type": "shell",
+            "inline": "sudo cp /home/ubuntu/init.gem5 /sbin/.."
+        },
+        {
+            "type": "shell",
+            "inline": "sudo cp /home/ubuntu/init.gem5 /"
+        },
+        {
+            "type": "shell",
+            "inline": "sudo mkdir /data"
+        },
+        {
+            "type": "shell",
+            "inline": "sudo mv /sbin/init /sbin/init_old"
+        },
+        {
+            "type": "shell",
+            "inline": "cd /sbin; sudo ln -s ../init.gem5 init"
+        },
+        {
+            "type": "shell",
+            "inline": "sudo apt-get -y remove --purge landscape-common"
+        },
+        {
+            "type": "shell",
+            "inline": "sudo apt-get -y remove lsb-release"
+        }
+    ]
+}
diff --git a/src/arm-ubuntu/disk-image/arm-ubuntu/cloud.txt b/src/arm-ubuntu/disk-image/arm-ubuntu/cloud.txt
new file mode 100644
index 0000000..4d22189
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/arm-ubuntu/cloud.txt
@@ -0,0 +1,9 @@
+#cloud-config
+users:
+  - name: ### insert the username here ###
+    ssh-authorized-keys:
+      - ssh-rsa ### insert the rsa key here ###
+    sudo: ['ALL=(ALL) NOPASSWD:ALL']
+    groups: sudo
+    shell: /bin/bash
+    homedir: ### insert the home directory here ###
diff --git a/src/arm-ubuntu/disk-image/arm-ubuntu/gem5_init.sh b/src/arm-ubuntu/disk-image/arm-ubuntu/gem5_init.sh
new file mode 100755
index 0000000..39b0a89
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/arm-ubuntu/gem5_init.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Copyright (c) 2022 The University of California
+# Copyright (c) 2021 The University of Texas at Austin.
+# SPDX-License-Identifier: BSD 3-Clause
+
+echo "Starting gem5 init... reading run script file."
+# Calling m5 with --addr as m5ops_base address.
+if ! m5 --addr 0x10010000 readfile > /tmp/script; then
+    echo "Failed to run m5 readfile, exiting!"
+    rm -f /tmp/script
+    if ! m5 exit; then
+        # Useful for booting the disk image in (e.g.,) qemu for debugging.
+        echo "m5 exit failed, dropping to shell."
+        /bin/sh
+    fi
+else
+    echo "Running m5 script from /tmp/script"
+    chmod 755 /tmp/script
+    /tmp/script || true
+    echo "Done running script, exiting."
+    rm -f /tmp/script
+    m5 --addr 0x10010000 exit
+fi
diff --git a/src/arm-ubuntu/disk-image/arm-ubuntu/init.gem5 b/src/arm-ubuntu/disk-image/arm-ubuntu/init.gem5
new file mode 100644
index 0000000..9059445
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/arm-ubuntu/init.gem5
@@ -0,0 +1,15 @@
+#!/bin/sh
+# This script is taken from ubuntu-18.04-arm64-docker.img, hosted
+# on gem5 guest binaries.
+set -ex
+mount -t proc - /proc
+mount -t sysfs - /sys
+mount -t debugfs - /sys/kernel/debug/
+for DEV in /dev/vdb1; do
+  if [ -e "${DEV}" ]; then
+    mount "${DEV}" /data
+    break
+  fi
+done
+exec /sbin/getty -a root -L ttyAMA0 vt102
+
diff --git a/src/arm-ubuntu/disk-image/arm-ubuntu/post-installation.sh b/src/arm-ubuntu/disk-image/arm-ubuntu/post-installation.sh
new file mode 100755
index 0000000..1d0bae2
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/arm-ubuntu/post-installation.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Copyright (c) 2022 The Regents of the University of California.
+# SPDX-License-Identifier: BSD 3-Clause
+
+mv /home/ubuntu/serial-getty@.service /lib/systemd/system/
+
+mv /home/ubuntu/m5 /sbin
+ln -s /sbin/m5 /sbin/gem5
+
+mv /home/ubuntu/gem5_init.sh /root/
+chmod +x /root/gem5_init.sh
+echo "/root/gem5_init.sh" >> /root/.bashrc
+
diff --git a/src/arm-ubuntu/disk-image/build.sh b/src/arm-ubuntu/disk-image/build.sh
new file mode 100644
index 0000000..86332d2
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/build.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+# Copyright (c) 2022 The Regents of the University of California.
+# SPDX-License-Identifier: BSD 3-Clause
+
+# This file validates the `cloud.txt` and `arm-ubuntu.json` files before
+# starting packer.
+
+USER=`whoami`
+HOSTNAME=`hostname`
+
+# Check if the  USER is `ubuntu`, otherwise tell the user to configure
+# ssh-keygen and cloud.txt file.
+
+if [ ! $USER = "ubuntu" ]; then
+    echo "The username is $USER"
+    echo "The script expects that the user HAVE ALREADY executed \"ssh-keygen\""
+
+    # Checking for the cloud.txt file
+
+    if [ ! -f "arm-ubuntu/cloud.txt" ]; then
+        echo "cloud.txt not found! Please refer to the README.md file on how to create the cloud.txt file."
+        exit
+    else
+
+        # cloud.txt file exists. Need to check whether it is modified or not.
+
+        count=`grep "name: ubuntu" arm-ubuntu/cloud.txt |wc -l`
+        if [ $count -ne 0 ]; then
+            echo "cloud.txt is not modified correctly! Please refer to the README.md file on how to modify name and ssh-rsa in the cloud.txt file."
+            exit
+        fi
+
+        # Checking whether the ssh-rsa line is modified.
+
+        KEY=`grep "ssh-rsa" arm-ubuntu/cloud.txt`
+
+        if [[ "$KEY" == *"$USER@$HOSTNAME"* ]]; then
+            echo "cloud.txt verified!"
+        else
+            echo "cloud.txt is not modified correctly! Please refer to the README.md file on how to modify ssh-rsa in the cloud.txt file."
+            exit
+        fi
+    fi
+
+    echo "All files are modified accordingly."
+fi
+
+# Modifying the json script.
+
+sed "s/\/home\/ubuntu/\/home\/${USER}/g" arm-ubuntu/arm-ubuntu.json > arm-ubuntu/.arm-ubuntu.json
+mv arm-ubuntu/.arm-ubuntu.json arm-ubuntu/arm-ubuntu.json
+sed "s/\"ssh_username\": \"ubuntu\",/\"ssh_username\": \"${USER}\",/g" arm-ubuntu/arm-ubuntu.json > arm-ubuntu/.arm-ubuntu.json
+mv arm-ubuntu/.arm-ubuntu.json arm-ubuntu/arm-ubuntu.json
+
+# Modifying the post-installation script.
+
+sed "s/\/home\/ubuntu/\/home\/${USER}/g" arm-ubuntu/post-installation.sh > arm-ubuntu/.post-installation.sh
+mv arm-ubuntu/.post-installation.sh arm-ubuntu/post-installation.sh
+
+# Downloading packer
+
+PACKER_VERSION="1.8.0"
+
+if [ ! -f ./packer ]; then
+    wget https://releases.hashicorp.com/packer/${PACKER_VERSION}/packer_${PACKER_VERSION}_linux_amd64.zip;
+    unzip packer_${PACKER_VERSION}_linux_amd64.zip;
+    rm packer_${PACKER_VERSION}_linux_amd64.zip;
+fi
+
+# Validating and executing packer
+
+./packer validate arm-ubuntu/arm-ubuntu.json
+./packer build arm-ubuntu/arm-ubuntu.json
+
+exit
diff --git a/src/arm-ubuntu/disk-image/shared/serial-getty@.service b/src/arm-ubuntu/disk-image/shared/serial-getty@.service
new file mode 100644
index 0000000..b0424f0
--- /dev/null
+++ b/src/arm-ubuntu/disk-image/shared/serial-getty@.service
@@ -0,0 +1,46 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Serial Getty on %I
+Documentation=man:agetty(8) man:systemd-getty-generator(8)
+Documentation=http://0pointer.de/blog/projects/serial-console.html
+BindsTo=dev-%i.device
+After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
+After=rc-local.service
+
+# If additional gettys are spawned during boot then we should make
+# sure that this is synchronized before getty.target, even though
+# getty.target didn't actually pull it in.
+Before=getty.target
+IgnoreOnIsolate=yes
+
+# IgnoreOnIsolate causes issues with sulogin, if someone isolates
+# rescue.target or starts rescue.service from multi-user.target or
+# graphical.target.
+Conflicts=rescue.service
+Before=rescue.service
+
+[Service]
+# The '-o' option value tells agetty to replace 'login' arguments with an
+# option to preserve environment (-p), followed by '--' for safety, and then
+# the entered username.
+ExecStart=-/sbin/agetty --autologin root --keep-baud 115200,38400,9600 %I $TERM
+Type=idle
+Restart=always
+UtmpIdentifier=%I
+TTYPath=/dev/%I
+TTYReset=yes
+TTYVHangup=yes
+KillMode=process
+IgnoreSIGPIPE=no
+SendSIGHUP=yes
+
+[Install]
+WantedBy=getty.target