resources: Add resources to build disk image with hack back script

JIRA: https://gem5.atlassian.net/browse/GEM5-489
Change-Id: I6d10659ba3f6ea8d4e2255c70eb88996bdc43823
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5-resources/+/32754
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Hoa Nguyen <hoanguyen@ucdavis.edu>
diff --git a/src/hack-back/README.md b/src/hack-back/README.md
new file mode 100755
index 0000000..e2c2b1e
--- /dev/null
+++ b/src/hack-back/README.md
@@ -0,0 +1,63 @@
+# Hack Back Checkpointing Disk Image
+
+This document provides instructions to create a disk image with `hack_back_ckpt.rcS` script (located in `gem5/configs/boot/`) installed.
+This script creates a checkpoint once the Linux system boots up.
+Using environment variables the script makes sure that the checkpoint is not created if one already exists.
+On restoring the simulation from the checkpoint, a new script can be provided to execute the desired applications.
+
+**Note:** The instructions in this README are based on experiments with gem5-20.
+
+We assume the following directory structure while following the instructions in this README file:
+
+```
+npb/
+  |___ gem5/                                # gem5 source code
+  |
+  |___ disk-image/
+  |      |___ shared/                       # Auxiliary files needed for disk creation
+  |      |___ hack-back/
+  |            |___ hack-back-image/        # Will be created once the disk is generated
+  |            |      |___ hack-back        # The generated disk image
+  |            |___ hack-back.json          # The Packer script to build the disk image
+  |            |___ hack_back_ckpt.rcS      # Main script responsible for checkpointing
+  |            |___ post-installation.sh    # Moves hack_back_ckpt.rcS to guest's .bashrc
+  |
+  |
+  |___ README.md                           # This README file
+```
+
+## Disk Image
+
+Assuming that you are in the `src/hack-back/` directory (the directory containing this README), first build `m5` (which is needed to create the disk image):
+
+```sh
+git clone https://gem5.googlesource.com/public/gem5
+cd gem5/util/m5
+scons build/x86/out/m5
+```
+
+Next,
+
+```sh
+cd disk-image
+# if packer is not already installed
+wget https://releases.hashicorp.com/packer/1.6.0/packer_1.6.0_linux_amd64.zip
+unzip packer_1.6.0_linux_amd64.zip
+
+# validate the packer script
+./packer validate hack-back/hack-back.json
+# build the disk image
+./packer build hack-back/hack-back.json
+```
+
+Once this process succeeds, the created disk image can be found on `hack-back/hack-back-image/hack-back`.
+
+## Using this Disk Image
+
+The details of how to use this disk image are following:
+
+- On starting a gem5 simulation using this disk image, after the kernel boot, the hack back script is the first thing which will run and exit the simulation with an `exit_event` cause of `checkpoint`.
+- Your gem5 run script should be able to recognize this `exit_event` and take a checkpoint using `m5.checkpoint(m5.options.outdir)`.
+- Later, on restoring the simulation from the checkpoint, you should be able to pass a new script to run your benchmarks, which will start simulating your benchmarks right from the point where the checkpoint was taken.
+- To restore from the checkpoint (assuming that it is in the gem5 outdir), use `m5.instantiate(m5.options.outdir)`.
+- Your benchmark script can be passed using `system.readfile=[path to the script]` in your gem5 run script.
diff --git a/src/hack-back/disk-image/hack-back/hack-back.json b/src/hack-back/disk-image/hack-back/hack-back.json
new file mode 100755
index 0000000..e4d2607
--- /dev/null
+++ b/src/hack-back/disk-image/hack-back/hack-back.json
@@ -0,0 +1,99 @@
+{
+    "_author": "Hoa Nguyen <hoanguyen@ucdavis.edu>, Ayaz Akram <yazakram@ucdavis.edu>",
+    "_license": "Copyright (c) 2020 The Regents of the University of California. SPDX-License-Identifier: BSD 3-Clause",
+    "builders":
+    [
+        {
+            "type": "qemu",
+            "format": "raw",
+            "accelerator": "kvm",
+            "boot_command":
+            [
+                "{{ user `boot_command_prefix` }}",
+                "debian-installer={{ user `locale` }} auto locale={{ user `locale` }} kbd-chooser/method=us ",
+                "file=/floppy/{{ user `preseed` }} ",
+                "fb=false debconf/frontend=noninteractive ",
+                "hostname={{ user `hostname` }} ",
+                "/install/vmlinuz noapic ",
+                "initrd=/install/initrd.gz ",
+                "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
+                "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
+                "passwd/user-fullname={{ user `ssh_fullname` }} ",
+                "passwd/user-password={{ user `ssh_password` }} ",
+                "passwd/user-password-again={{ user `ssh_password` }} ",
+                "passwd/username={{ user `ssh_username` }} ",
+                "-- <enter>"
+            ],
+            "cpus": "{{ user `vm_cpus`}}",
+            "disk_size": "{{ user `image_size` }}",
+            "floppy_files":
+            [
+                "shared/{{ user `preseed` }}"
+            ],
+            "headless": "{{ user `headless` }}",
+            "http_directory": "shared/",
+            "iso_checksum": "{{ user `iso_checksum_type` }}:{{ user `iso_checksum` }}",
+            "iso_urls": [ "{{ user `iso_url` }}" ],
+            "memory": "{{ user `vm_memory`}}",
+            "output_directory": "hack-back/{{ user `image_name` }}-image",
+            "qemuargs":
+            [
+                [ "-cpu", "host" ],
+                [ "-display", "none" ]
+            ],
+            "qemu_binary":"/usr/bin/qemu-system-x86_64",
+            "shutdown_command": "echo '{{ user `ssh_password` }}'|sudo -S shutdown -P now",
+            "ssh_password": "{{ user `ssh_password` }}",
+            "ssh_username": "{{ user `ssh_username` }}",
+            "ssh_wait_timeout": "60m",
+            "vm_name": "{{ user `image_name` }}"
+        }
+    ],
+    "provisioners":
+    [
+        {
+            "type": "file",
+            "source": "../gem5/util/m5/build/x86/out/m5",
+            "destination": "/home/gem5/"
+        },
+        {
+            "type": "file",
+            "source": "shared/serial-getty@.service",
+            "destination": "/home/gem5/"
+        },
+        {
+            "type": "file",
+            "source": "hack-back/hack_back_ckpt.rcS",
+            "destination": "/home/gem5/"
+        },
+        {
+            "type": "shell",
+            "execute_command": "echo '{{ user `ssh_password` }}' | {{.Vars}} sudo -E -S bash '{{.Path}}'",
+            "scripts":
+            [
+                "hack-back/post-installation.sh"
+            ]
+        }
+    ],
+    "variables":
+    {
+        "boot_command_prefix": "<enter><wait><f6><esc><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
+        "desktop": "false",
+        "image_size": "12000",
+        "headless": "true",
+        "iso_checksum": "34416ff83179728d54583bf3f18d42d2",
+        "iso_checksum_type": "md5",
+        "iso_name": "ubuntu-18.04.2-server-amd64.iso",
+        "iso_url": "http://old-releases.ubuntu.com/releases/18.04.2/ubuntu-18.04.2-server-amd64.iso",
+        "locale": "en_US",
+        "preseed" : "preseed.cfg",
+        "hostname": "gem5",
+        "ssh_fullname": "gem5",
+        "ssh_password": "12345",
+        "ssh_username": "gem5",
+        "vm_cpus": "4",
+        "vm_memory": "8192",
+        "image_name": "hack-back"
+  }
+
+}
diff --git a/src/hack-back/disk-image/hack-back/hack_back_ckpt.rcS b/src/hack-back/disk-image/hack-back/hack_back_ckpt.rcS
new file mode 100755
index 0000000..51ca6a3
--- /dev/null
+++ b/src/hack-back/disk-image/hack-back/hack_back_ckpt.rcS
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+#
+# This is a tricky script to understand. When run in M5, it creates
+# a checkpoint after Linux boot up, but before any benchmarks have
+# been run. By playing around with environment variables, we can
+# detect whether the checkpoint has been taken.
+#  - If the checkpoint hasn't been taken, the script allows M5 to checkpoint the system,
+# re-read this script into a new tmp file, and re-run it. On the
+# second execution of this script (checkpoint has been taken), the
+# environment variable is already set, so the script will exit the
+# simulation
+#  - When we restore the simulation from a checkpoint, we can
+# specify a new script for M5 to execute in the full-system simulation,
+# and it will be executed as if a checkpoint had just been taken.
+#
+# Author:
+#   Joel Hestness, hestness@cs.utexas.edu
+#   while at AMD Research and Advanced Development Lab
+# Date:
+#   10/5/2010
+#
+
+# Test if the RUNSCRIPT_VAR environment variable is already set
+
+if [ "${RUNSCRIPT_VAR+set}" != set ]
+then
+	# Signal our future self that it's safe to continue
+	export RUNSCRIPT_VAR=1
+else
+	# We've already executed once, so we should exit
+	/sbin/m5 exit
+fi
+
+# Checkpoint the first execution
+echo "Checkpointing simulation..."
+/sbin/m5 checkpoint
+
+# Test if we previously okayed ourselves to run this script
+if [ "$RUNSCRIPT_VAR" -eq 1 ]
+then
+
+	# Signal our future self not to recurse infinitely
+	export RUNSCRIPT_VAR=2
+
+	# Read the script for the checkpoint restored execution
+	echo "Loading new script..."
+	/sbin/m5 readfile > /tmp/runscript
+	chmod 755 /tmp/runscript
+
+	# Execute the new runscript
+	if [ -s /tmp/runscript ]
+	then
+		exec /tmp/runscript
+	else
+		echo "Script not specified. Dropping into shell..."
+		/bin/bash
+	fi
+
+fi
+
+echo "Fell through script. Exiting..."
+/sbin/m5 exit
diff --git a/src/hack-back/disk-image/hack-back/post-installation.sh b/src/hack-back/disk-image/hack-back/post-installation.sh
new file mode 100755
index 0000000..d04ef94
--- /dev/null
+++ b/src/hack-back/disk-image/hack-back/post-installation.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Copyright (c) 2020 The Regents of the University of California.
+# SPDX-License-Identifier: BSD 3-Clause
+
+echo 'Post Installation Started'
+
+mv /home/gem5/serial-getty@.service /lib/systemd/system/
+
+mv /home/gem5/m5 /sbin
+ln -s /sbin/m5 /sbin/gem5
+
+# copy and run outside (host) script after booting
+cat /home/gem5/hack_back_ckpt.rcS >> /root/.bashrc
+
+echo 'Post Installation Done'
diff --git a/src/hack-back/disk-image/shared/preseed.cfg b/src/hack-back/disk-image/shared/preseed.cfg
new file mode 100755
index 0000000..b5cd8a7
--- /dev/null
+++ b/src/hack-back/disk-image/shared/preseed.cfg
@@ -0,0 +1,96 @@
+# Copyright (c) 2020 The Regents of the University of California.
+# SPDX-License-Identifier: BSD 3-Clause
+
+# Choosing keyboard layout
+d-i debian-installer/locale string en_US
+d-i console-setup/ask_detect boolean false
+d-i keyboard-configuration/xkb-keymap select us
+
+# Choosing network interface
+d-i netcfg/choose_interface select auto
+
+# Assigning hostname and domain
+d-i netcfg/get_hostname string gem5-host
+d-i netcfg/get_domain string gem5-domain
+
+d-i netcfg/wireless_wep string
+
+# https://unix.stackexchange.com/q/216348
+# The above link says there's no way to not to set a mirror
+# Should choose a local minor
+d-i mirror/country string manual
+d-i mirror/http/hostname string archive.ubuntu.com
+d-i mirror/http/directory string /ubuntu
+d-i mirror/http/proxy string
+
+# Setting up `root` password
+d-i passwd/root-login boolean false
+
+# Creating a normal user account. This account has sudo permission.
+d-i passwd/user-fullname string gem5
+d-i passwd/username string gem5
+d-i passwd/user-password password 12345
+d-i passwd/user-password-again password 12345
+d-i user-setup/allow-password-weak boolean true
+
+# No home folder encryption
+d-i user-setup/encrypt-home boolean false
+
+# Choosing the clock timezone
+d-i clock-setup/utc boolean true
+d-i time/zone string US/Eastern
+d-i clock-setup/ntp boolean true
+
+# Choosing partition scheme
+# This setting should result in MBR
+# gem5 doesn't work with logical volumes
+d-i partman-auto/method string regular
+d-i partman-lvm/device_remove_lvm boolean true
+d-i partman-md/device_remove_md boolean true
+d-i partman-lvm/confirm boolean true
+d-i partman-lvm/confirm_nooverwrite boolean true
+
+# Ignoring an option to set the home folder in another partition
+d-i partman-auto/choose_recipe select atomic
+
+# Finishing disk partition settings
+d-i partman-md/confirm boolean true
+d-i partman-partitioning/confirm_write_new_label boolean true
+d-i partman/choose_partition select finish
+d-i partman/confirm boolean true
+d-i partman/confirm_nooverwrite boolean true
+
+# Installing standard packages and ubuntu-server packages
+# More details about ubuntu standard packages:
+# https://packages.ubuntu.com/bionic/ubuntu-standard
+# More details about ubuntu-server packages:
+# https://packages.ubuntu.com/bionic/ubuntu-server
+tasksel tasksel/first multiselect standard, ubuntu-server
+
+# openssh-server is required for communicating with Packer
+# build-essential has standard compiling tools, could be removed
+d-i pkgsel/include string openssh-server build-essential
+# No package upgrade
+d-i pkgsel/upgrade select none
+
+# Updating packages automatically is unnecessary
+d-i pkgsel/update-policy select none
+
+# Choosing not to report installed software to some servers
+popularity-contest popularity-contest/participate boolean false
+
+# Installing grub
+d-i grub-installer/only_debian boolean true
+
+# Specifying which partition to boot
+d-i grub-installer/bootdev  string /dev/sda
+
+# Install to the above partition
+d-i grub-installer/bootdev  string default
+
+# Answering the prompt saying the installation is finished
+d-i finish-install/reboot_in_progress note
+
+# Answering the prompt saying no bootloader is installed
+# This will appear if grub is not installed
+nobootloader nobootloader/confirmation_common note
diff --git a/src/hack-back/disk-image/shared/serial-getty@.service b/src/hack-back/disk-image/shared/serial-getty@.service
new file mode 100644
index 0000000..b0424f0
--- /dev/null
+++ b/src/hack-back/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