summaryrefslogtreecommitdiff
path: root/documentation
diff options
context:
space:
mode:
authorRyan Roberts <ryan.roberts@arm.com>2022-09-14 08:07:34 +0100
committerRyan Roberts <ryan.roberts@arm.com>2022-11-02 14:48:39 +0000
commit2c8598c2041973a91ec921fedc5f7f36cc402caf (patch)
tree46754ed3961b2abaf2e1dbb45ea116a334716d30 /documentation
parent4cc63ac23d695f3946bbfef8c41d80a675751a5d (diff)
docs: Significant documentation additions.
Adds an overview to describe the purpose of Shrinkwrap and its key features, along with information about the project. Adds a quickstart guide to describe some example use cases. Adds documentation for the config yaml format including how merging works, how macros work and the schema. Signed-off-by: Ryan Roberts <ryan.roberts@arm.com>
Diffstat (limited to 'documentation')
-rw-r--r--documentation/overview.rst53
-rw-r--r--documentation/user_guide/config.rst210
-rw-r--r--documentation/user_guide/quickstart.rst359
-rw-r--r--documentation/user_guide/runtimes.rst27
4 files changed, 635 insertions, 14 deletions
diff --git a/documentation/overview.rst b/documentation/overview.rst
index 65fc15c..3d5213f 100644
--- a/documentation/overview.rst
+++ b/documentation/overview.rst
@@ -11,29 +11,54 @@ Overview
Introduction
************
-Here we expect a brief introduction about the solution as well as the main
-target audience and use cases.
+Shrinkwrap is a tool to simplify the process of building and running firmware on
+Arm Fixed Virtual Platforms (FVP). Users simply invoke the tool to build the
+required config, then pass their own kernel and rootfs to the tool to boot the
+full system on FVP.
-*********************
-Solution Architecture
-*********************
+Firmware for Arm platforms is becoming increasingly complex with both more
+firmware components (TF-A, Hafnium, OP-TEE, Trusty, RMM, EDK2, U-Boot, etc) and
+more configuration options, both when building the components and when launching
+the FVP. Shrinkwrap solves this problem by abstracting all of this complexity
+into a set of configurations, which can be composed and extended. The tool reads
+the config and generates appropriate shell commands to build, package and run
+the firmware.
-A high-level diagram included with some explanation highlighting the main
-features and deliverables.
+See :ref:`user_guide/quickstart:Quick Start Guide` to get up and running.
+
+************
+Architecture
+************
+
+Shrinkwrap is implemented in Python and has a command line interface similar to
+git, with sub-commands that take options. The Python code parses the supplied
+config(s) to generate shell commands that are executed in a backend runtime. The
+runtime is specified by the user and may be ``null`` (executed natively on the
+user's system), or a container runtime such as ``docker`` or ``podman``. For the
+container runtimes, a standard image is provided with all tools preinstalled.
********
Features
********
-- **Feature A:**
-- **Feature B:**
+- A simple and intuitive command line interface enables:
+
+ - Building all required firmware components for a given configuration
+ - Packaging built firmware components for easy distribution
+ - Configuring and booting the FVP with the packaged firmware components
-*********
-Use Cases
-*********
+- Introspect and use any of supplied the out-of-box configurations
+- Create your own configurations by composing with and extending others
+- Choose from multiple runtime engines (Docker, Podman, native)
+- Ensure Reproducible builds with supplied runtime container images
+- Transparently view the generated bash commands for a given config build or run
+- Parallelize builds to make best use of available resources
+- Acquire source from Git remote or point to existing Git local repo
+- Choose how to redirect each FVP UART terminal I/O:
-- **Use Case A:**
-- **Use Case B:**
+ - mux to stdout
+ - launch telnet
+ - automatically launch xterm (only in native runtime)
********************
Repository Structure
diff --git a/documentation/user_guide/config.rst b/documentation/user_guide/config.rst
index 5cdabf1..b262fbc 100644
--- a/documentation/user_guide/config.rst
+++ b/documentation/user_guide/config.rst
@@ -6,3 +6,213 @@
#######
Configs
#######
+
+A config is a yaml file that defines everything about a given configuration.
+This includes:
+
+- meta data (e.g. its name, description, etc)
+- the components that should be built
+- how those components are built
+- dependencies between components
+- what artifacts are produced
+- how to configure the fvp
+- how to load and run the artifacts on the fvp
+
+A config is declarative; the user declares how things relate and how things
+should be done, and the tool extracts the information to decide exactly what
+should be done and in what order in order to complete a task. All the data is
+contained within the config and drives the tool. This way, shrinkwrap is highly
+extensible. The user specifies the config(s) that should be used when invoking
+shrinkwrap.
+
+
+***************
+Merging Configs
+***************
+
+A config is laid out as a hierachical data structure, using nested dictionaries.
+This suits it very well to being split into partial configs that are merged
+together into a single, final config. This allows maximal reuse of the config
+fragments and improves maintainability. Each config can optionally define a set
+of foundational ``layers`` which it then builds upon. Furthermore, the user can
+optionally specify a custom ``overlay`` config on the command line. Layers are
+merged in order according to the following rules:
+
+For each leaf key in the union of the hierachical dictionaries:
+
+- If the upper value is null or not present, then the lower value is taken
+
+- If both the upper and lower values are lists, then the final value is the
+ lower list with the upper list appended to its end.
+
+- In all other cases the upper value is taken
+
+You can use the ``process`` command to merge configs and see the resulting
+output to get a better feel for how it works. See
+:ref:`user_guide/commands:Commands`.
+
+---------------
+Merging Example
+---------------
+
+.. code-block:: yaml
+ :caption: lower config
+
+ people:
+ Iris:
+ age: 2
+ likes:
+ - Peppa Pig
+ - Bananas
+
+.. code-block:: yaml
+ :caption: upper config
+
+ people:
+ Iris:
+ age: 3
+ likes:
+ - Peas
+ James:
+ age: 6
+ likes:
+ - FIFA
+
+.. code-block:: yaml
+ :caption: merged result
+
+ people:
+ Iris:
+ age: 3
+ likes:
+ - Peppa Pig
+ - Bananas
+ - Peas
+ James:
+ age: 6
+ likes:
+ - FIFA
+
+
+******
+Macros
+******
+
+Macros are placeholders that can be specified in various parts of a config yaml
+file, which are substituted ("resolved") with information that is only known and
+build-time or run-time. There are specific rules about which macros can be used
+in which parts of the config, and about the order in which they get substituted.
+
+Macros take the following form:
+
+ ``${<type>:[<name>]}``
+
+where:
+
+ - ``type`` is a required namespace for the macro family
+ - ``name`` is an optional name for the macro within its namespace. For some
+ macro types, there are a fixed set of names. For others, the names are
+ defined by the config itself.
+
+You can use the ``process`` command to resolve macros and see the resulting
+output to get a better feel for how they work. See
+:ref:`user_guide/commands:Commands`.
+
+--------------
+Defined Macros
+--------------
+
+======================= ================================================================== ====
+macro scope description
+======================= ================================================================== ====
+``${param:sourcedir}`` build.<component>.{params, prebuild, build, postbuild, artifacts} Directory in which the component's source code is located.
+``${param:builddir}`` build.<component>.{params, prebuild, build, postbuild, artifacts} Directory in which the component should be built, if the component's build system supports separation of source and build trees.
+``${param:packagedir}`` build.<component>.{params, prebuild, build, postbuild, artifacts} Directory in which all artifacts from the config build are packaged to and accessed from during run.
+``${param:packagedir}`` run.{params, rtvars, prerun} Directory in which all artifacts from the config build are packaged to and accessed from during run.
+``${param:jobs}`` build.<component>.{params, prebuild, build, postbuild} Maximum number of low level parallel jobs specified on the command line. To be passed to (e.g.) make as ``-j${param:jobs}``.
+``${param:join_equal}`` build.<component>.{prebuild, build, postbuild} String containing all of the component's parameters (from its params dictionary), concatenated as ``key=value`` pairs.
+``${param:join_space}`` build.<component>.{prebuild, build, postbuild} String containing all of the component's parameters (from its params dictionary), concatenated as ``key value`` pairs.
+``${artifact:<name>}`` build.<component>.{params, prebuild, build, postbuild} Build path of an artifact declared by another component. Usage of these macros determine the component build dependency graph.
+``${artifact:<name>}`` run.rtvars Package path of an artifact.
+``${rtvar:<name>}`` run.params Run-time variables. The variable names, along with default values are declared in run.rtvars, and the user may override the value on the command line.
+======================= ================================================================== ====
+
+******
+Schema
+******
+
+--------------
+Top-Level keys
+--------------
+
+The following is the set of top-level public keys that should be defined by a
+config. There are some additional private keys that the tool will add (and make
+visible as part of the ``process`` command), but these are subject to change and
+not documented.
+
+=========== ========== ===========
+key type description
+=========== ========== ===========
+description string A human-readable description of what the config contains and does. Displayed by the ``inspect`` command.
+concrete boolean true if the config is intended to be directly built and run, or false if it is intended as a fragment to be included in other configs.
+build dictionary Contains all the components to be built. The key is the component name and the value is a dictionary.
+run dictionary Contains all the information about how to run the built artifacts on the FVP.
+=========== ========== ===========
+
+-------------
+build section
+-------------
+
+The build section, contains a dictionary of components that must be built. The
+keys are the component names and the values are themselves dictionaries, each
+containing the component meta data.
+
+~~~~~~~~~~~~~~~~~
+component section
+~~~~~~~~~~~~~~~~~
+
+=========== =========== ===========
+key type description
+=========== =========== ===========
+repo dictionary Specifies information about the git repo(s) that must be cloned and checked out. Shrinkwrap will only sync the git repo if it does not already exist. If it exists, it leaves it in whatever state the user left it in and attempts to build it. Not required if ``sourcedir`` is provided.
+sourcedir string If specified, points to the path on disk where the source repo can be found. Useful for developer use cases where a local repo already exists.
+builddir string If specified, the location where the component will be built. If not specified, shrinkwrap allocates its own location based on SHRINKWRAP_BUILD.
+params dictionary Optional set of key:value pairs. When building most components, they require a set of parameters to be passed. By setting them out as a dictionary, it is easy to override and add to them in higher layers. See ``${param:join_*}`` macros.
+prebuild list List of shell commands to be executed during component build before the ``build`` list.
+build list List of shell commands to be executed during component build.
+postbuild list List of shell commands to be executed during component build after the ``build`` list.
+artifacts dictionary Set of artifacts that the component exports. Key is artifact name and value is path to built artifact. Other components can reference them with the ``${artifact:<name>}`` macros. Used to determine build dependencies.
+=========== =========== ===========
+
+-----------
+run section
+-----------
+
+=========== =========== ===========
+key type description
+=========== =========== ===========
+name string Name of the FVP binary, which must be in $PATH.
+rtvars dictionary Run-Time variables. Keys are the variable names and values are the variables' default values. Run-Time variables can be overridden by the user at the command line.
+params dictionary Dictionary of parameters to be passed to the FVP. Similar to the component's params, laying these out in a dictionary makes it easy for higher layers to override and add parameters.
+prerun list List of shell commands to be executed before the FVP is started.
+terminals dictionary Describes the set of UART terminals available for the FVP. key is the terminal parameter name known to the FVP (e.g. ``bp.terminal_0``) See below for format of the value.
+=========== =========== ===========
+
+~~~~~~~~~~~~~~~~
+terminal section
+~~~~~~~~~~~~~~~~
+
+=========== =========== ===========
+key type description
+=========== =========== ===========
+friendly string Label to display against the terminal when muxing to stdout.
+port_regex string Regex to use to find the TCP port of the terminal when parsing the FVP stdout. Must have single capture group.
+type enum-string Terminal type. See below for options.
+=========== =========== ===========
+
+Terminal types:
+
+- **stdout**: Mux output to stdout. Do not supply any input.
+- **stdinout**: Mux output to stdout. Forward stdin to its input. Max of 1 of these types allowed.
+- **telnet**: Shrinkwrap will print out a telnet command to run in a separate terminal to get a unique interactive terminal.
+- **xterm**: Shrinkwrap will automatically launch xterm to provide a unique interactive terminal. Only works when runtime=null.
diff --git a/documentation/user_guide/quickstart.rst b/documentation/user_guide/quickstart.rst
index 87d23da..64df583 100644
--- a/documentation/user_guide/quickstart.rst
+++ b/documentation/user_guide/quickstart.rst
@@ -1,3 +1,362 @@
#################
Quick Start Guide
#################
+
+******************
+Install Shrinkwrap
+******************
+
+Packages don't yet exist, so currently the only way to install Shrinkwrap is to
+install its dependencies and clone the git repository:
+
+.. code-block:: shell
+
+ sudo apt-get install docker.io netcat-openbsd python3 python3-pip
+ sudo pip3 install termcolor tuxmake
+ git clone git@git.gitlab.oss.arm.com:engineering/linux-arm/shrinkwrap.git
+ export PATH=$PWD/shrinkwrap/shrinkwrap:$PATH
+
+Now invoke the tool to view help:
+
+.. code-block:: shell
+
+ shrinkwrap --help
+ shrinkwrap <command> --help
+
+*********
+Use Cases
+*********
+
+.. note::
+
+ The below commands all use the ``docker`` runtime, which means the docker
+ container image will be automatically downloaded and all commands will be
+ executed in it. This ensures that the required toolchains and other tools are
+ available. The default docker image is stored in Arm's internal Artifactory
+ repository. Before using Shrinkwrap with the ``docker`` or ``podman``
+ runtimes, you must log your local docker install into this repository. See
+ :ref:`user_guide/runtimes:Run-Times` for instructions.
+
+ Users can choose to run with the ``null`` runtime (which is the default if the
+ ``--runtime`` option is omitted). This will cause all commands to be executed
+ on the native system. Users are responsible for setting up the environment in
+ this case.
+
+********************************************
+Use Case: Build & Run ns-preload.yaml Config
+********************************************
+
+First, inspect the available configs:
+
+.. code-block:: shell
+
+ shrinkwrap --runtime=docker inspect
+
+This will show all of the (concrete) configs in the config store. The below
+output shows a sample. Notice that each config lists its runtime variables along
+with their default values. ``None`` means there is no default and the user must
+provide a value when running the config.
+
+.. raw:: html
+
+ <p>
+ <details>
+ <summary><a>Expand</a></summary>
+
+.. code-block:: none
+
+ name: cca.yaml
+
+ description: Brings together TF-A, RMM, Hafnium and a set of simple
+ secure partitions to demonstrate Arm CCA running on FVP.
+
+ concrete: True
+
+ run-time variables: LOCAL_NET_PORT: 8022
+ BL1: ${artifact:BL1}
+ FIP: ${artifact:FIP}
+ ROOTFS: None
+ KERNEL: None
+
+ --------------------------------------------------------------------------------
+
+ name: ns-edk2-acpi.yaml
+
+ description: Best choice for: I want to run Linux on FVP, booting with
+ ACPI, and have easy control over its command line.
+
+ Brings together TF-A and EDK2 to provide a simple non-
+ secure world environment running on FVP. Allows easy
+ specification of the kernel image and command line, and
+ rootfs at runtime (see rtvars). ACPI is provided by UEFI.
+
+ By default (if not overriding the rtvars) a sensible
+ command line is used that will set up the console for
+ logging and attempt to mount the rootfs image from the
+ FVP's virtio block device. However the default rootfs image
+ is empty, so the kernel will panic when attempting to
+ mount; the user must supply a rootfs if it is required that
+ the kernel completes its boot. No default kernel image is
+ supplied and the config will refuse to run unless it is
+ explicitly specified.
+
+ Note that the config will boot to the EDK2 main screen and
+ the user must navigate to Boot Manager -> UEFI Shell. Then
+ hit enter to execute startup.nsh, which will boot the
+ kernel as specified. The main UART console is redirected to
+ telnet and the user will be prompted to run the required
+ telnet command in a separate window. This is required due
+ to EDK2's ncurses-like output.
+
+ concrete: True
+
+ run-time variables: LOCAL_NET_PORT: 8022
+ BL1: ${artifact:BL1}
+ FIP: ${artifact:FIP}
+ CMDLINE: console=ttyAMA0
+ earlycon=pl011,0x1c090000
+ root=/dev/vda ip=dhcp
+ KERNEL: None
+ ROOTFS:
+
+ --------------------------------------------------------------------------------
+
+ name: ns-edk2-dt.yaml
+
+ description: Best choice for: I want to run Linux on FVP, booting with
+ device tree, and have easy control over its command line.
+
+ Builds on ns-edk2-acpi.yaml, but adds a device tree that is
+ passed to the kernel to use instead of ACPI. See the
+ description in that file for details.
+
+ An extra rtvar is added (DTB) which allows specification of
+ a custom device tree. By default (if not overriding the
+ rtvar), the upstream kernel device tree is used.
+
+ concrete: True
+
+ run-time variables: LOCAL_NET_PORT: 8022
+ BL1: ${artifact:BL1}
+ FIP: ${artifact:FIP}
+ CMDLINE: console=ttyAMA0
+ earlycon=pl011,0x1c090000
+ root=/dev/vda ip=dhcp
+ KERNEL: None
+ ROOTFS:
+ DTB: ${artifact:DTB}
+
+ --------------------------------------------------------------------------------
+
+ name: ns-preload.yaml
+
+ description: Best choice for: I just want to run Linux on FVP.
+
+ A simple, non-secure-only configuration where all
+ components are preloaded into memory (TF-A's BL31, DTB and
+ kernel). The system resets directly to BL31. Allows easy
+ specification of a custom command line at build-time (via
+ build.dt.params dictionary) and specification of the device
+ tree, kernel image and rootfs at run-time (see rtvars).
+
+ By default (if not overriding the rtvars), the upstream
+ kernel device tree is used along with a sensible command
+ line that will set up the console for logging and attempt
+ to mount the rootfs image from the FVP's virtio block
+ device. However the default rootfs image is empty, so the
+ kernel will panic when attempting to mount; the user must
+ supply a rootfs if it is required that the kernel completes
+ its boot. No default kernel image is supplied and the
+ config will refuse to run unless it is explicitly
+ specified. Note: If specifying a custom dtb at runtime,
+ this will also override any command line specified at build
+ time, since the command line is added to the chosen node of
+ the default dtb.
+
+ concrete: True
+
+ run-time variables: LOCAL_NET_PORT: 8022
+ BL1: ${artifact:BL1}
+ FIP: ${artifact:FIP}
+ BL31: ${artifact:BL31}
+ DTB: ${artifact:DTB}
+ KERNEL: None
+ ROOTFS:
+
+.. raw:: html
+
+ </details>
+ </p>
+
+Now build the ``ns-preload.yaml`` config. This is the simplest config that
+allows booking a kernel on FVP. (optionally add ``--verbose`` to see all the
+output from the component build systems).
+
+.. code-block:: shell
+
+ shrinkwrap --runtime=docker build ns-preload.yaml
+
+This will sync all the required repos, build the components and package the
+artifacts.
+
+Alternatively, pass ``--dry-run`` to view the shell script that would have been
+run:
+
+.. code-block:: shell
+
+ shrinkwrap --runtime=docker build --dry-run ns-preload.yaml
+
+.. raw:: html
+
+ <p>
+ <details>
+ <summary><a>Expand</a></summary>
+
+.. code-block:: none
+
+ #!/bin/bash
+ # SHRINKWRAP AUTOGENERATED SCRIPT.
+
+ # Exit on error, error on unbound vars and echo commands.
+ set -eux
+
+ # Remove old package.
+ rm -rf <root>/package/ns-preload.yaml > /dev/null 2>&1 || true
+ rm -rf <root>/package/ns-preload > /dev/null 2>&1 || true
+
+ # Create directory structure.
+ mkdir -p <root>/build/source/ns-preload
+ mkdir -p <root>/package/ns-preload
+
+ # Sync git repo for config=ns-preload component=dt.
+ pushd <root>/build/source/ns-preload
+ if [ ! -d "dt/.git" ] || [ -f "./.dt_sync" ]; then
+ rm -rf dt > /dev/null 2>&1 || true
+ mkdir -p .
+ touch ./.dt_sync
+ git clone git://git.kernel.org/pub/scm/linux/kernel/git/devicetree/devicetree-rebasing.git dt
+ pushd dt
+ git checkout --force master
+ git submodule update --init --checkout --recursive --force
+ popd
+ rm ./.dt_sync
+ fi
+ popd
+
+ # Sync git repo for config=ns-preload component=tfa.
+ pushd <root>/build/source/ns-preload
+ if [ ! -d "tfa/.git" ] || [ -f "./.tfa_sync" ]; then
+ rm -rf tfa > /dev/null 2>&1 || true
+ mkdir -p .
+ touch ./.tfa_sync
+ git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git tfa
+ pushd tfa
+ git checkout --force master
+ git submodule update --init --checkout --recursive --force
+ popd
+ rm ./.tfa_sync
+ fi
+ popd
+
+ # Build for config=ns-preload component=dt.
+ pushd <root>/build/source/ns-preload/dt
+ DTS_IN=<root>/build/source/ns-preload/dt/src/arm64/arm/fvp-base-revc.dts
+ DTS_OUT=<root>/build/source/ns-preload/dt/src/arm64/arm/fvp-base-revc_args.dts
+ if [ -z "console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda ip=dhcp" ]; then
+ cp $DTS_IN $DTS_OUT
+ else
+ ESC_PARAMS=$(printf '%s\n' "console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda ip=dhcp" | sed -e 's/[\/&]/\\&/g')
+ sed "s/chosen {.*};/chosen { bootargs = \"$ESC_PARAMS\"; };/g" $DTS_IN > $DTS_OUT
+ fi
+ make CPP=${CROSS_COMPILE}cpp -j28 src/arm64/arm/fvp-base-revc_args.dtb
+ popd
+
+ # Build for config=ns-preload component=tfa.
+ pushd <root>/build/source/ns-preload/tfa
+ make BUILD_BASE=<root>/build/build/ns-preload/tfa PLAT=fvp DEBUG=0 LOG_LEVEL=40 ENABLE_SVE_FOR_NS=1 ENABLE_SVE_FOR_SWD=1 ARM_DISABLE_TRUSTED_WDOG=1 FVP_HW_CONFIG_DTS=fdts/fvp-base-gicv3-psci-1t.dts ARM_ARCH_MINOR=5 BRANCH_PROTECTION=1 CTX_INCLUDE_PAUTH_REGS=1 CTX_INCLUDE_MTE_REGS=1 RESET_TO_BL31=1 ARM_LINUX_KERNEL_AS_BL33=1 PRELOADED_BL33_BASE=2214592512 ARM_PRELOADED_DTB_BASE=2181038080 all fip
+ popd
+
+ # Copy artifacts for config=ns-preload.
+ cp <root>/build/source/ns-preload/dt/src/arm64/arm/fvp-base-revc_args.dtb <root>/package/ns-preload/fvp-base-revc_args.dtb
+ cp <root>/build/build/ns-preload/tfa/fvp/release/bl1.bin <root>/package/ns-preload/bl1.bin
+ cp <root>/build/build/ns-preload/tfa/fvp/release/bl2.bin <root>/package/ns-preload/bl2.bin
+ cp <root>/build/build/ns-preload/tfa/fvp/release/bl31.bin <root>/package/ns-preload/bl31.bin
+ cp <root>/build/build/ns-preload/tfa/fvp/release/fip.bin <root>/package/ns-preload/fip.bin
+
+.. raw:: html
+
+ </details>
+ </p>
+
+Now start the FVP. We will pass our own kernel and rootfs disk image (you could
+add ``--dry-run`` here too to see the FVP command that would have been run):
+
+.. code-block:: shell
+
+ shrinkwrap --runtime=docker run --rtvar=KERNEL=path/to/Image --rtvar=ROOTFS=path/to/rootfs.img ns-preload.yaml
+
+This starts the FVP and multiplexes all the UART terminals to stdout and
+forwards stdin to the ``tfa+linux`` uart terminal:
+
+.. raw:: html
+
+ <p>
+ <details>
+ <summary><a>Expand</a></summary>
+
+.. code-block:: none
+
+ [ fvp ] terminal_0: Listening for serial connection on port 5000
+ [ fvp ] terminal_1: Listening for serial connection on port 5001
+ [ fvp ] terminal_2: Listening for serial connection on port 5002
+ [ fvp ] terminal_3: Listening for serial connection on port 5003
+ [ fvp ]
+ [ fvp ] Info: FVP_Base_RevC_2xAEMvA: FVP_Base_RevC_2xAEMvA.bp.flashloader0: FlashLoader: Loaded 100 kB from file '<root>/package/ns-preload/fip.bin'
+ [ fvp ]
+ [ fvp ] Info: FVP_Base_RevC_2xAEMvA: FVP_Base_RevC_2xAEMvA.bp.secureflashloader: FlashLoader: Loaded 30 kB from file '<root>/package/ns-preload/bl1.bin'
+ [ fvp ]
+ [ fvp ] libdbus-1.so.3: cannot open shared object file: No such file or directory
+ [ fvp ] libdbus-1.so.3: cannot open shared object file: No such file or directory
+ [ tfa+linux ] NOTICE: BL31: v2.7(release):v2.7.0-391-g9dedc1ab2
+ [ tfa+linux ] NOTICE: BL31: Built : 09:41:20, Sep 15 2022
+ [ tfa+linux ] INFO: GICv3 with legacy support detected.
+ [ tfa+linux ] INFO: ARM GICv3 driver initialized in EL3
+ [ tfa+linux ] INFO: Maximum SPI INTID supported: 255
+ [ tfa+linux ] INFO: Configuring TrustZone Controller
+ [ tfa+linux ] INFO: Total 8 regions set.
+ [ tfa+linux ] INFO: BL31: Initializing runtime services
+ [ tfa+linux ] INFO: BL31: Preparing for EL3 exit to normal world
+ [ tfa+linux ] INFO: Entry point address = 0x84000000
+ [ tfa+linux ] INFO: SPSR = 0x3c9
+ [ tfa+linux ] [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd0f0]
+ [ tfa+linux ] [ 0.000000] Linux version 5.15.0-rc2-gca9bfbea162d (ryarob01@e125769) (aarch64-none-linux-gnu-gcc (GNU Toolchain for the A-profile Architecture 9.2-2019.12 (arm-9.10)) 9.2.1 20191025, GNU ld (GNU Toolchain for the A-profile Architecture 9.2-2019.12 (arm-9.10)) 2.33.1.20191209) #1 SMP PREEMPT Thu Aug 4 11:31:55 BST 2022
+ [ tfa+linux ] [ 0.000000] Machine model: FVP Base RevC
+ [ tfa+linux ] [ 0.000000] earlycon: pl11 at MMIO 0x000000001c090000 (options '')
+ [ tfa+linux ] [ 0.000000] printk: bootconsole [pl11] enabled
+ [ tfa+linux ] [ 0.000000] efi: UEFI not found.
+ [ tfa+linux ] [ 0.000000] Reserved memory: created DMA memory pool at 0x0000000018000000, size 8 MiB
+ [ tfa+linux ] [ 0.000000] OF: reserved mem: initialized node vram@18000000, compatible id shared-dma-pool
+ [ tfa+linux ] [ 0.000000] NUMA: No NUMA configuration found
+ [ tfa+linux ] [ 0.000000] NUMA: Faking a node at [mem 0x0000000080000000-0x00000008ffffffff]
+ [ tfa+linux ] [ 0.000000] NUMA: NODE_DATA [mem 0x8ff7efc00-0x8ff7f1fff]
+ [ tfa+linux ] [ 0.000000] Zone ranges:
+ [ tfa+linux ] [ 0.000000] DMA [mem 0x0000000080000000-0x00000000ffffffff]
+ [ tfa+linux ] [ 0.000000] DMA32 empty
+ [ tfa+linux ] [ 0.000000] Normal [mem 0x0000000100000000-0x00000008ffffffff]
+ [ tfa+linux ] [ 0.000000] Movable zone start for each node
+ [ tfa+linux ] [ 0.000000] Early memory node ranges
+ [ tfa+linux ] [ 0.000000] node 0: [mem 0x0000000080000000-0x00000000ffffffff]
+ [ tfa+linux ] [ 0.000000] node 0: [mem 0x0000000880000000-0x00000008ffffffff]
+ [ tfa+linux ] [ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x00000008ffffffff]
+ [ tfa+linux ] [ 0.000000] cma: Reserved 32 MiB at 0x00000000fe000000
+ [ tfa+linux ] [ 0.000000] psci: probing for conduit method from DT.
+ [ tfa+linux ] [ 0.000000] psci: PSCIv1.1 detected in firmware.
+ [ tfa+linux ] [ 0.000000] psci: Using standard PSCI v0.2 function IDs
+ [ tfa+linux ] [ 0.000000] psci: MIGRATE_INFO_TYPE not supported.
+ [ tfa+linux ] [ 0.000000] psci: SMC Calling Convention v1.2
+ ...
+
+.. raw:: html
+
+ </details>
+ </p>
diff --git a/documentation/user_guide/runtimes.rst b/documentation/user_guide/runtimes.rst
index 12ab431..fb994f2 100644
--- a/documentation/user_guide/runtimes.rst
+++ b/documentation/user_guide/runtimes.rst
@@ -6,3 +6,30 @@
#########
Run-Times
#########
+
+***********************************
+Log into Arm Artifactory Repository
+***********************************
+
+The official shrinkwrap docker images are stored in Arm's Artifactory
+repository. shrinkwrap will look here for the default image, but will fail
+unless you have previously logged your local docker instance into the
+repository. This is a one-time operation.
+
+First, create an **identity token** using the Artifactory UI:
+
+- Goto https://artifactory.geo.arm.com
+- Log in by pressing the "Azure" button
+- In the top-right drop down menu, select "Edit Profile"
+- Click "Generate Identity Token"
+- Enter a description (e.g. "shrinkwrap")
+- Click "Next"
+- Copy the "Reference Token"
+
+Now perform the login on your local system:
+
+.. code-block:: shell
+
+ docker login -u <arm email address> -p <reference token> oss-kernel--docker.artifactory.geo.arm.com
+
+You are now logged in and able to pull shrinkwrap images.