New build

This commit is contained in:
Qubot 2023-10-20 19:47:08 +08:00
commit d867bcfd20
4386 changed files with 755066 additions and 0 deletions

91
.checkpatch.conf Normal file
View File

@ -0,0 +1,91 @@
#
# Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#
# Configure how the Linux checkpatch script should be invoked in the context of
# the Trusted Firmware source tree.
#
# This is not Linux so don't expect a Linux tree!
--no-tree
# The Linux kernel expects the SPDX license tag in the first line of each file.
# We don't follow this in the Trusted Firmware.
--ignore SPDX_LICENSE_TAG
# This clarifes the lines indications in the report.
#
# E.g.:
# Without this option, we have the following output:
# #333: FILE: drivers/arm/gic/arm_gic.c:160:
# So we have 2 lines indications (333 and 160), which is confusing.
# We only care about the position in the source file.
#
# With this option, it becomes:
# drivers/arm/gic/arm_gic.c:160:
--showfile
# Don't show some messages like the list of ignored types or the suggestion to
# use "--fix" or report changes to the maintainers.
--quiet
#
# Ignore the following message types, as they don't necessarily make sense in
# the context of the Trusted Firmware.
#
# COMPLEX_MACRO generates false positives.
--ignore COMPLEX_MACRO
# Commit messages might contain a Gerrit Change-Id.
--ignore GERRIT_CHANGE_ID
# Do not check the format of commit messages, as Gerrit's merge commits do not
# preserve it.
--ignore GIT_COMMIT_ID
# FILE_PATH_CHANGES reports this kind of message:
# "added, moved or deleted file(s), does MAINTAINERS need updating?"
# We do not use this MAINTAINERS file process in TF.
--ignore FILE_PATH_CHANGES
# AVOID_EXTERNS reports this kind of messages:
# "externs should be avoided in .c files"
# We don't follow this convention in TF.
--ignore AVOID_EXTERNS
# NEW_TYPEDEFS reports this kind of messages:
# "do not add new typedefs"
# We allow adding new typedefs in TF.
--ignore NEW_TYPEDEFS
# Avoid "Does not appear to be a unified-diff format patch" message
--ignore NOT_UNIFIED_DIFF
# VOLATILE reports this kind of messages:
# "Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt"
# We allow the usage of the volatile keyword in TF.
--ignore VOLATILE
# BRACES reports this kind of messages:
# braces {} are not necessary for any arm of this statement
--ignore BRACES
# PREFER_KERNEL_TYPES reports this kind of messages (when using --strict):
# "Prefer kernel type 'u32' over 'uint32_t'"
--ignore PREFER_KERNEL_TYPES
# USLEEP_RANGE reports this kind of messages (when using --strict):
# "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt"
--ignore USLEEP_RANGE
# COMPARISON_TO_NULL reports this kind of messages (when using --strict):
# Comparison to NULL could be written ""
--ignore COMPARISON_TO_NULL
# UNNECESSARY_PARENTHESES reports this kind of messages (when using --strict):
# Unnecessary parentheses around ""
--ignore UNNECESSARY_PARENTHESES

73
.commitlintrc.js Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* eslint-env es6 */
"use strict";
const fs = require("fs");
const yaml = require("js-yaml");
const { "trailer-exists": trailerExists } = require("@commitlint/rules").default;
/*
* The types and scopes accepted by both Commitlint and Commitizen are defined by the changelog
* configuration file - `changelog.yaml` - as they decide which section of the changelog commits
* with a given type and scope are placed in.
*/
let changelog;
try {
const contents = fs.readFileSync("changelog.yaml", "utf8");
changelog = yaml.load(contents);
} catch (err) {
console.log(err);
throw err;
}
function getTypes(sections) {
return sections.map(section => section.type)
}
function getScopes(subsections) {
return subsections.flatMap(subsection => {
const scope = subsection.scope ? [ subsection.scope ] : [];
const subscopes = getScopes(subsection.subsections || []);
return scope.concat(subscopes);
})
};
const types = getTypes(changelog.sections).sort(); /* Sort alphabetically */
const scopes = getScopes(changelog.subsections).sort(); /* Sort alphabetically */
module.exports = {
extends: ["@commitlint/config-conventional"],
plugins: [
{
rules: {
"signed-off-by-exists": trailerExists,
"change-id-exists": trailerExists,
},
},
],
rules: {
"header-max-length": [1, "always", 50], /* Warning */
"body-max-line-length": [1, "always", 72], /* Warning */
"change-id-exists": [1, "always", "Change-Id:"], /* Warning */
"signed-off-by-exists": [1, "always", "Signed-off-by:"], /* Warning */
"type-case": [2, "always", "lower-case" ], /* Error */
"type-enum": [2, "always", types], /* Error */
"scope-case": [2, "always", "lower-case"], /* Error */
"scope-enum": [1, "always", scopes] /* Warning */
},
};

3
.cz.json Normal file
View File

@ -0,0 +1,3 @@
{
"path": "@commitlint/cz-commitlint"
}

72
.editorconfig Normal file
View File

@ -0,0 +1,72 @@
#
# Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Trusted Firmware-A Coding style spec for editors.
# References:
# [EC] http://editorconfig.org/
# [CONT] contributing.rst
# [LCS] Linux Coding Style
# (https://www.kernel.org/doc/html/v4.10/process/coding-style.html)
# [PEP8] Style Guide for Python Code
# (https://www.python.org/dev/peps/pep-0008)
root = true
# set default to match [LCS] .c/.h settings.
# This will also apply to .S, .mk, .sh, Makefile, .dts, etc.
[*]
# Not specified, but fits current ARM-TF sources.
charset = utf-8
# Not specified, but implicit for "LINUX coding style".
end_of_line = lf
# [LCS] Chapter 1: Indentation
# "and thus indentations are also 8 characters"
indent_size = 8
# [LCS] Chapter 1: Indentation
# "Outside of comments,...spaces are never used for indentation"
indent_style = tab
# Not specified by [LCS], but sensible
insert_final_newline = true
# [LCS] Chapter 2: Breaking long lines and strings
# "The limit on the length of lines is 100 columns"
# This is a "soft" requirement for Arm-TF, and should not be the sole
# reason for changes.
max_line_length = 100
# [LCS] Chapter 1: Indentation
# "Tabs are 8 characters"
tab_width = 8
# [LCS] Chapter 1: Indentation
# "Get a decent editor and don't leave whitespace at the end of lines."
# [LCS] Chapter 3.1: Spaces
# "Do not leave trailing whitespace at the ends of lines."
trim_trailing_whitespace = true
# Adjustment for ReStructuredText (RST) documentation
[*.{rst}]
indent_size = 4
indent_style = space
# Adjustment for python which prefers a different style
[*.py]
# [PEP8] Indentation
# "Use 4 spaces per indentation level."
indent_size = 4
indent_style = space
# [PEP8] Maximum Line Length
# "Limit all lines to a maximum of 79 characters."
max_line_length = 79

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

7
.husky/commit-msg Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
# shellcheck source=./_/husky.sh
. "$(dirname "$0")/_/husky.sh"
"$(dirname "$0")/commit-msg.gerrit" "$@"
"$(dirname "$0")/commit-msg.commitlint" "$@"

3
.husky/commit-msg.commitlint Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
npx --no-install commitlint --edit "$1"

194
.husky/commit-msg.gerrit Executable file
View File

@ -0,0 +1,194 @@
#!/bin/sh
# From Gerrit Code Review 2.14.20
#
# Part of Gerrit Code Review (https://www.gerritcodereview.com/)
#
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
unset GREP_OPTIONS
CHANGE_ID_AFTER="Bug|Depends-On|Issue|Test|Feature|Fixes|Fixed"
MSG="$1"
# Check for, and add if missing, a unique Change-Id
#
add_ChangeId() {
clean_message=`sed -e '
/^diff --git .*/{
s///
q
}
/^Signed-off-by:/d
/^#/d
' "$MSG" | git stripspace`
if test -z "$clean_message"
then
return
fi
# Do not add Change-Id to temp commits
if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!'
then
return
fi
if test "false" = "`git config --bool --get gerrit.createChangeId`"
then
return
fi
# Does Change-Id: already exist? if so, exit (no change).
if grep -i '^Change-Id:' "$MSG" >/dev/null
then
return
fi
id=`_gen_ChangeId`
T="$MSG.tmp.$$"
AWK=awk
if [ -x /usr/xpg4/bin/awk ]; then
# Solaris AWK is just too broken
AWK=/usr/xpg4/bin/awk
fi
# Get core.commentChar from git config or use default symbol
commentChar=`git config --get core.commentChar`
commentChar=${commentChar:-#}
# How this works:
# - parse the commit message as (textLine+ blankLine*)*
# - assume textLine+ to be a footer until proven otherwise
# - exception: the first block is not footer (as it is the title)
# - read textLine+ into a variable
# - then count blankLines
# - once the next textLine appears, print textLine+ blankLine* as these
# aren't footer
# - in END, the last textLine+ block is available for footer parsing
$AWK '
BEGIN {
if (match(ENVIRON["OS"], "Windows")) {
RS="\r?\n" # Required on recent Cygwin
}
# while we start with the assumption that textLine+
# is a footer, the first block is not.
isFooter = 0
footerComment = 0
blankLines = 0
}
# Skip lines starting with commentChar without any spaces before it.
/^'"$commentChar"'/ { next }
# Skip the line starting with the diff command and everything after it,
# up to the end of the file, assuming it is only patch data.
# If more than one line before the diff was empty, strip all but one.
/^diff --git / {
blankLines = 0
while (getline) { }
next
}
# Count blank lines outside footer comments
/^$/ && (footerComment == 0) {
blankLines++
next
}
# Catch footer comment
/^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
footerComment = 1
}
/]$/ && (footerComment == 1) {
footerComment = 2
}
# We have a non-blank line after blank lines. Handle this.
(blankLines > 0) {
print lines
for (i = 0; i < blankLines; i++) {
print ""
}
lines = ""
blankLines = 0
isFooter = 1
footerComment = 0
}
# Detect that the current block is not the footer
(footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
isFooter = 0
}
{
# We need this information about the current last comment line
if (footerComment == 2) {
footerComment = 0
}
if (lines != "") {
lines = lines "\n";
}
lines = lines $0
}
# Footer handling:
# If the last block is considered a footer, splice in the Change-Id at the
# right place.
# Look for the right place to inject Change-Id by considering
# CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
# then Change-Id, then everything else (eg. Signed-off-by:).
#
# Otherwise just print the last block, a new line and the Change-Id as a
# block of its own.
END {
unprinted = 1
if (isFooter == 0) {
print lines "\n"
lines = ""
}
changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
numlines = split(lines, footer, "\n")
for (line = 1; line <= numlines; line++) {
if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
unprinted = 0
print "Change-Id: I'"$id"'"
}
print footer[line]
}
if (unprinted) {
print "Change-Id: I'"$id"'"
}
}' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T"
}
_gen_ChangeIdInput() {
echo "tree `git write-tree`"
if parent=`git rev-parse "HEAD^0" 2>/dev/null`
then
echo "parent $parent"
fi
echo "author `git var GIT_AUTHOR_IDENT`"
echo "committer `git var GIT_COMMITTER_IDENT`"
echo
printf '%s' "$clean_message"
}
_gen_ChangeId() {
_gen_ChangeIdInput |
git hash-object -t commit --stdin
}
add_ChangeId

6
.husky/pre-commit Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
# shellcheck source=./_/husky.sh
. "$(dirname "$0")/_/husky.sh"
"$(dirname "$0")/pre-commit.copyright" "$@"

63
.husky/pre-commit.copyright Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
# A hook script that checks if files staged for commit have updated Arm copyright year.
# In case they are not - updates the years and prompts user to add them to the change.
# This hook is called on "git commit" after changes have been staged, but before commit
# message has to be provided.
RED="\033[00;31m"
YELLOW="\033[00;33m"
BLANK="\033[00;00m"
FILES=`git diff --cached --name-only HEAD`
YEAR_NOW=`date +"%Y"`
YEAR_RGX="[0-9][0-9][0-9][0-9]"
ARM_RGX="\(ARM\|Arm\|arm\)"
exit_code=0
function user_warning() {
echo -e "Copyright of $RED$FILE$BLANK is out of date/incorrect"
echo -e "Updated copyright to"
grep -nr "opyright.*$YEAR_RGX.*$ARM_RGX" "$FILE"
echo
}
while read -r FILE; do
if [ -z "$FILE" ]
then
break
fi
# Check if correct copyright notice is in file.
# To reduce false positives, we assume files with no
# copyright notice do not require it.
if ! grep "opyright.*$YEAR_NOW.*$ARM_RGX" "$FILE">/dev/null 2>&1
then
# If it is "from_date - to_date" type of entry - change to_date entry.
if grep "opyright.*$YEAR_RGX.*-.*$YEAR_RGX.*$ARM_RGX" "$FILE" >/dev/null 2>&1
then
exit_code=1
sed -i "s/\(opyright.*\)$YEAR_RGX\(.*$ARM_RGX\)/\1$(date +"%Y"), Arm/" $FILE
user_warning
# If it is single "date" type of entry - add the copyright extension to current year.
elif grep "opyright.*$YEAR_RGX.*$ARM_RGX" "$FILE" >/dev/null 2>&1
then
exit_code=1
sed -i "s/\(opyright.*$YEAR_RGX\)\(.*$ARM_RGX\)/\1-$(date +"%Y"), Arm/" $FILE
user_warning
fi
# Even if the year is correct - verify that Arm copyright is formatted correctly.
elif grep "opyright.*\(ARM\|arm\)" "$FILE">/dev/null 2>&1
then
exit_code=1
sed -i "s/\(opyright.*\)\(ARM\|arm\)/\1Arm/" $FILE
user_warning
fi
done <<< "$FILES"
if [ $exit_code -eq 1 ]
then
echo -e "$RED""Please stage updated files$BLANK before commiting or use$YELLOW git commit --no-verify$BLANK to skip copyright check"
fi
exit $exit_code

8
.husky/prepare-commit-msg Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
# shellcheck source=./_/husky.sh
. "$(dirname "$0")/_/husky.sh"
if ! git config --get tf-a.disableCommitizen > /dev/null; then
"$(dirname "$0")/prepare-commit-msg.cz" "$@"
fi

28
.husky/prepare-commit-msg.cz Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash
file="$1"
type="$2"
if [ -z "$type" ]; then # only run on new commits
#
# Save any commit message trailers generated by Git.
#
trailers=$(git interpret-trailers --parse "$file")
#
# Execute the Commitizen hook.
#
(exec < "/dev/tty" && npx --no-install git-cz --hook) || true
#
# Restore any trailers that Commitizen might have overwritten.
#
printf "\n" >> "$file"
while IFS= read -r trailer; do
git interpret-trailers --in-place --trailer "$trailer" "$file"
done <<< "$trailers"
fi

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v16.17.1

32
.readthedocs.yaml Normal file
View File

@ -0,0 +1,32 @@
# Copyright (c) 2023, Arm Limited. All rights reserved
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Configuration file for the readthedocs deploy
# Available at https://trustedfirmware-a.readthedocs.io/en/latest/
# readthedocs config version
version: 2
build:
os: ubuntu-22.04 # Ubuntu Jammy LTS
tools:
python: "3.10"
apt_packages:
- plantuml
- librsvg2-bin
jobs:
post_create_environment:
- pip install poetry=="1.3.2"
- poetry config virtualenvs.create false
post_install:
- poetry install --with doc
sphinx:
configuration: docs/conf.py
# Auxiliary formats to export to (in addition to the default HTML output).
formats:
- pdf

151
.versionrc.js Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2021-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* eslint-env es6 */
"use strict";
const fs = require("fs");
const yaml = require("js-yaml");
/*
* The types and scopes accepted by both Commitlint and Commitizen are defined by the changelog
* configuration file - `changelog.yaml` - as they decide which section of the changelog commits
* with a given type and scope are placed in.
*/
let changelog;
try {
const contents = fs.readFileSync("changelog.yaml", "utf8");
changelog = yaml.load(contents);
} catch (err) {
console.log(err);
throw err;
}
/*
* The next couple of functions are just used to transform the changelog YAML configuration
* structure into one accepted by the Conventional Changelog adapter (conventional-changelog-tf-a).
*/
function getTypes(sections) {
return sections.map(section => {
return {
"type": section.type,
"section": section.hidden ? undefined : section.title,
"hidden": section.hidden || false,
};
})
}
function getSections(subsections) {
return subsections.flatMap(subsection => {
const scope = subsection.scope ? [ subsection.scope ] : [];
return {
"title": subsection.title,
"sections": getSections(subsection.subsections || []),
"scopes": scope.concat(subsection.deprecated || []),
};
})
};
const types = getTypes(changelog.sections);
const sections = getSections(changelog.subsections);
module.exports = {
"header": "# Change Log & Release Notes\n\nThis document contains a summary of the new features, changes, fixes and known\nissues in each release of Trusted Firmware-A.\n",
"preset": {
"name": "tf-a",
"commitUrlFormat": "https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/{{hash}}",
"compareUrlFormat": "https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/tags/{{previousTag}}..refs/tags/{{currentTag}}",
"userUrlFormat": "https://github.com/{{user}}",
"types": types,
"sections": sections,
},
"infile": "docs/change-log.md",
"skip": {
"commit": true,
"tag": true
},
"bumpFiles": [
{
"filename": "package.json",
"type": "json"
},
{
"filename": "pyproject.toml",
"updater": {
"readVersion": function (contents) {
const _ver = contents.match(/version\s=.*"(\d)\.(\d)\.(\d)/);
return `${_ver[1]}.${_ver[2]}.${_ver[2]}`;
},
"writeVersion": function (contents, version) {
const _ver = 'version = "' + version + '"'
return contents.replace(/^(version\s=\s")((\d).?)*$/m, _ver)
}
},
},
{
"filename": "package-lock.json",
"type": "json"
},
{
"filename": "docs/conf.py",
"updater": {
"readVersion": function (contents) {
const _ver = contents.match(/version\s=.*"(\d)\.(\d)\.(\d)/);
return `${_ver[1]}.${_ver[2]}.${_ver[2]}`;
},
"writeVersion": function (contents, version) {
const _ver = 'version = "' + version + '"'
const _rel = 'release = "' + version + '"'
contents = contents.replace(/^(version\s=\s")((\d).?)*$/m, _ver)
contents = contents.replace(/^(release\s=\s")((\d).?)*$/m, _rel)
return contents
}
},
},
{
"filename": "tools/conventional-changelog-tf-a/package.json",
"type": "json"
},
{
"filename": "Makefile",
"updater": {
"readVersion": function (contents) {
const major = contents.match(/^VERSION_MAJOR\s*:=\s*(\d+?)$/m)[1];
const minor = contents.match(/^VERSION_MINOR\s*:=\s*(\d+?)$/m)[1];
const patch = contents.match(/^VERSION_PATCH\s*:=\s*(\d+?)$/m)[1];
return `${major}.${minor}.${patch}`;
},
"writeVersion": function (contents, version) {
const major = version.split(".")[0];
const minor = version.split(".")[1];
const patch = version.split(".")[2];
contents = contents.replace(/^(VERSION_MAJOR\s*:=\s*)(\d+?)$/m, `$1${major}`);
contents = contents.replace(/^(VERSION_MINOR\s*:=\s*)(\d+?)$/m, `$1${minor}`);
contents = contents.replace(/^(VERSION_PATCH\s*:=\s*)(\d+?)$/m, `$1${patch}`);
return contents;
}
}
}
]
};

1781
Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
/*
* Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "../bl1_private.h"
/*******************************************************************************
* TODO: Function that does the first bit of architectural setup.
******************************************************************************/
void bl1_arch_setup(void)
{
}

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <context.h>
#include <common/debug.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <plat/common/platform.h>
#include <smccc_helpers.h>
#include "../bl1_private.h"
/*
* Following arrays will be used for context management.
* There are 2 instances, for the Secure and Non-Secure contexts.
*/
static cpu_context_t bl1_cpu_context[2];
static smc_ctx_t bl1_smc_context[2];
/* Following contains the next cpu context pointer. */
static void *bl1_next_cpu_context_ptr;
/* Following contains the next smc context pointer. */
static void *bl1_next_smc_context_ptr;
/* Following functions are used for SMC context handling */
void *smc_get_ctx(unsigned int security_state)
{
assert(sec_state_is_valid(security_state));
return &bl1_smc_context[security_state];
}
void smc_set_next_ctx(unsigned int security_state)
{
assert(sec_state_is_valid(security_state));
bl1_next_smc_context_ptr = &bl1_smc_context[security_state];
}
void *smc_get_next_ctx(void)
{
return bl1_next_smc_context_ptr;
}
/* Following functions are used for CPU context handling */
void *cm_get_context(uint32_t security_state)
{
assert(sec_state_is_valid(security_state));
return &bl1_cpu_context[security_state];
}
void cm_set_next_context(void *context)
{
assert(context != NULL);
bl1_next_cpu_context_ptr = context;
}
void *cm_get_next_context(void)
{
return bl1_next_cpu_context_ptr;
}
/*******************************************************************************
* Following function copies GP regs r0-r4, lr and spsr,
* from the CPU context to the SMC context structures.
******************************************************************************/
static void copy_cpu_ctx_to_smc_ctx(const regs_t *cpu_reg_ctx,
smc_ctx_t *next_smc_ctx)
{
next_smc_ctx->r0 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R0);
next_smc_ctx->r1 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R1);
next_smc_ctx->r2 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R2);
next_smc_ctx->r3 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R3);
next_smc_ctx->lr_mon = read_ctx_reg(cpu_reg_ctx, CTX_LR);
next_smc_ctx->spsr_mon = read_ctx_reg(cpu_reg_ctx, CTX_SPSR);
next_smc_ctx->scr = read_ctx_reg(cpu_reg_ctx, CTX_SCR);
}
/*******************************************************************************
* Following function flushes the SMC & CPU context pointer and its data.
******************************************************************************/
static void flush_smc_and_cpu_ctx(void)
{
flush_dcache_range((uintptr_t)&bl1_next_smc_context_ptr,
sizeof(bl1_next_smc_context_ptr));
flush_dcache_range((uintptr_t)bl1_next_smc_context_ptr,
sizeof(smc_ctx_t));
flush_dcache_range((uintptr_t)&bl1_next_cpu_context_ptr,
sizeof(bl1_next_cpu_context_ptr));
flush_dcache_range((uintptr_t)bl1_next_cpu_context_ptr,
sizeof(cpu_context_t));
}
/*******************************************************************************
* This function prepares the context for Secure/Normal world images.
* Normal world images are transitioned to HYP(if supported) else SVC.
******************************************************************************/
void bl1_prepare_next_image(unsigned int image_id)
{
unsigned int security_state, mode = MODE32_svc;
image_desc_t *desc;
entry_point_info_t *next_bl_ep;
/* Get the image descriptor. */
desc = bl1_plat_get_image_desc(image_id);
assert(desc != NULL);
/* Get the entry point info. */
next_bl_ep = &desc->ep_info;
/* Get the image security state. */
security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);
/* Prepare the SPSR for the next BL image. */
if ((security_state != SECURE) && (GET_VIRT_EXT(read_id_pfr1()) != 0U)) {
mode = MODE32_hyp;
}
next_bl_ep->spsr = SPSR_MODE32(mode, SPSR_T_ARM,
SPSR_E_LITTLE, DISABLE_ALL_EXCEPTIONS);
/* Allow platform to make change */
bl1_plat_set_ep_info(image_id, next_bl_ep);
/* Prepare the cpu context for the next BL image. */
cm_init_my_context(next_bl_ep);
cm_prepare_el3_exit(security_state);
cm_set_next_context(cm_get_context(security_state));
/* Prepare the smc context for the next BL image. */
smc_set_next_ctx(security_state);
copy_cpu_ctx_to_smc_ctx(get_regs_ctx(cm_get_next_context()),
smc_get_next_ctx());
/*
* If the next image is non-secure, then we need to program the banked
* non secure sctlr. This is not required when the next image is secure
* because in AArch32, we expect the secure world to have the same
* SCTLR settings.
*/
if (security_state == NON_SECURE) {
cpu_context_t *ctx = cm_get_context(security_state);
u_register_t ns_sctlr;
/* Temporarily set the NS bit to access NS SCTLR */
write_scr(read_scr() | SCR_NS_BIT);
isb();
ns_sctlr = read_ctx_reg(get_regs_ctx(ctx), CTX_NS_SCTLR);
write_sctlr(ns_sctlr);
isb();
write_scr(read_scr() & ~SCR_NS_BIT);
isb();
}
/*
* Flush the SMC & CPU context and the (next)pointers,
* to access them after caches are disabled.
*/
flush_smc_and_cpu_ctx();
/* Indicate that image is in execution state. */
desc->state = IMAGE_STATE_EXECUTED;
print_entry_point_info(next_bl_ep);
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
#include <context.h>
#include <el3_common_macros.S>
#include <smccc_helpers.h>
#include <smccc_macros.S>
.globl bl1_vector_table
.globl bl1_entrypoint
/* -----------------------------------------------------
* Setup the vector table to support SVC & MON mode.
* -----------------------------------------------------
*/
vector_base bl1_vector_table
b bl1_entrypoint
b report_exception /* Undef */
b bl1_aarch32_smc_handler /* SMC call */
b report_prefetch_abort /* Prefetch abort */
b report_data_abort /* Data abort */
b report_exception /* Reserved */
b report_exception /* IRQ */
b report_exception /* FIQ */
/* -----------------------------------------------------
* bl1_entrypoint() is the entry point into the trusted
* firmware code when a cpu is released from warm or
* cold reset.
* -----------------------------------------------------
*/
func bl1_entrypoint
/* ---------------------------------------------------------------------
* If the reset address is programmable then bl1_entrypoint() is
* executed only on the cold boot path. Therefore, we can skip the warm
* boot mailbox mechanism.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=bl1_vector_table \
_pie_fixup_size=0
/* -----------------------------------------------------
* Perform BL1 setup
* -----------------------------------------------------
*/
bl bl1_setup
/* -----------------------------------------------------
* Jump to main function.
* -----------------------------------------------------
*/
bl bl1_main
/* -----------------------------------------------------
* Jump to next image.
* -----------------------------------------------------
*/
/*
* Get the smc_context for next BL image,
* program the gp/system registers and save it in `r4`.
*/
bl smc_get_next_ctx
mov r4, r0
/* Only turn-off MMU if going to secure world */
ldr r5, [r4, #SMC_CTX_SCR]
tst r5, #SCR_NS_BIT
bne skip_mmu_off
/*
* MMU needs to be disabled because both BL1 and BL2/BL2U execute
* in PL1, and therefore share the same address space.
* BL2/BL2U will initialize the address space according to its
* own requirement.
*/
bl disable_mmu_icache_secure
stcopr r0, TLBIALL
dsb sy
isb
skip_mmu_off:
/* Restore smc_context from `r4` and exit secure monitor mode. */
mov r0, r4
monitor_exit
endfunc bl1_entrypoint

View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <bl1/bl1.h>
#include <common/bl_common.h>
#include <context.h>
#include <lib/xlat_tables/xlat_tables.h>
#include <smccc_helpers.h>
#include <smccc_macros.S>
.globl bl1_aarch32_smc_handler
func bl1_aarch32_smc_handler
/* On SMC entry, `sp` points to `smc_ctx_t`. Save `lr`. */
str lr, [sp, #SMC_CTX_LR_MON]
/* ------------------------------------------------
* SMC in BL1 is handled assuming that the MMU is
* turned off by BL2.
* ------------------------------------------------
*/
/* ----------------------------------------------
* Detect if this is a RUN_IMAGE or other SMC.
* ----------------------------------------------
*/
mov lr, #BL1_SMC_RUN_IMAGE
cmp lr, r0
bne smc_handler
/* ------------------------------------------------
* Make sure only Secure world reaches here.
* ------------------------------------------------
*/
ldcopr r8, SCR
tst r8, #SCR_NS_BIT
blne report_exception
/* ---------------------------------------------------------------------
* Pass control to next secure image.
* Here it expects r1 to contain the address of a entry_point_info_t
* structure describing the BL entrypoint.
* ---------------------------------------------------------------------
*/
mov r8, r1
mov r0, r1
bl bl1_print_next_bl_ep_info
#if SPIN_ON_BL1_EXIT
bl print_debug_loop_message
debug_loop:
b debug_loop
#endif
mov r0, r8
bl bl1_plat_prepare_exit
stcopr r0, TLBIALL
dsb sy
isb
/*
* Extract PC and SPSR based on struct `entry_point_info_t`
* and load it in LR and SPSR registers respectively.
*/
ldr lr, [r8, #ENTRY_POINT_INFO_PC_OFFSET]
ldr r1, [r8, #(ENTRY_POINT_INFO_PC_OFFSET + 4)]
msr spsr_xc, r1
/* Some BL32 stages expect lr_svc to provide the BL33 entry address */
cps #MODE32_svc
ldr lr, [r8, #ENTRY_POINT_INFO_LR_SVC_OFFSET]
cps #MODE32_mon
add r8, r8, #ENTRY_POINT_INFO_ARGS_OFFSET
ldm r8, {r0, r1, r2, r3}
exception_return
endfunc bl1_aarch32_smc_handler
/* -----------------------------------------------------
* Save Secure/Normal world context and jump to
* BL1 SMC handler.
* -----------------------------------------------------
*/
func smc_handler
/* -----------------------------------------------------
* Save the GP registers.
* -----------------------------------------------------
*/
smccc_save_gp_mode_regs
/*
* `sp` still points to `smc_ctx_t`. Save it to a register
* and restore the C runtime stack pointer to `sp`.
*/
mov r6, sp
ldr sp, [r6, #SMC_CTX_SP_MON]
ldr r0, [r6, #SMC_CTX_SCR]
and r7, r0, #SCR_NS_BIT /* flags */
/* Switch to Secure Mode */
bic r0, #SCR_NS_BIT
stcopr r0, SCR
isb
/* If caller is from Secure world then turn on the MMU */
tst r7, #SCR_NS_BIT
bne skip_mmu_on
/* Turn on the MMU */
mov r0, #DISABLE_DCACHE
bl enable_mmu_svc_mon
/*
* Invalidate `smc_ctx_t` in data cache to prevent dirty data being
* used.
*/
mov r0, r6
mov r1, #SMC_CTX_SIZE
bl inv_dcache_range
/* Enable the data cache. */
ldcopr r9, SCTLR
orr r9, r9, #SCTLR_C_BIT
stcopr r9, SCTLR
isb
skip_mmu_on:
/* Prepare arguments for BL1 SMC wrapper. */
ldr r0, [r6, #SMC_CTX_GPREG_R0] /* smc_fid */
mov r1, #0 /* cookie */
mov r2, r6 /* handle */
mov r3, r7 /* flags */
bl bl1_smc_wrapper
/* Get the smc_context for next BL image */
bl smc_get_next_ctx
mov r4, r0
/* Only turn-off MMU if going to secure world */
ldr r5, [r4, #SMC_CTX_SCR]
tst r5, #SCR_NS_BIT
bne skip_mmu_off
/* Disable the MMU */
bl disable_mmu_icache_secure
stcopr r0, TLBIALL
dsb sy
isb
skip_mmu_off:
/* -----------------------------------------------------
* Do the transition to next BL image.
* -----------------------------------------------------
*/
mov r0, r4
monitor_exit
endfunc smc_handler

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include "../bl1_private.h"
/*******************************************************************************
* Function that does the first bit of architectural setup that affects
* execution in the non-secure address space.
******************************************************************************/
void bl1_arch_setup(void)
{
/* Set the next EL to be AArch64 */
write_scr_el3(read_scr_el3() | SCR_RW_BIT);
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <context.h>
#include <common/debug.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <plat/common/platform.h>
#include "../bl1_private.h"
/* Following contains the cpu context pointers. */
static void *bl1_cpu_context_ptr[2];
entry_point_info_t *bl2_ep_info;
void *cm_get_context(uint32_t security_state)
{
assert(sec_state_is_valid(security_state));
return bl1_cpu_context_ptr[security_state];
}
void cm_set_context(void *context, uint32_t security_state)
{
assert(sec_state_is_valid(security_state));
bl1_cpu_context_ptr[security_state] = context;
}
#if ENABLE_RME
/*******************************************************************************
* This function prepares the entry point information to run BL2 in Root world,
* i.e. EL3, for the case when FEAT_RME is enabled.
******************************************************************************/
void bl1_prepare_next_image(unsigned int image_id)
{
image_desc_t *bl2_desc;
assert(image_id == BL2_IMAGE_ID);
/* Get the image descriptor. */
bl2_desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);
assert(bl2_desc != NULL);
/* Get the entry point info. */
bl2_ep_info = &bl2_desc->ep_info;
bl2_ep_info->spsr = (uint32_t)SPSR_64(MODE_EL3, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
/*
* Flush cache since bl2_ep_info is accessed after MMU is disabled
* before jumping to BL2.
*/
flush_dcache_range((uintptr_t)bl2_ep_info, sizeof(entry_point_info_t));
/* Indicate that image is in execution state. */
bl2_desc->state = IMAGE_STATE_EXECUTED;
/* Print debug info and flush the console before running BL2. */
print_entry_point_info(bl2_ep_info);
}
#else
/*******************************************************************************
* This function prepares the context for Secure/Normal world images.
* Normal world images are transitioned to EL2(if supported) else EL1.
******************************************************************************/
void bl1_prepare_next_image(unsigned int image_id)
{
/*
* Following array will be used for context management.
* There are 2 instances, for the Secure and Non-Secure contexts.
*/
static cpu_context_t bl1_cpu_context[2];
unsigned int security_state, mode = MODE_EL1;
image_desc_t *desc;
entry_point_info_t *next_bl_ep;
#if CTX_INCLUDE_AARCH32_REGS
/*
* Ensure that the build flag to save AArch32 system registers in CPU
* context is not set for AArch64-only platforms.
*/
if (el_implemented(1) == EL_IMPL_A64ONLY) {
ERROR("EL1 supports AArch64-only. Please set build flag "
"CTX_INCLUDE_AARCH32_REGS = 0\n");
panic();
}
#endif
/* Get the image descriptor. */
desc = bl1_plat_get_image_desc(image_id);
assert(desc != NULL);
/* Get the entry point info. */
next_bl_ep = &desc->ep_info;
/* Get the image security state. */
security_state = GET_SECURITY_STATE(next_bl_ep->h.attr);
/* Setup the Secure/Non-Secure context if not done already. */
if (cm_get_context(security_state) == NULL)
cm_set_context(&bl1_cpu_context[security_state], security_state);
/* Prepare the SPSR for the next BL image. */
if ((security_state != SECURE) && (el_implemented(2) != EL_IMPL_NONE)) {
mode = MODE_EL2;
}
next_bl_ep->spsr = (uint32_t)SPSR_64((uint64_t) mode,
(uint64_t)MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
/* Allow platform to make change */
bl1_plat_set_ep_info(image_id, next_bl_ep);
/* Prepare the context for the next BL image. */
cm_init_my_context(next_bl_ep);
cm_prepare_el3_exit(security_state);
/* Indicate that image is in execution state. */
desc->state = IMAGE_STATE_EXECUTED;
print_entry_point_info(next_bl_ep);
}
#endif /* ENABLE_RME */

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <common/bl_common.h>
#include <el3_common_macros.S>
.globl bl1_entrypoint
.globl bl1_run_bl2_in_root
/* -----------------------------------------------------
* bl1_entrypoint() is the entry point into the trusted
* firmware code when a cpu is released from warm or
* cold reset.
* -----------------------------------------------------
*/
func bl1_entrypoint
/* ---------------------------------------------------------------------
* If the reset address is programmable then bl1_entrypoint() is
* executed only on the cold boot path. Therefore, we can skip the warm
* boot mailbox mechanism.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=bl1_exceptions \
_pie_fixup_size=0
/* --------------------------------------------------------------------
* Perform BL1 setup
* --------------------------------------------------------------------
*/
bl bl1_setup
#if ENABLE_PAUTH
/* --------------------------------------------------------------------
* Program APIAKey_EL1 and enable pointer authentication.
* --------------------------------------------------------------------
*/
bl pauth_init_enable_el3
#endif /* ENABLE_PAUTH */
/* --------------------------------------------------------------------
* Initialize platform and jump to our c-entry point
* for this type of reset.
* --------------------------------------------------------------------
*/
bl bl1_main
#if ENABLE_PAUTH
/* --------------------------------------------------------------------
* Disable pointer authentication before jumping to next boot image.
* --------------------------------------------------------------------
*/
bl pauth_disable_el3
#endif /* ENABLE_PAUTH */
/* --------------------------------------------------
* Do the transition to next boot image.
* --------------------------------------------------
*/
#if ENABLE_RME
b bl1_run_bl2_in_root
#else
b el3_exit
#endif
endfunc bl1_entrypoint
/* -----------------------------------------------------
* void bl1_run_bl2_in_root();
* This function runs BL2 in root/EL3 when RME is enabled.
* -----------------------------------------------------
*/
func bl1_run_bl2_in_root
/* read bl2_ep_info */
adrp x20, bl2_ep_info
add x20, x20, :lo12:bl2_ep_info
ldr x20, [x20]
/* ---------------------------------------------
* MMU needs to be disabled because BL2 executes
* in EL3. It will initialize the address space
* according to its own requirements.
* ---------------------------------------------
*/
bl disable_mmu_icache_el3
tlbi alle3
ldp x0, x1, [x20, #ENTRY_POINT_INFO_PC_OFFSET]
msr elr_el3, x0
msr spsr_el3, x1
ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)]
ldp x4, x5, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x20)]
ldp x2, x3, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x10)]
ldp x0, x1, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x0)]
exception_return
endfunc bl1_run_bl2_in_root

View File

@ -0,0 +1,287 @@
/*
* Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <bl1/bl1.h>
#include <common/bl_common.h>
#include <context.h>
/* -----------------------------------------------------------------------------
* Very simple stackless exception handlers used by BL1.
* -----------------------------------------------------------------------------
*/
.globl bl1_exceptions
vector_base bl1_exceptions
/* -----------------------------------------------------
* Current EL with SP0 : 0x0 - 0x200
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSP0
mov x0, #SYNC_EXCEPTION_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSP0
vector_entry IrqSP0
mov x0, #IRQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSP0
vector_entry FiqSP0
mov x0, #FIQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSP0
vector_entry SErrorSP0
mov x0, #SERROR_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSP0
/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x400
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSPx
mov x0, #SYNC_EXCEPTION_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSPx
vector_entry IrqSPx
mov x0, #IRQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSPx
vector_entry FiqSPx
mov x0, #FIQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSPx
vector_entry SErrorSPx
mov x0, #SERROR_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSPx
/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA64
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
/* Expect only SMC exceptions */
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x30, #EC_AARCH64_SMC
b.ne unexpected_sync_exception
b smc_handler64
end_vector_entry SynchronousExceptionA64
vector_entry IrqA64
mov x0, #IRQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA64
vector_entry FiqA64
mov x0, #FIQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA64
vector_entry SErrorA64
mov x0, #SERROR_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA64
/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA32
mov x0, #SYNC_EXCEPTION_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionA32
vector_entry IrqA32
mov x0, #IRQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA32
vector_entry FiqA32
mov x0, #FIQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA32
vector_entry SErrorA32
mov x0, #SERROR_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA32
func smc_handler64
/* ----------------------------------------------
* Detect if this is a RUN_IMAGE or other SMC.
* ----------------------------------------------
*/
mov x30, #BL1_SMC_RUN_IMAGE
cmp x30, x0
b.ne smc_handler
/* ------------------------------------------------
* Make sure only Secure world reaches here.
* ------------------------------------------------
*/
mrs x30, scr_el3
tst x30, #SCR_NS_BIT
b.ne unexpected_sync_exception
/* ----------------------------------------------
* Handling RUN_IMAGE SMC. First switch back to
* SP_EL0 for the C runtime stack.
* ----------------------------------------------
*/
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
msr spsel, #MODE_SP_EL0
mov sp, x30
/* ---------------------------------------------------------------------
* Pass EL3 control to next BL image.
* Here it expects X1 with the address of a entry_point_info_t
* structure describing the next BL image entrypoint.
* ---------------------------------------------------------------------
*/
mov x20, x1
mov x0, x20
bl bl1_print_next_bl_ep_info
ldp x0, x1, [x20, #ENTRY_POINT_INFO_PC_OFFSET]
msr elr_el3, x0
msr spsr_el3, x1
ubfx x0, x1, #MODE_EL_SHIFT, #2
cmp x0, #MODE_EL3
b.ne unexpected_sync_exception
bl disable_mmu_icache_el3
tlbi alle3
dsb ish /* ERET implies ISB, so it is not needed here */
#if SPIN_ON_BL1_EXIT
bl print_debug_loop_message
debug_loop:
b debug_loop
#endif
mov x0, x20
bl bl1_plat_prepare_exit
ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)]
ldp x4, x5, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x20)]
ldp x2, x3, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x10)]
ldp x0, x1, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x0)]
exception_return
endfunc smc_handler64
unexpected_sync_exception:
mov x0, #SYNC_EXCEPTION_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
/* -----------------------------------------------------
* Save Secure/Normal world context and jump to
* BL1 SMC handler.
* -----------------------------------------------------
*/
smc_handler:
/* -----------------------------------------------------
* Save x0-x29 and ARMv8.3-PAuth (if enabled) registers.
* Save PMCR_EL0 and disable Cycle Counter.
* TODO: Revisit to store only SMCCC specified registers.
* -----------------------------------------------------
*/
bl prepare_el3_entry
#if ENABLE_PAUTH
/* -----------------------------------------------------
* Load and program stored APIAKey firmware key.
* Re-enable pointer authentication in EL3, as it was
* disabled before jumping to the next boot image.
* -----------------------------------------------------
*/
bl pauth_load_bl1_apiakey_enable
#endif
/* -----------------------------------------------------
* Populate the parameters for the SMC handler. We
* already have x0-x4 in place. x5 will point to a
* cookie (not used now). x6 will point to the context
* structure (SP_EL3) and x7 will contain flags we need
* to pass to the handler.
* -----------------------------------------------------
*/
mov x5, xzr
mov x6, sp
/* -----------------------------------------------------
* Restore the saved C runtime stack value which will
* become the new SP_EL0 i.e. EL3 runtime stack. It was
* saved in the 'cpu_context' structure prior to the last
* ERET from EL3.
* -----------------------------------------------------
*/
ldr x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
/* ---------------------------------------------
* Switch back to SP_EL0 for the C runtime stack.
* ---------------------------------------------
*/
msr spsel, #MODE_SP_EL0
mov sp, x12
/* -----------------------------------------------------
* Save the SPSR_EL3, ELR_EL3, & SCR_EL3 in case there
* is a world switch during SMC handling.
* -----------------------------------------------------
*/
mrs x16, spsr_el3
mrs x17, elr_el3
mrs x18, scr_el3
stp x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
str x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
bfi x7, x18, #0, #1
/* -----------------------------------------------------
* Go to BL1 SMC handler.
* -----------------------------------------------------
*/
bl bl1_smc_handler
/* -----------------------------------------------------
* Do the transition to next BL image.
* -----------------------------------------------------
*/
b el3_exit

169
bl1/bl1.ld.S Normal file
View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* The .data section gets copied from ROM to RAM at runtime. Its LMA should be
* 16-byte aligned to allow efficient copying of 16-bytes aligned regions in it.
* Its VMA must be page-aligned as it marks the first read/write page.
*/
#define DATA_ALIGN 16
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl1_entrypoint)
MEMORY {
ROM (rx): ORIGIN = BL1_RO_BASE, LENGTH = BL1_RO_LIMIT - BL1_RO_BASE
RAM (rwx): ORIGIN = BL1_RW_BASE, LENGTH = BL1_RW_LIMIT - BL1_RW_BASE
}
SECTIONS {
ROM_REGION_START = ORIGIN(ROM);
ROM_REGION_LENGTH = LENGTH(ROM);
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
. = BL1_RO_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL1_RO_BASE address is not aligned on a page boundary.")
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
*bl1_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >ROM
/* .ARM.extab and .ARM.exidx are only added because Clang needs them */
.ARM.extab . : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >ROM
.ARM.exidx . : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} >ROM
.rodata . : {
__RODATA_START__ = .;
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
/*
* No need to pad out the .rodata section to a page boundary. Next is
* the .data section, which can mapped in ROM with the same memory
* attributes as the .rodata section.
*
* Pad out to 16 bytes though as .data section needs to be 16-byte
* aligned and lld does not align the LMA to the alignment specified
* on the .data section.
*/
__RODATA_END_UNALIGNED__ = .;
__RODATA_END__ = .;
. = ALIGN(16);
} >ROM
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
*bl1_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
*(.vectors)
__RO_END__ = .;
/*
* Pad out to 16 bytes as the .data section needs to be 16-byte aligned
* and lld does not align the LMA to the alignment specified on the
* .data section.
*/
. = ALIGN(16);
} >ROM
#endif /* SEPARATE_CODE_AND_RODATA */
ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__,
"cpu_ops not defined for this platform.")
ROM_REGION_END = .;
. = BL1_RW_BASE;
ASSERT(BL1_RW_BASE == ALIGN(PAGE_SIZE),
"BL1_RW_BASE address is not aligned on a page boundary.")
DATA_SECTION >RAM AT>ROM
__DATA_RAM_START__ = __DATA_START__;
__DATA_RAM_END__ = __DATA_END__;
STACK_SECTION >RAM
BSS_SECTION >RAM
XLAT_TABLE_SECTION >RAM
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct memory
* attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure the rest of
* the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
#endif /* USE_COHERENT_MEM */
__BL1_RAM_START__ = ADDR(.data);
__BL1_RAM_END__ = .;
__DATA_ROM_START__ = LOADADDR(.data);
__DATA_SIZE__ = SIZEOF(.data);
/*
* The .data section is the last PROGBITS section so its end marks the end
* of BL1's actual content in Trusted ROM.
*/
__BL1_ROM_END__ = __DATA_ROM_START__ + __DATA_SIZE__;
ASSERT(__BL1_ROM_END__ <= BL1_RO_LIMIT,
"BL1's ROM content has exceeded its limit.")
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
ASSERT(. <= BL1_RW_LIMIT, "BL1's RW section has exceeded its limit.")
RAM_REGION_END = .;
}

38
bl1/bl1.mk Normal file
View File

@ -0,0 +1,38 @@
#
# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
BL1_SOURCES += bl1/${ARCH}/bl1_arch_setup.c \
bl1/${ARCH}/bl1_context_mgmt.c \
bl1/${ARCH}/bl1_entrypoint.S \
bl1/${ARCH}/bl1_exceptions.S \
bl1/bl1_main.c \
lib/cpus/${ARCH}/cpu_helpers.S \
lib/cpus/errata_report.c \
lib/el3_runtime/${ARCH}/context_mgmt.c \
plat/common/plat_bl1_common.c \
plat/common/${ARCH}/platform_up_stack.S \
${MBEDTLS_SOURCES}
ifeq (${ARCH},aarch64)
BL1_SOURCES += lib/cpus/aarch64/dsu_helpers.S \
lib/el3_runtime/aarch64/context.S
endif
ifeq (${TRUSTED_BOARD_BOOT},1)
BL1_SOURCES += bl1/bl1_fwu.c
endif
ifeq (${ENABLE_PMF},1)
BL1_SOURCES += lib/pmf/pmf_main.c
endif
ifneq ($(findstring gcc,$(notdir $(LD))),)
BL1_LDFLAGS += -Wl,--sort-section=alignment
else ifneq ($(findstring ld,$(notdir $(LD))),)
BL1_LDFLAGS += --sort-section=alignment
endif
BL1_DEFAULT_LINKER_SCRIPT_SOURCE := bl1/bl1.ld.S

745
bl1/bl1_fwu.c Normal file
View File

@ -0,0 +1,745 @@
/*
* Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <platform_def.h>
#include <arch_helpers.h>
#include <bl1/bl1.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <context.h>
#include <drivers/auth/auth_mod.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/utils.h>
#include <plat/common/platform.h>
#include <smccc_helpers.h>
#include "bl1_private.h"
/*
* Function declarations.
*/
static int bl1_fwu_image_copy(unsigned int image_id,
uintptr_t image_src,
unsigned int block_size,
unsigned int image_size,
unsigned int flags);
static int bl1_fwu_image_auth(unsigned int image_id,
uintptr_t image_src,
unsigned int image_size,
unsigned int flags);
static int bl1_fwu_image_execute(unsigned int image_id,
void **handle,
unsigned int flags);
static register_t bl1_fwu_image_resume(register_t image_param,
void **handle,
unsigned int flags);
static int bl1_fwu_sec_image_done(void **handle,
unsigned int flags);
static int bl1_fwu_image_reset(unsigned int image_id,
unsigned int flags);
__dead2 static void bl1_fwu_done(void *client_cookie, void *reserved);
/*
* This keeps track of last executed secure image id.
*/
static unsigned int sec_exec_image_id = INVALID_IMAGE_ID;
/*******************************************************************************
* Top level handler for servicing FWU SMCs.
******************************************************************************/
u_register_t bl1_fwu_smc_handler(unsigned int smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
unsigned int flags)
{
switch (smc_fid) {
case FWU_SMC_IMAGE_COPY:
SMC_RET1(handle, bl1_fwu_image_copy((uint32_t)x1, x2,
(uint32_t)x3, (uint32_t)x4, flags));
case FWU_SMC_IMAGE_AUTH:
SMC_RET1(handle, bl1_fwu_image_auth((uint32_t)x1, x2,
(uint32_t)x3, flags));
case FWU_SMC_IMAGE_EXECUTE:
SMC_RET1(handle, bl1_fwu_image_execute((uint32_t)x1, &handle,
flags));
case FWU_SMC_IMAGE_RESUME:
SMC_RET1(handle, bl1_fwu_image_resume((register_t)x1, &handle,
flags));
case FWU_SMC_SEC_IMAGE_DONE:
SMC_RET1(handle, bl1_fwu_sec_image_done(&handle, flags));
case FWU_SMC_IMAGE_RESET:
SMC_RET1(handle, bl1_fwu_image_reset((uint32_t)x1, flags));
case FWU_SMC_UPDATE_DONE:
bl1_fwu_done((void *)x1, NULL);
default:
assert(false); /* Unreachable */
break;
}
SMC_RET1(handle, SMC_UNK);
}
/*******************************************************************************
* Utility functions to keep track of the images that are loaded at any time.
******************************************************************************/
#ifdef PLAT_FWU_MAX_SIMULTANEOUS_IMAGES
#define FWU_MAX_SIMULTANEOUS_IMAGES PLAT_FWU_MAX_SIMULTANEOUS_IMAGES
#else
#define FWU_MAX_SIMULTANEOUS_IMAGES 10
#endif
static unsigned int bl1_fwu_loaded_ids[FWU_MAX_SIMULTANEOUS_IMAGES] = {
[0 ... FWU_MAX_SIMULTANEOUS_IMAGES-1] = INVALID_IMAGE_ID
};
/*
* Adds an image_id to the bl1_fwu_loaded_ids array.
* Returns 0 on success, 1 on error.
*/
static int bl1_fwu_add_loaded_id(unsigned int image_id)
{
int i;
/* Check if the ID is already in the list */
for (i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
if (bl1_fwu_loaded_ids[i] == image_id)
return 0;
}
/* Find an empty slot */
for (i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
if (bl1_fwu_loaded_ids[i] == INVALID_IMAGE_ID) {
bl1_fwu_loaded_ids[i] = image_id;
return 0;
}
}
return 1;
}
/*
* Removes an image_id from the bl1_fwu_loaded_ids array.
* Returns 0 on success, 1 on error.
*/
static int bl1_fwu_remove_loaded_id(unsigned int image_id)
{
int i;
/* Find the ID */
for (i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
if (bl1_fwu_loaded_ids[i] == image_id) {
bl1_fwu_loaded_ids[i] = INVALID_IMAGE_ID;
return 0;
}
}
return 1;
}
/*******************************************************************************
* This function checks if the specified image overlaps another image already
* loaded. It returns 0 if there is no overlap, a negative error code otherwise.
******************************************************************************/
static int bl1_fwu_image_check_overlaps(unsigned int image_id)
{
const image_desc_t *desc, *checked_desc;
const image_info_t *info, *checked_info;
uintptr_t image_base, image_end;
uintptr_t checked_image_base, checked_image_end;
checked_desc = bl1_plat_get_image_desc(image_id);
checked_info = &checked_desc->image_info;
/* Image being checked mustn't be empty. */
assert(checked_info->image_size != 0);
checked_image_base = checked_info->image_base;
checked_image_end = checked_image_base + checked_info->image_size - 1;
/* No need to check for overflows, it's done in bl1_fwu_image_copy(). */
for (int i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
/* Skip INVALID_IMAGE_IDs and don't check image against itself */
if ((bl1_fwu_loaded_ids[i] == INVALID_IMAGE_ID) ||
(bl1_fwu_loaded_ids[i] == image_id))
continue;
desc = bl1_plat_get_image_desc(bl1_fwu_loaded_ids[i]);
/* Only check images that are loaded or being loaded. */
assert ((desc != NULL) && (desc->state != IMAGE_STATE_RESET));
info = &desc->image_info;
/* There cannot be overlaps with an empty image. */
if (info->image_size == 0)
continue;
image_base = info->image_base;
image_end = image_base + info->image_size - 1;
/*
* Overflows cannot happen. It is checked in
* bl1_fwu_image_copy() when the image goes from RESET to
* COPYING or COPIED.
*/
assert (image_end > image_base);
/* Check if there are overlaps. */
if (!((image_end < checked_image_base) ||
(checked_image_end < image_base))) {
VERBOSE("Image with ID %d overlaps existing image with ID %d",
checked_desc->image_id, desc->image_id);
return -EPERM;
}
}
return 0;
}
/*******************************************************************************
* This function is responsible for copying secure images in AP Secure RAM.
******************************************************************************/
static int bl1_fwu_image_copy(unsigned int image_id,
uintptr_t image_src,
unsigned int block_size,
unsigned int image_size,
unsigned int flags)
{
uintptr_t dest_addr;
unsigned int remaining;
image_desc_t *desc;
/* Get the image descriptor. */
desc = bl1_plat_get_image_desc(image_id);
if (desc == NULL) {
WARN("BL1-FWU: Invalid image ID %u\n", image_id);
return -EPERM;
}
/*
* The request must originate from a non-secure caller and target a
* secure image. Any other scenario is invalid.
*/
if (GET_SECURITY_STATE(flags) == SECURE) {
WARN("BL1-FWU: Copy not allowed from secure world.\n");
return -EPERM;
}
if (GET_SECURITY_STATE(desc->ep_info.h.attr) == NON_SECURE) {
WARN("BL1-FWU: Copy not allowed for non-secure images.\n");
return -EPERM;
}
/* Check whether the FWU state machine is in the correct state. */
if ((desc->state != IMAGE_STATE_RESET) &&
(desc->state != IMAGE_STATE_COPYING)) {
WARN("BL1-FWU: Copy not allowed at this point of the FWU"
" process.\n");
return -EPERM;
}
if ((image_src == 0U) || (block_size == 0U) ||
check_uptr_overflow(image_src, block_size - 1)) {
WARN("BL1-FWU: Copy not allowed due to invalid image source"
" or block size\n");
return -ENOMEM;
}
if (desc->state == IMAGE_STATE_COPYING) {
/*
* There must have been at least 1 copy operation for this image
* previously.
*/
assert(desc->copied_size != 0U);
/*
* The image size must have been recorded in the 1st copy
* operation.
*/
image_size = desc->image_info.image_size;
assert(image_size != 0);
assert(desc->copied_size < image_size);
INFO("BL1-FWU: Continuing image copy in blocks\n");
} else { /* desc->state == IMAGE_STATE_RESET */
INFO("BL1-FWU: Initial call to copy an image\n");
/*
* image_size is relevant only for the 1st copy request, it is
* then ignored for subsequent calls for this image.
*/
if (image_size == 0) {
WARN("BL1-FWU: Copy not allowed due to invalid image"
" size\n");
return -ENOMEM;
}
/* Check that the image size to load is within limit */
if (image_size > desc->image_info.image_max_size) {
WARN("BL1-FWU: Image size out of bounds\n");
return -ENOMEM;
}
/* Save the given image size. */
desc->image_info.image_size = image_size;
/* Make sure the image doesn't overlap other images. */
if (bl1_fwu_image_check_overlaps(image_id) != 0) {
desc->image_info.image_size = 0;
WARN("BL1-FWU: This image overlaps another one\n");
return -EPERM;
}
/*
* copied_size must be explicitly initialized here because the
* FWU code doesn't necessarily do it when it resets the state
* machine.
*/
desc->copied_size = 0;
}
/*
* If the given block size is more than the total image size
* then clip the former to the latter.
*/
remaining = image_size - desc->copied_size;
if (block_size > remaining) {
WARN("BL1-FWU: Block size is too big, clipping it.\n");
block_size = remaining;
}
/* Make sure the source image is mapped in memory. */
if (bl1_plat_mem_check(image_src, block_size, flags) != 0) {
WARN("BL1-FWU: Source image is not mapped.\n");
return -ENOMEM;
}
if (bl1_fwu_add_loaded_id(image_id) != 0) {
WARN("BL1-FWU: Too many images loaded at the same time.\n");
return -ENOMEM;
}
/* Allow the platform to handle pre-image load before copying */
if (desc->state == IMAGE_STATE_RESET) {
if (bl1_plat_handle_pre_image_load(image_id) != 0) {
ERROR("BL1-FWU: Failure in pre-image load of image id %d\n",
image_id);
return -EPERM;
}
}
/* Everything looks sane. Go ahead and copy the block of data. */
dest_addr = desc->image_info.image_base + desc->copied_size;
(void)memcpy((void *) dest_addr, (const void *) image_src, block_size);
flush_dcache_range(dest_addr, block_size);
desc->copied_size += block_size;
desc->state = (block_size == remaining) ?
IMAGE_STATE_COPIED : IMAGE_STATE_COPYING;
INFO("BL1-FWU: Copy operation successful.\n");
return 0;
}
/*******************************************************************************
* This function is responsible for authenticating Normal/Secure images.
******************************************************************************/
static int bl1_fwu_image_auth(unsigned int image_id,
uintptr_t image_src,
unsigned int image_size,
unsigned int flags)
{
int result;
uintptr_t base_addr;
unsigned int total_size;
image_desc_t *desc;
/* Get the image descriptor. */
desc = bl1_plat_get_image_desc(image_id);
if (desc == NULL)
return -EPERM;
if (GET_SECURITY_STATE(flags) == SECURE) {
if (desc->state != IMAGE_STATE_RESET) {
WARN("BL1-FWU: Authentication from secure world "
"while in invalid state\n");
return -EPERM;
}
} else {
if (GET_SECURITY_STATE(desc->ep_info.h.attr) == SECURE) {
if (desc->state != IMAGE_STATE_COPIED) {
WARN("BL1-FWU: Authentication of secure image "
"from non-secure world while not in copied state\n");
return -EPERM;
}
} else {
if (desc->state != IMAGE_STATE_RESET) {
WARN("BL1-FWU: Authentication of non-secure image "
"from non-secure world while in invalid state\n");
return -EPERM;
}
}
}
if (desc->state == IMAGE_STATE_COPIED) {
/*
* Image is in COPIED state.
* Use the stored address and size.
*/
base_addr = desc->image_info.image_base;
total_size = desc->image_info.image_size;
} else {
if ((image_src == 0U) || (image_size == 0U) ||
check_uptr_overflow(image_src, image_size - 1)) {
WARN("BL1-FWU: Auth not allowed due to invalid"
" image source/size\n");
return -ENOMEM;
}
/*
* Image is in RESET state.
* Check the parameters and authenticate the source image in place.
*/
if (bl1_plat_mem_check(image_src, image_size,
desc->ep_info.h.attr) != 0) {
WARN("BL1-FWU: Authentication arguments source/size not mapped\n");
return -ENOMEM;
}
if (bl1_fwu_add_loaded_id(image_id) != 0) {
WARN("BL1-FWU: Too many images loaded at the same time.\n");
return -ENOMEM;
}
base_addr = image_src;
total_size = image_size;
/* Update the image size in the descriptor. */
desc->image_info.image_size = total_size;
}
/*
* Authenticate the image.
*/
INFO("BL1-FWU: Authenticating image_id:%d\n", image_id);
result = auth_mod_verify_img(image_id, (void *)base_addr, total_size);
if (result != 0) {
WARN("BL1-FWU: Authentication Failed err=%d\n", result);
/*
* Authentication has failed.
* Clear the memory if the image was copied.
* This is to prevent an attack where this contains
* some malicious code that can somehow be executed later.
*/
if (desc->state == IMAGE_STATE_COPIED) {
/* Clear the memory.*/
zero_normalmem((void *)base_addr, total_size);
flush_dcache_range(base_addr, total_size);
/* Indicate that image can be copied again*/
desc->state = IMAGE_STATE_RESET;
}
/*
* Even if this fails it's ok because the ID isn't in the array.
* The image cannot be in RESET state here, it is checked at the
* beginning of the function.
*/
(void)bl1_fwu_remove_loaded_id(image_id);
return -EAUTH;
}
/* Indicate that image is in authenticated state. */
desc->state = IMAGE_STATE_AUTHENTICATED;
/* Allow the platform to handle post-image load */
result = bl1_plat_handle_post_image_load(image_id);
if (result != 0) {
ERROR("BL1-FWU: Failure %d in post-image load of image id %d\n",
result, image_id);
/*
* Panic here as the platform handling of post-image load is
* not correct.
*/
plat_error_handler(result);
}
/*
* Flush image_info to memory so that other
* secure world images can see changes.
*/
flush_dcache_range((uintptr_t)&desc->image_info,
sizeof(image_info_t));
INFO("BL1-FWU: Authentication was successful\n");
return 0;
}
/*******************************************************************************
* This function is responsible for executing Secure images.
******************************************************************************/
static int bl1_fwu_image_execute(unsigned int image_id,
void **handle,
unsigned int flags)
{
/* Get the image descriptor. */
image_desc_t *desc = bl1_plat_get_image_desc(image_id);
/*
* Execution is NOT allowed if:
* image_id is invalid OR
* Caller is from Secure world OR
* Image is Non-Secure OR
* Image is Non-Executable OR
* Image is NOT in AUTHENTICATED state.
*/
if ((desc == NULL) ||
(GET_SECURITY_STATE(flags) == SECURE) ||
(GET_SECURITY_STATE(desc->ep_info.h.attr) == NON_SECURE) ||
(EP_GET_EXE(desc->ep_info.h.attr) == NON_EXECUTABLE) ||
(desc->state != IMAGE_STATE_AUTHENTICATED)) {
WARN("BL1-FWU: Execution not allowed due to invalid state/args\n");
return -EPERM;
}
INFO("BL1-FWU: Executing Secure image\n");
#ifdef __aarch64__
/* Save NS-EL1 system registers. */
cm_el1_sysregs_context_save(NON_SECURE);
#endif
/* Prepare the image for execution. */
bl1_prepare_next_image(image_id);
/* Update the secure image id. */
sec_exec_image_id = image_id;
#ifdef __aarch64__
*handle = cm_get_context(SECURE);
#else
*handle = smc_get_ctx(SECURE);
#endif
return 0;
}
/*******************************************************************************
* This function is responsible for resuming execution in the other security
* world
******************************************************************************/
static register_t bl1_fwu_image_resume(register_t image_param,
void **handle,
unsigned int flags)
{
image_desc_t *desc;
unsigned int resume_sec_state;
unsigned int caller_sec_state = GET_SECURITY_STATE(flags);
/* Get the image descriptor for last executed secure image id. */
desc = bl1_plat_get_image_desc(sec_exec_image_id);
if (caller_sec_state == NON_SECURE) {
if (desc == NULL) {
WARN("BL1-FWU: Resume not allowed due to no available"
"secure image\n");
return -EPERM;
}
} else {
/* desc must be valid for secure world callers */
assert(desc != NULL);
}
assert(GET_SECURITY_STATE(desc->ep_info.h.attr) == SECURE);
assert(EP_GET_EXE(desc->ep_info.h.attr) == EXECUTABLE);
if (caller_sec_state == SECURE) {
assert(desc->state == IMAGE_STATE_EXECUTED);
/* Update the flags. */
desc->state = IMAGE_STATE_INTERRUPTED;
resume_sec_state = NON_SECURE;
} else {
assert(desc->state == IMAGE_STATE_INTERRUPTED);
/* Update the flags. */
desc->state = IMAGE_STATE_EXECUTED;
resume_sec_state = SECURE;
}
INFO("BL1-FWU: Resuming %s world context\n",
(resume_sec_state == SECURE) ? "secure" : "normal");
#ifdef __aarch64__
/* Save the EL1 system registers of calling world. */
cm_el1_sysregs_context_save(caller_sec_state);
/* Restore the EL1 system registers of resuming world. */
cm_el1_sysregs_context_restore(resume_sec_state);
/* Update the next context. */
cm_set_next_eret_context(resume_sec_state);
*handle = cm_get_context(resume_sec_state);
#else
/* Update the next context. */
cm_set_next_context(cm_get_context(resume_sec_state));
/* Prepare the smc context for the next BL image. */
smc_set_next_ctx(resume_sec_state);
*handle = smc_get_ctx(resume_sec_state);
#endif
return image_param;
}
/*******************************************************************************
* This function is responsible for resuming normal world context.
******************************************************************************/
static int bl1_fwu_sec_image_done(void **handle, unsigned int flags)
{
image_desc_t *desc;
/* Make sure caller is from the secure world */
if (GET_SECURITY_STATE(flags) == NON_SECURE) {
WARN("BL1-FWU: Image done not allowed from normal world\n");
return -EPERM;
}
/* Get the image descriptor for last executed secure image id */
desc = bl1_plat_get_image_desc(sec_exec_image_id);
/* desc must correspond to a valid secure executing image */
assert(desc != NULL);
assert(GET_SECURITY_STATE(desc->ep_info.h.attr) == SECURE);
assert(EP_GET_EXE(desc->ep_info.h.attr) == EXECUTABLE);
assert(desc->state == IMAGE_STATE_EXECUTED);
#if ENABLE_ASSERTIONS
int rc = bl1_fwu_remove_loaded_id(sec_exec_image_id);
assert(rc == 0);
#else
bl1_fwu_remove_loaded_id(sec_exec_image_id);
#endif
/* Update the flags. */
desc->state = IMAGE_STATE_RESET;
sec_exec_image_id = INVALID_IMAGE_ID;
INFO("BL1-FWU: Resuming Normal world context\n");
#ifdef __aarch64__
/*
* Secure world is done so no need to save the context.
* Just restore the Non-Secure context.
*/
cm_el1_sysregs_context_restore(NON_SECURE);
/* Update the next context. */
cm_set_next_eret_context(NON_SECURE);
*handle = cm_get_context(NON_SECURE);
#else
/* Update the next context. */
cm_set_next_context(cm_get_context(NON_SECURE));
/* Prepare the smc context for the next BL image. */
smc_set_next_ctx(NON_SECURE);
*handle = smc_get_ctx(NON_SECURE);
#endif
return 0;
}
/*******************************************************************************
* This function provides the opportunity for users to perform any
* platform specific handling after the Firmware update is done.
******************************************************************************/
__dead2 static void bl1_fwu_done(void *client_cookie, void *reserved)
{
NOTICE("BL1-FWU: *******FWU Process Completed*******\n");
/*
* Call platform done function.
*/
bl1_plat_fwu_done(client_cookie, reserved);
assert(false);
}
/*******************************************************************************
* This function resets an image to IMAGE_STATE_RESET. It fails if the image is
* being executed.
******************************************************************************/
static int bl1_fwu_image_reset(unsigned int image_id, unsigned int flags)
{
image_desc_t *desc = bl1_plat_get_image_desc(image_id);
if ((desc == NULL) || (GET_SECURITY_STATE(flags) == SECURE)) {
WARN("BL1-FWU: Reset not allowed due to invalid args\n");
return -EPERM;
}
switch (desc->state) {
case IMAGE_STATE_RESET:
/* Nothing to do. */
break;
case IMAGE_STATE_INTERRUPTED:
case IMAGE_STATE_AUTHENTICATED:
case IMAGE_STATE_COPIED:
case IMAGE_STATE_COPYING:
if (bl1_fwu_remove_loaded_id(image_id) != 0) {
WARN("BL1-FWU: Image reset couldn't find the image ID\n");
return -EPERM;
}
if (desc->copied_size != 0U) {
/* Clear the memory if the image is copied */
assert(GET_SECURITY_STATE(desc->ep_info.h.attr)
== SECURE);
zero_normalmem((void *)desc->image_info.image_base,
desc->copied_size);
flush_dcache_range(desc->image_info.image_base,
desc->copied_size);
}
/* Reset status variables */
desc->copied_size = 0;
desc->image_info.image_size = 0;
desc->state = IMAGE_STATE_RESET;
/* Clear authentication state */
auth_img_flags[image_id] = 0;
break;
case IMAGE_STATE_EXECUTED:
default:
assert(false); /* Unreachable */
break;
}
return 0;
}

302
bl1/bl1_main.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <platform_def.h>
#include <arch.h>
#include <arch_features.h>
#include <arch_helpers.h>
#include <bl1/bl1.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/auth/auth_mod.h>
#include <drivers/auth/crypto_mod.h>
#include <drivers/console.h>
#include <lib/bootmarker_capture.h>
#include <lib/cpus/errata.h>
#include <lib/pmf/pmf.h>
#include <lib/utils.h>
#include <plat/common/platform.h>
#include <smccc_helpers.h>
#include <tools_share/uuid.h>
#include "bl1_private.h"
static void bl1_load_bl2(void);
#if ENABLE_PAUTH
uint64_t bl1_apiakey[2];
#endif
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_REGISTER_SERVICE(bl_svc, PMF_RT_INSTR_SVC_ID,
BL_TOTAL_IDS, PMF_DUMP_ENABLE)
#endif
/*******************************************************************************
* Helper utility to calculate the BL2 memory layout taking into consideration
* the BL1 RW data assuming that it is at the top of the memory layout.
******************************************************************************/
void bl1_calc_bl2_mem_layout(const meminfo_t *bl1_mem_layout,
meminfo_t *bl2_mem_layout)
{
assert(bl1_mem_layout != NULL);
assert(bl2_mem_layout != NULL);
/*
* Remove BL1 RW data from the scope of memory visible to BL2.
* This is assuming BL1 RW data is at the top of bl1_mem_layout.
*/
assert(BL1_RW_BASE > bl1_mem_layout->total_base);
bl2_mem_layout->total_base = bl1_mem_layout->total_base;
bl2_mem_layout->total_size = BL1_RW_BASE - bl1_mem_layout->total_base;
flush_dcache_range((uintptr_t)bl2_mem_layout, sizeof(meminfo_t));
}
/*******************************************************************************
* Setup function for BL1.
******************************************************************************/
void bl1_setup(void)
{
/* Perform early platform-specific setup */
bl1_early_platform_setup();
/* Perform late platform-specific setup */
bl1_plat_arch_setup();
#if CTX_INCLUDE_PAUTH_REGS
/*
* Assert that the ARMv8.3-PAuth registers are present or an access
* fault will be triggered when they are being saved or restored.
*/
assert(is_armv8_3_pauth_present());
#endif /* CTX_INCLUDE_PAUTH_REGS */
}
/*******************************************************************************
* Function to perform late architectural and platform specific initialization.
* It also queries the platform to load and run next BL image. Only called
* by the primary cpu after a cold boot.
******************************************************************************/
void bl1_main(void)
{
unsigned int image_id;
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_ENTRY, PMF_CACHE_MAINT);
#endif
/* Announce our arrival */
NOTICE(FIRMWARE_WELCOME_STR);
NOTICE("BL1: %s\n", version_string);
NOTICE("BL1: %s\n", build_message);
INFO("BL1: RAM %p - %p\n", (void *)BL1_RAM_BASE, (void *)BL1_RAM_LIMIT);
print_errata_status();
#if ENABLE_ASSERTIONS
u_register_t val;
/*
* Ensure that MMU/Caches and coherency are turned on
*/
#ifdef __aarch64__
val = read_sctlr_el3();
#else
val = read_sctlr();
#endif
assert((val & SCTLR_M_BIT) != 0);
assert((val & SCTLR_C_BIT) != 0);
assert((val & SCTLR_I_BIT) != 0);
/*
* Check that Cache Writeback Granule (CWG) in CTR_EL0 matches the
* provided platform value
*/
val = (read_ctr_el0() >> CTR_CWG_SHIFT) & CTR_CWG_MASK;
/*
* If CWG is zero, then no CWG information is available but we can
* at least check the platform value is less than the architectural
* maximum.
*/
if (val != 0)
assert(CACHE_WRITEBACK_GRANULE == SIZE_FROM_LOG2_WORDS(val));
else
assert(CACHE_WRITEBACK_GRANULE <= MAX_CACHE_LINE_SIZE);
#endif /* ENABLE_ASSERTIONS */
/* Perform remaining generic architectural setup from EL3 */
bl1_arch_setup();
crypto_mod_init();
/* Initialize authentication module */
auth_mod_init();
/* Initialize the measured boot */
bl1_plat_mboot_init();
/* Perform platform setup in BL1. */
bl1_platform_setup();
#if ENABLE_PAUTH
/* Store APIAKey_EL1 key */
bl1_apiakey[0] = read_apiakeylo_el1();
bl1_apiakey[1] = read_apiakeyhi_el1();
#endif /* ENABLE_PAUTH */
/* Get the image id of next image to load and run. */
image_id = bl1_plat_get_next_image_id();
/*
* We currently interpret any image id other than
* BL2_IMAGE_ID as the start of firmware update.
*/
if (image_id == BL2_IMAGE_ID)
bl1_load_bl2();
else
NOTICE("BL1-FWU: *******FWU Process Started*******\n");
/* Teardown the measured boot driver */
bl1_plat_mboot_finish();
bl1_prepare_next_image(image_id);
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_EXIT, PMF_CACHE_MAINT);
#endif
console_flush();
}
/*******************************************************************************
* This function locates and loads the BL2 raw binary image in the trusted SRAM.
* Called by the primary cpu after a cold boot.
* TODO: Add support for alternative image load mechanism e.g using virtio/elf
* loader etc.
******************************************************************************/
static void bl1_load_bl2(void)
{
image_desc_t *desc;
image_info_t *info;
int err;
/* Get the image descriptor */
desc = bl1_plat_get_image_desc(BL2_IMAGE_ID);
assert(desc != NULL);
/* Get the image info */
info = &desc->image_info;
INFO("BL1: Loading BL2\n");
err = bl1_plat_handle_pre_image_load(BL2_IMAGE_ID);
if (err != 0) {
ERROR("Failure in pre image load handling of BL2 (%d)\n", err);
plat_error_handler(err);
}
err = load_auth_image(BL2_IMAGE_ID, info);
if (err != 0) {
ERROR("Failed to load BL2 firmware.\n");
plat_error_handler(err);
}
/* Allow platform to handle image information. */
err = bl1_plat_handle_post_image_load(BL2_IMAGE_ID);
if (err != 0) {
ERROR("Failure in post image load handling of BL2 (%d)\n", err);
plat_error_handler(err);
}
NOTICE("BL1: Booting BL2\n");
}
/*******************************************************************************
* Function called just before handing over to the next BL to inform the user
* about the boot progress. In debug mode, also print details about the BL
* image's execution context.
******************************************************************************/
void bl1_print_next_bl_ep_info(const entry_point_info_t *bl_ep_info)
{
#ifdef __aarch64__
NOTICE("BL1: Booting BL31\n");
#else
NOTICE("BL1: Booting BL32\n");
#endif /* __aarch64__ */
print_entry_point_info(bl_ep_info);
}
#if SPIN_ON_BL1_EXIT
void print_debug_loop_message(void)
{
NOTICE("BL1: Debug loop, spinning forever\n");
NOTICE("BL1: Please connect the debugger to continue\n");
}
#endif
/*******************************************************************************
* Top level handler for servicing BL1 SMCs.
******************************************************************************/
u_register_t bl1_smc_handler(unsigned int smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
unsigned int flags)
{
/* BL1 Service UUID */
DEFINE_SVC_UUID2(bl1_svc_uid,
U(0xd46739fd), 0xcb72, 0x9a4d, 0xb5, 0x75,
0x67, 0x15, 0xd6, 0xf4, 0xbb, 0x4a);
#if TRUSTED_BOARD_BOOT
/*
* Dispatch FWU calls to FWU SMC handler and return its return
* value
*/
if (is_fwu_fid(smc_fid)) {
return bl1_fwu_smc_handler(smc_fid, x1, x2, x3, x4, cookie,
handle, flags);
}
#endif
switch (smc_fid) {
case BL1_SMC_CALL_COUNT:
SMC_RET1(handle, BL1_NUM_SMC_CALLS);
case BL1_SMC_UID:
SMC_UUID_RET(handle, bl1_svc_uid);
case BL1_SMC_VERSION:
SMC_RET1(handle, BL1_SMC_MAJOR_VER | BL1_SMC_MINOR_VER);
default:
WARN("Unimplemented BL1 SMC Call: 0x%x\n", smc_fid);
SMC_RET1(handle, SMC_UNK);
}
}
/*******************************************************************************
* BL1 SMC wrapper. This function is only used in AArch32 mode to ensure ABI
* compliance when invoking bl1_smc_handler.
******************************************************************************/
u_register_t bl1_smc_wrapper(uint32_t smc_fid,
void *cookie,
void *handle,
unsigned int flags)
{
u_register_t x1, x2, x3, x4;
assert(handle != NULL);
get_smc_params_from_ctx(handle, x1, x2, x3, x4);
return bl1_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags);
}

33
bl1/bl1_private.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef BL1_PRIVATE_H
#define BL1_PRIVATE_H
#include <stdint.h>
#include <common/bl_common.h>
extern entry_point_info_t *bl2_ep_info;
/******************************************
* Function prototypes
*****************************************/
void bl1_arch_setup(void);
void bl1_prepare_next_image(unsigned int image_id);
void bl1_run_bl2_in_root(void);
u_register_t bl1_fwu_smc_handler(unsigned int smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
unsigned int flags);
#endif /* BL1_PRIVATE_H */

65
bl1/tbbr/tbbr_img_desc.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <bl1/tbbr/tbbr_img_desc.h>
#include <common/bl_common.h>
image_desc_t bl1_tbbr_image_descs[] = {
{
.image_id = FWU_CERT_ID,
SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY,
VERSION_1, image_info_t, 0),
.image_info.image_base = BL2_BASE,
.image_info.image_max_size = BL2_LIMIT - BL2_BASE,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY,
VERSION_1, entry_point_info_t, SECURE),
},
#if NS_BL1U_BASE
{
.image_id = NS_BL1U_IMAGE_ID,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
VERSION_1, entry_point_info_t, NON_SECURE | EXECUTABLE),
.ep_info.pc = NS_BL1U_BASE,
},
#endif
#if SCP_BL2U_BASE
{
.image_id = SCP_BL2U_IMAGE_ID,
SET_STATIC_PARAM_HEAD(image_info, PARAM_IMAGE_BINARY,
VERSION_1, image_info_t, 0),
.image_info.image_base = SCP_BL2U_BASE,
.image_info.image_max_size = SCP_BL2U_LIMIT - SCP_BL2U_BASE,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_IMAGE_BINARY,
VERSION_1, entry_point_info_t, SECURE),
},
#endif
#if BL2U_BASE
{
.image_id = BL2U_IMAGE_ID,
SET_STATIC_PARAM_HEAD(image_info, PARAM_EP,
VERSION_1, image_info_t, 0),
.image_info.image_base = BL2U_BASE,
.image_info.image_max_size = BL2U_LIMIT - BL2U_BASE,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
VERSION_1, entry_point_info_t, SECURE | EXECUTABLE),
.ep_info.pc = BL2U_BASE,
},
#endif
#if NS_BL2U_BASE
{
.image_id = NS_BL2U_IMAGE_ID,
SET_STATIC_PARAM_HEAD(ep_info, PARAM_EP,
VERSION_1, entry_point_info_t, NON_SECURE),
},
#endif
BL2_IMAGE_DESC,
{
.image_id = INVALID_IMAGE_ID,
}
};

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "../bl2_private.h"
/*******************************************************************************
* Place holder function to perform any Secure SVC specific architectural
* setup. At the moment there is nothing to do.
******************************************************************************/
void bl2_arch_setup(void)
{
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
#include <el3_common_macros.S>
.globl bl2_entrypoint
func bl2_entrypoint
/* Save arguments x0-x3 from previous Boot loader */
mov r9, r0
mov r10, r1
mov r11, r2
mov r12, r3
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=bl2_vector_table \
_pie_fixup_size=0
/*
* Restore parameters of boot rom
*/
mov r0, r9
mov r1, r10
mov r2, r11
mov r3, r12
/* ---------------------------------------------
* Perform BL2 setup
* ---------------------------------------------
*/
bl bl2_el3_setup
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2_vector_table
vector_base bl2_vector_table
b bl2_entrypoint
b report_exception /* Undef */
b report_exception /* SVC call */
b report_prefetch_abort /* Prefetch abort */
b report_data_abort /* Data abort */
b report_exception /* Reserved */
b report_exception /* IRQ */
b report_exception /* FIQ */

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2_vector_table
.globl bl2_entrypoint
vector_base bl2_vector_table
b bl2_entrypoint
b report_exception /* Undef */
b report_exception /* SVC call */
b report_prefetch_abort /* Prefetch abort */
b report_data_abort /* Data abort */
b report_exception /* Reserved */
b report_exception /* IRQ */
b report_exception /* FIQ */
func bl2_entrypoint
/*---------------------------------------------
* Save arguments x0 - x3 from BL1 for future
* use.
* ---------------------------------------------
*/
mov r9, r0
mov r10, r1
mov r11, r2
mov r12, r3
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
ldr r0, =bl2_vector_table
stcopr r0, VBAR
isb
/* --------------------------------------------------------
* Enable the instruction cache - disable speculative loads
* --------------------------------------------------------
*/
ldcopr r0, SCTLR
orr r0, r0, #SCTLR_I_BIT
bic r0, r0, #SCTLR_DSSBS_BIT
stcopr r0, SCTLR
isb
/* ---------------------------------------------
* Since BL2 executes after BL1, it is assumed
* here that BL1 has already has done the
* necessary register initializations.
* ---------------------------------------------
*/
/* ---------------------------------------------
* Invalidate the RW memory used by the BL2
* image. This includes the data and NOBITS
* sections. This is done to safeguard against
* possible corruption of this memory by dirty
* cache lines in a system cache as a result of
* use by an earlier boot loader stage.
* ---------------------------------------------
*/
ldr r0, =__RW_START__
ldr r1, =__RW_END__
sub r1, r1, r0
bl inv_dcache_range
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
ldr r0, =__BSS_START__
ldr r1, =__BSS_END__
sub r1, r1, r0
bl zeromem
#if USE_COHERENT_MEM
ldr r0, =__COHERENT_RAM_START__
ldr r1, =__COHERENT_RAM_END_UNALIGNED__
sub r1, r1, r0
bl zeromem
#endif
/* --------------------------------------------
* Allocate a stack whose memory will be marked
* as Normal-IS-WBWA when the MMU is enabled.
* There is no risk of reading stale stack
* memory after enabling the MMU as only the
* primary cpu is running at the moment.
* --------------------------------------------
*/
bl plat_set_my_stack
/* ---------------------------------------------
* Initialize the stack protector canary before
* any C code is called.
* ---------------------------------------------
*/
#if STACK_PROTECTOR_ENABLED
bl update_stack_protector_canary
#endif
/* ---------------------------------------------
* Perform BL2 setup
* ---------------------------------------------
*/
mov r0, r9
mov r1, r10
mov r2, r11
mov r3, r12
bl bl2_setup
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2_run_next_image
func bl2_run_next_image
mov r8,r0
/*
* MMU needs to be disabled because both BL2 and BL32 execute
* in PL1, and therefore share the same address space.
* BL32 will initialize the address space according to its
* own requirement.
*/
bl disable_mmu_icache_secure
stcopr r0, TLBIALL
dsb sy
isb
mov r0, r8
bl bl2_el3_plat_prepare_exit
/*
* Extract PC and SPSR based on struct `entry_point_info_t`
* and load it in LR and SPSR registers respectively.
*/
ldr lr, [r8, #ENTRY_POINT_INFO_PC_OFFSET]
ldr r1, [r8, #(ENTRY_POINT_INFO_PC_OFFSET + 4)]
msr spsr_xc, r1
/* Some BL32 stages expect lr_svc to provide the BL33 entry address */
cps #MODE32_svc
ldr lr, [r8, #ENTRY_POINT_INFO_LR_SVC_OFFSET]
cps #MODE32_mon
add r8, r8, #ENTRY_POINT_INFO_ARGS_OFFSET
ldm r8, {r0, r1, r2, r3}
exception_return
endfunc bl2_run_next_image

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include "../bl2_private.h"
/*******************************************************************************
* Place holder function to perform any S-EL1 specific architectural setup. At
* the moment there is nothing to do.
******************************************************************************/
void bl2_arch_setup(void)
{
/* Give access to FP/SIMD registers */
write_cpacr(CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE));
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2017-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
#include <el3_common_macros.S>
.globl bl2_entrypoint
#if BL2_IN_XIP_MEM
#define FIXUP_SIZE 0
#else
#define FIXUP_SIZE ((BL2_LIMIT) - (BL2_BASE))
#endif
func bl2_entrypoint
/* Save arguments x0-x3 from previous Boot loader */
mov x20, x0
mov x21, x1
mov x22, x2
mov x23, x3
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=bl2_el3_exceptions \
_pie_fixup_size=FIXUP_SIZE
/* ---------------------------------------------
* Restore parameters of boot rom
* ---------------------------------------------
*/
mov x0, x20
mov x1, x21
mov x2, x22
mov x3, x23
/* ---------------------------------------------
* Perform BL2 setup
* ---------------------------------------------
*/
bl bl2_el3_setup
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1 and enable pointer authentication.
* ---------------------------------------------
*/
bl pauth_init_enable_el3
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <bl1/bl1.h>
#include <common/bl_common.h>
#include <context.h>
/* -----------------------------------------------------------------------------
* Very simple stackless exception handlers used by BL2.
* -----------------------------------------------------------------------------
*/
.globl bl2_el3_exceptions
vector_base bl2_el3_exceptions
/* -----------------------------------------------------
* Current EL with SP0 : 0x0 - 0x200
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSP0
mov x0, #SYNC_EXCEPTION_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSP0
vector_entry IrqSP0
mov x0, #IRQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSP0
vector_entry FiqSP0
mov x0, #FIQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSP0
vector_entry SErrorSP0
mov x0, #SERROR_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSP0
/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x400
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSPx
mov x0, #SYNC_EXCEPTION_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSPx
vector_entry IrqSPx
mov x0, #IRQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSPx
vector_entry FiqSPx
mov x0, #FIQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSPx
vector_entry SErrorSPx
mov x0, #SERROR_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSPx
/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA64
mov x0, #SYNC_EXCEPTION_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionA64
vector_entry IrqA64
mov x0, #IRQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA64
vector_entry FiqA64
mov x0, #FIQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA64
vector_entry SErrorA64
mov x0, #SERROR_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA64
/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA32
mov x0, #SYNC_EXCEPTION_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionA32
vector_entry IrqA32
mov x0, #IRQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA32
vector_entry FiqA32
mov x0, #FIQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA32
vector_entry SErrorA32
mov x0, #SERROR_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA32

View File

@ -0,0 +1,141 @@
/*
* Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2_entrypoint
func bl2_entrypoint
/*---------------------------------------------
* Save arguments x0 - x3 from BL1 for future
* use.
* ---------------------------------------------
*/
mov x20, x0
mov x21, x1
mov x22, x2
mov x23, x3
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, early_exceptions
msr vbar_el1, x0
isb
/* ---------------------------------------------
* Enable the SError interrupt now that the
* exception vectors have been setup.
* ---------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks and disable
* speculative loads.
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
bic x0, x0, #SCTLR_DSSBS_BIT
msr sctlr_el1, x0
isb
/* ---------------------------------------------
* Invalidate the RW memory used by the BL2
* image. This includes the data and NOBITS
* sections. This is done to safeguard against
* possible corruption of this memory by dirty
* cache lines in a system cache as a result of
* use by an earlier boot loader stage.
* ---------------------------------------------
*/
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
adrp x0, __BSS_START__
add x0, x0, :lo12:__BSS_START__
adrp x1, __BSS_END__
add x1, x1, :lo12:__BSS_END__
sub x1, x1, x0
bl zeromem
#if USE_COHERENT_MEM
adrp x0, __COHERENT_RAM_START__
add x0, x0, :lo12:__COHERENT_RAM_START__
adrp x1, __COHERENT_RAM_END_UNALIGNED__
add x1, x1, :lo12:__COHERENT_RAM_END_UNALIGNED__
sub x1, x1, x0
bl zeromem
#endif
/* --------------------------------------------
* Allocate a stack whose memory will be marked
* as Normal-IS-WBWA when the MMU is enabled.
* There is no risk of reading stale stack
* memory after enabling the MMU as only the
* primary cpu is running at the moment.
* --------------------------------------------
*/
bl plat_set_my_stack
/* ---------------------------------------------
* Initialize the stack protector canary before
* any C code is called.
* ---------------------------------------------
*/
#if STACK_PROTECTOR_ENABLED
bl update_stack_protector_canary
#endif
/* ---------------------------------------------
* Perform BL2 setup
* ---------------------------------------------
*/
mov x0, x20
mov x1, x21
mov x2, x22
mov x3, x23
bl bl2_setup
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1
* and enable pointer authentication.
* ---------------------------------------------
*/
bl pauth_init_enable_el1
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
#include <el3_common_macros.S>
.globl bl2_entrypoint
func bl2_entrypoint
/* Save arguments x0-x3 from previous Boot loader */
mov x20, x0
mov x21, x1
mov x22, x2
mov x23, x3
el3_entrypoint_common \
_init_sctlr=0 \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=1 \
_exception_vectors=bl2_el3_exceptions \
_pie_fixup_size=0
/* ---------------------------------------------
* Restore parameters of boot rom
* ---------------------------------------------
*/
mov x0, x20
mov x1, x21
mov x2, x22
mov x3, x23
/* ---------------------------------------------
* Perform BL2 setup
* ---------------------------------------------
*/
bl bl2_setup
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1 and enable pointer authentication.
* ---------------------------------------------
*/
bl pauth_init_enable_el3
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2_run_next_image
func bl2_run_next_image
mov x20,x0
/* ---------------------------------------------
* MMU needs to be disabled because both BL2 and BL31 execute
* in EL3, and therefore share the same address space.
* BL31 will initialize the address space according to its
* own requirement.
* ---------------------------------------------
*/
bl disable_mmu_icache_el3
tlbi alle3
bl bl2_el3_plat_prepare_exit
ldp x0, x1, [x20, #ENTRY_POINT_INFO_PC_OFFSET]
msr elr_el3, x0
msr spsr_el3, x1
ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)]
ldp x4, x5, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x20)]
ldp x2, x3, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x10)]
ldp x0, x1, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x0)]
exception_return
endfunc bl2_run_next_image

133
bl2/bl2.ld.S Normal file
View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl2_entrypoint)
MEMORY {
RAM (rwx): ORIGIN = BL2_BASE, LENGTH = BL2_LIMIT - BL2_BASE
}
SECTIONS {
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
. = BL2_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL2_BASE address is not aligned on a page boundary.")
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
#if ENABLE_RME
*bl2_rme_entrypoint.o(.text*)
#else /* ENABLE_RME */
*bl2_entrypoint.o(.text*)
#endif /* ENABLE_RME */
*(SORT_BY_ALIGNMENT(.text*))
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >RAM
/* .ARM.extab and .ARM.exidx are only added because Clang needs them */
.ARM.extab . : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >RAM
.ARM.exidx . : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} >RAM
.rodata . : {
__RODATA_START__ = .;
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
__RODATA_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__RODATA_END__ = .;
} >RAM
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
*bl2_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as read-only,
* executable. No RW data from the next section must creep in. Ensure
* that the rest of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__RO_END__ = .;
} >RAM
#endif /* SEPARATE_CODE_AND_RODATA */
__RW_START__ = .;
DATA_SECTION >RAM
STACK_SECTION >RAM
BSS_SECTION >RAM
XLAT_TABLE_SECTION >RAM
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure the rest of
* the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL2_END__ = .;
RAM_REGION_END = .;
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
ASSERT(. <= BL2_LIMIT, "BL2 image has exceeded its limit.")
}

55
bl2/bl2.mk Normal file
View File

@ -0,0 +1,55 @@
#
# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
BL2_SOURCES += bl2/bl2_image_load_v2.c \
bl2/bl2_main.c \
bl2/${ARCH}/bl2_arch_setup.c \
lib/locks/exclusive/${ARCH}/spinlock.S \
plat/common/${ARCH}/platform_up_stack.S \
${MBEDTLS_SOURCES}
ifeq (${ARCH},aarch64)
BL2_SOURCES += common/aarch64/early_exceptions.S
endif
ifneq ($(findstring gcc,$(notdir $(LD))),)
BL2_LDFLAGS += -Wl,--sort-section=alignment
else ifneq ($(findstring ld,$(notdir $(LD))),)
BL2_LDFLAGS += --sort-section=alignment
endif
ifeq (${ENABLE_RME},1)
# Using RME, run BL2 at EL3
include lib/gpt_rme/gpt_rme.mk
BL2_SOURCES += bl2/${ARCH}/bl2_rme_entrypoint.S \
bl2/${ARCH}/bl2_el3_exceptions.S \
bl2/${ARCH}/bl2_run_next_image.S \
${GPT_LIB_SRCS}
BL2_DEFAULT_LINKER_SCRIPT_SOURCE := bl2/bl2.ld.S
else ifeq (${RESET_TO_BL2},0)
# Normal operation, no RME, no BL2 at EL3
BL2_SOURCES += bl2/${ARCH}/bl2_entrypoint.S
BL2_DEFAULT_LINKER_SCRIPT_SOURCE := bl2/bl2.ld.S
else
# BL2 at EL3, no RME
BL2_SOURCES += bl2/${ARCH}/bl2_el3_entrypoint.S \
bl2/${ARCH}/bl2_el3_exceptions.S \
bl2/${ARCH}/bl2_run_next_image.S \
lib/cpus/${ARCH}/cpu_helpers.S
ifeq (${ARCH},aarch64)
BL2_SOURCES += lib/cpus/aarch64/dsu_helpers.S
endif
BL2_DEFAULT_LINKER_SCRIPT_SOURCE := bl2/bl2_el3.ld.S
endif
ifeq (${ENABLE_PMF},1)
BL2_SOURCES += lib/pmf/pmf_main.c
endif

221
bl2/bl2_el3.ld.S Normal file
View File

@ -0,0 +1,221 @@
/*
* Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl2_entrypoint)
MEMORY {
#if BL2_IN_XIP_MEM
ROM (rx): ORIGIN = BL2_RO_BASE, LENGTH = BL2_RO_LIMIT - BL2_RO_BASE
RAM (rwx): ORIGIN = BL2_RW_BASE, LENGTH = BL2_RW_LIMIT - BL2_RW_BASE
#else /* BL2_IN_XIP_MEM */
RAM (rwx): ORIGIN = BL2_BASE, LENGTH = BL2_LIMIT - BL2_BASE
#endif /* BL2_IN_XIP_MEM */
#if SEPARATE_BL2_NOLOAD_REGION
RAM_NOLOAD (rw!a): ORIGIN = BL2_NOLOAD_START, LENGTH = BL2_NOLOAD_LIMIT - BL2_NOLOAD_START
#else /* SEPARATE_BL2_NOLOAD_REGION */
# define RAM_NOLOAD RAM
#endif /* SEPARATE_BL2_NOLOAD_REGION */
}
#if !BL2_IN_XIP_MEM
# define ROM RAM
#endif /* !BL2_IN_XIP_MEM */
SECTIONS {
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
#if BL2_IN_XIP_MEM
ROM_REGION_START = ORIGIN(ROM);
ROM_REGION_LENGTH = LENGTH(ROM);
. = BL2_RO_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL2_RO_BASE address is not aligned on a page boundary.")
#else /* BL2_IN_XIP_MEM */
. = BL2_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL2_BASE address is not aligned on a page boundary.")
#endif /* BL2_IN_XIP_MEM */
#if SEPARATE_BL2_NOLOAD_REGION
RAM_NOLOAD_REGION_START = ORIGIN(RAM_NOLOAD);
RAM_NOLOAD_REGION_LENGTH = LENGTH(RAM_NOLOAD);
#endif
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
__TEXT_RESIDENT_START__ = .;
*bl2_el3_entrypoint.o(.text*)
*(.text.asm.*)
__TEXT_RESIDENT_END__ = .;
*(SORT_BY_ALIGNMENT(.text*))
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >ROM
.rodata . : {
__RODATA_START__ = .;
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
__RODATA_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__RODATA_END__ = .;
} >ROM
ASSERT(__TEXT_RESIDENT_END__ - __TEXT_RESIDENT_START__ <= PAGE_SIZE,
"Resident part of BL2 has exceeded its limit.")
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
__TEXT_RESIDENT_START__ = .;
*bl2_el3_entrypoint.o(.text*)
*(.text.asm.*)
__TEXT_RESIDENT_END__ = .;
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as read-only,
* executable. No RW data from the next section must creep in. Ensure
* that the rest of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__RO_END__ = .;
} >ROM
#endif /* SEPARATE_CODE_AND_RODATA */
ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__,
"cpu_ops not defined for this platform.")
#if BL2_IN_XIP_MEM
ROM_REGION_END = .;
. = BL2_RW_BASE;
ASSERT(BL2_RW_BASE == ALIGN(PAGE_SIZE),
"BL2_RW_BASE address is not aligned on a page boundary.")
#endif /* BL2_IN_XIP_MEM */
__RW_START__ = .;
DATA_SECTION >RAM AT>ROM
__DATA_RAM_START__ = __DATA_START__;
__DATA_RAM_END__ = __DATA_END__;
RELA_SECTION >RAM
#if SEPARATE_BL2_NOLOAD_REGION
SAVED_ADDR = .;
. = BL2_NOLOAD_START;
__BL2_NOLOAD_START__ = .;
#endif /* SEPARATE_BL2_NOLOAD_REGION */
STACK_SECTION >RAM_NOLOAD
BSS_SECTION >RAM_NOLOAD
XLAT_TABLE_SECTION >RAM_NOLOAD
#if SEPARATE_BL2_NOLOAD_REGION
__BL2_NOLOAD_END__ = .;
RAM_NOLOAD_REGION_END = .;
. = SAVED_ADDR;
#endif /* SEPARATE_BL2_NOLOAD_REGION */
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure the rest of
* the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL2_END__ = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
#if BL2_IN_XIP_MEM
__BL2_RAM_START__ = ADDR(.data);
__BL2_RAM_END__ = .;
__DATA_ROM_START__ = LOADADDR(.data);
__DATA_SIZE__ = SIZEOF(.data);
/*
* The .data section is the last PROGBITS section so its end marks the end
* of BL2's RO content in XIP memory.
*/
__BL2_ROM_END__ = __DATA_ROM_START__ + __DATA_SIZE__;
ASSERT(__BL2_ROM_END__ <= BL2_RO_LIMIT,
"BL2's RO content has exceeded its limit.")
#endif /* BL2_IN_XIP_MEM */
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
RAM_REGION_END = .;
#if BL2_IN_XIP_MEM
ASSERT(. <= BL2_RW_LIMIT, "BL2's RW content has exceeded its limit.")
#else /* BL2_IN_XIP_MEM */
ASSERT(. <= BL2_LIMIT, "BL2 image has exceeded its limit.")
#endif /* BL2_IN_XIP_MEM */
}

110
bl2/bl2_image_load_v2.c Normal file
View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdint.h>
#include <arch.h>
#include <arch_helpers.h>
#include "bl2_private.h"
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/desc_image_load.h>
#include <drivers/auth/auth_mod.h>
#include <plat/common/platform.h>
#include <platform_def.h>
/*******************************************************************************
* This function loads SCP_BL2/BL3x images and returns the ep_info for
* the next executable image.
******************************************************************************/
struct entry_point_info *bl2_load_images(void)
{
bl_params_t *bl2_to_next_bl_params;
bl_load_info_t *bl2_load_info;
const bl_load_info_node_t *bl2_node_info;
int plat_setup_done = 0;
int err;
/*
* Get information about the images to load.
*/
bl2_load_info = plat_get_bl_image_load_info();
assert(bl2_load_info != NULL);
assert(bl2_load_info->head != NULL);
assert(bl2_load_info->h.type == PARAM_BL_LOAD_INFO);
assert(bl2_load_info->h.version >= VERSION_2);
bl2_node_info = bl2_load_info->head;
while (bl2_node_info != NULL) {
/*
* Perform platform setup before loading the image,
* if indicated in the image attributes AND if NOT
* already done before.
*/
if ((bl2_node_info->image_info->h.attr &
IMAGE_ATTRIB_PLAT_SETUP) != 0U) {
if (plat_setup_done != 0) {
WARN("BL2: Platform setup already done!!\n");
} else {
INFO("BL2: Doing platform setup\n");
bl2_platform_setup();
plat_setup_done = 1;
}
}
err = bl2_plat_handle_pre_image_load(bl2_node_info->image_id);
if (err != 0) {
ERROR("BL2: Failure in pre image load handling (%i)\n", err);
plat_error_handler(err);
}
if ((bl2_node_info->image_info->h.attr &
IMAGE_ATTRIB_SKIP_LOADING) == 0U) {
INFO("BL2: Loading image id %u\n", bl2_node_info->image_id);
err = load_auth_image(bl2_node_info->image_id,
bl2_node_info->image_info);
if (err != 0) {
ERROR("BL2: Failed to load image id %u (%i)\n",
bl2_node_info->image_id, err);
plat_error_handler(err);
}
} else {
INFO("BL2: Skip loading image id %u\n", bl2_node_info->image_id);
}
/* Allow platform to handle image information. */
err = bl2_plat_handle_post_image_load(bl2_node_info->image_id);
if (err != 0) {
ERROR("BL2: Failure in post image load handling (%i)\n", err);
plat_error_handler(err);
}
/* Go to next image */
bl2_node_info = bl2_node_info->next_load_info;
}
/*
* Get information to pass to the next image.
*/
bl2_to_next_bl_params = plat_get_next_bl_params();
assert(bl2_to_next_bl_params != NULL);
assert(bl2_to_next_bl_params->head != NULL);
assert(bl2_to_next_bl_params->h.type == PARAM_BL_PARAMS);
assert(bl2_to_next_bl_params->h.version >= VERSION_2);
assert(bl2_to_next_bl_params->head->ep_info != NULL);
/* Populate arg0 for the next BL image if not already provided */
if (bl2_to_next_bl_params->head->ep_info->args.arg0 == (u_register_t)0)
bl2_to_next_bl_params->head->ep_info->args.arg0 =
(u_register_t)bl2_to_next_bl_params;
/* Flush the parameters to be passed to next image */
plat_flush_next_bl_params();
return bl2_to_next_bl_params->head->ep_info;
}

169
bl2/bl2_main.c Normal file
View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <arch_features.h>
#include <bl1/bl1.h>
#include <bl2/bl2.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/auth/auth_mod.h>
#include <drivers/auth/crypto_mod.h>
#include <drivers/console.h>
#include <drivers/fwu/fwu.h>
#include <lib/bootmarker_capture.h>
#include <lib/extensions/pauth.h>
#include <lib/pmf/pmf.h>
#include <plat/common/platform.h>
#include "bl2_private.h"
#ifdef __aarch64__
#define NEXT_IMAGE "BL31"
#else
#define NEXT_IMAGE "BL32"
#endif
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_REGISTER_SERVICE(bl_svc, PMF_RT_INSTR_SVC_ID,
BL_TOTAL_IDS, PMF_DUMP_ENABLE);
#endif
#if RESET_TO_BL2
/*******************************************************************************
* Setup function for BL2 when RESET_TO_BL2=1
******************************************************************************/
void bl2_el3_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
/* Perform early platform-specific setup */
bl2_el3_early_platform_setup(arg0, arg1, arg2, arg3);
/* Perform late platform-specific setup */
bl2_el3_plat_arch_setup();
#if CTX_INCLUDE_PAUTH_REGS
/*
* Assert that the ARMv8.3-PAuth registers are present or an access
* fault will be triggered when they are being saved or restored.
*/
assert(is_armv8_3_pauth_present());
#endif /* CTX_INCLUDE_PAUTH_REGS */
}
#else /* RESET_TO_BL2 */
/*******************************************************************************
* Setup function for BL2 when RESET_TO_BL2=0
******************************************************************************/
void bl2_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
/* Perform early platform-specific setup */
bl2_early_platform_setup2(arg0, arg1, arg2, arg3);
/* Perform late platform-specific setup */
bl2_plat_arch_setup();
#if CTX_INCLUDE_PAUTH_REGS
/*
* Assert that the ARMv8.3-PAuth registers are present or an access
* fault will be triggered when they are being saved or restored.
*/
assert(is_armv8_3_pauth_present());
#endif /* CTX_INCLUDE_PAUTH_REGS */
}
#endif /* RESET_TO_BL2 */
/*******************************************************************************
* The only thing to do in BL2 is to load further images and pass control to
* next BL. The memory occupied by BL2 will be reclaimed by BL3x stages. BL2
* runs entirely in S-EL1.
******************************************************************************/
void bl2_main(void)
{
entry_point_info_t *next_bl_ep_info;
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_ENTRY, PMF_CACHE_MAINT);
#endif
NOTICE("BL2: %s\n", version_string);
NOTICE("BL2: %s\n", build_message);
/* Perform remaining generic architectural setup in S-EL1 */
bl2_arch_setup();
#if PSA_FWU_SUPPORT
fwu_init();
#endif /* PSA_FWU_SUPPORT */
crypto_mod_init();
/* Initialize authentication module */
auth_mod_init();
/* Initialize the Measured Boot backend */
bl2_plat_mboot_init();
/* Initialize boot source */
bl2_plat_preload_setup();
/* Load the subsequent bootloader images. */
next_bl_ep_info = bl2_load_images();
/* Teardown the Measured Boot backend */
bl2_plat_mboot_finish();
#if !BL2_RUNS_AT_EL3
#ifndef __aarch64__
/*
* For AArch32 state BL1 and BL2 share the MMU setup.
* Given that BL2 does not map BL1 regions, MMU needs
* to be disabled in order to go back to BL1.
*/
disable_mmu_icache_secure();
#endif /* !__aarch64__ */
#if ENABLE_PAUTH
/*
* Disable pointer authentication before running next boot image
*/
pauth_disable_el1();
#endif /* ENABLE_PAUTH */
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_EXIT, PMF_CACHE_MAINT);
#endif
console_flush();
/*
* Run next BL image via an SMC to BL1. Information on how to pass
* control to the BL32 (if present) and BL33 software images will
* be passed to next BL image as an argument.
*/
smc(BL1_SMC_RUN_IMAGE, (unsigned long)next_bl_ep_info, 0, 0, 0, 0, 0, 0);
#else /* if BL2_RUNS_AT_EL3 */
NOTICE("BL2: Booting " NEXT_IMAGE "\n");
print_entry_point_info(next_bl_ep_info);
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_EXIT, PMF_CACHE_MAINT);
#endif
console_flush();
#if ENABLE_PAUTH
/*
* Disable pointer authentication before running next boot image
*/
pauth_disable_el3();
#endif /* ENABLE_PAUTH */
bl2_run_next_image(next_bl_ep_info);
#endif /* BL2_RUNS_AT_EL3 */
}

24
bl2/bl2_private.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef BL2_PRIVATE_H
#define BL2_PRIVATE_H
#include <common/bl_common.h>
/******************************************
* Forward declarations
*****************************************/
struct entry_point_info;
/******************************************
* Function prototypes
*****************************************/
void bl2_arch_setup(void);
struct entry_point_info *bl2_load_images(void);
void bl2_run_next_image(const struct entry_point_info *bl_ep_info);
#endif /* BL2_PRIVATE_H */

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2u_vector_table
.globl bl2u_entrypoint
vector_base bl2u_vector_table
b bl2u_entrypoint
b report_exception /* Undef */
b report_exception /* SVC call */
b report_prefetch_abort /* Prefetch abort */
b report_data_abort /* Data abort */
b report_exception /* Reserved */
b report_exception /* IRQ */
b report_exception /* FIQ */
func bl2u_entrypoint
/*---------------------------------------------
* Save from r1 the extents of the trusted ram
* available to BL2U for future use.
* r0 is not currently used.
* ---------------------------------------------
*/
mov r11, r1
mov r10, r2
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
ldr r0, =bl2u_vector_table
stcopr r0, VBAR
isb
/* --------------------------------------------------------
* Enable the instruction cache - disable speculative loads
* --------------------------------------------------------
*/
ldcopr r0, SCTLR
orr r0, r0, #SCTLR_I_BIT
bic r0, r0, #SCTLR_DSSBS_BIT
stcopr r0, SCTLR
isb
/* ---------------------------------------------
* Since BL2U executes after BL1, it is assumed
* here that BL1 has already has done the
* necessary register initializations.
* ---------------------------------------------
*/
/* ---------------------------------------------
* Invalidate the RW memory used by the BL2U
* image. This includes the data and NOBITS
* sections. This is done to safeguard against
* possible corruption of this memory by dirty
* cache lines in a system cache as a result of
* use by an earlier boot loader stage.
* ---------------------------------------------
*/
ldr r0, =__RW_START__
ldr r1, =__RW_END__
sub r1, r1, r0
bl inv_dcache_range
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
ldr r0, =__BSS_START__
ldr r1, =__BSS_END__
sub r1, r1, r0
bl zeromem
/* --------------------------------------------
* Allocate a stack whose memory will be marked
* as Normal-IS-WBWA when the MMU is enabled.
* There is no risk of reading stale stack
* memory after enabling the MMU as only the
* primary cpu is running at the moment.
* --------------------------------------------
*/
bl plat_set_my_stack
/* ---------------------------------------------
* Initialize the stack protector canary before
* any C code is called.
* ---------------------------------------------
*/
#if STACK_PROTECTOR_ENABLED
bl update_stack_protector_canary
#endif
/* ---------------------------------------------
* Perform early platform setup & platform
* specific early arch. setup e.g. mmu setup
* ---------------------------------------------
*/
mov r0, r11
mov r1, r10
bl bl2u_early_platform_setup
bl bl2u_plat_arch_setup
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2u_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2u_entrypoint

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
.globl bl2u_entrypoint
func bl2u_entrypoint
/*---------------------------------------------
* Store the extents of the tzram available to
* BL2U and other platform specific information
* for future use. x0 is currently not used.
* ---------------------------------------------
*/
mov x20, x1
mov x21, x2
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, early_exceptions
msr vbar_el1, x0
isb
/* ---------------------------------------------
* Enable the SError interrupt now that the
* exception vectors have been setup.
* ---------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks and disable
* speculative loads.
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
bic x0, x0, #SCTLR_DSSBS_BIT
msr sctlr_el1, x0
isb
/* ---------------------------------------------
* Invalidate the RW memory used by the BL2U
* image. This includes the data and NOBITS
* sections. This is done to safeguard against
* possible corruption of this memory by dirty
* cache lines in a system cache as a result of
* use by an earlier boot loader stage.
* ---------------------------------------------
*/
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
adrp x0, __BSS_START__
add x0, x0, :lo12:__BSS_START__
adrp x1, __BSS_END__
add x1, x1, :lo12:__BSS_END__
sub x1, x1, x0
bl zeromem
/* --------------------------------------------
* Allocate a stack whose memory will be marked
* as Normal-IS-WBWA when the MMU is enabled.
* There is no risk of reading stale stack
* memory after enabling the MMU as only the
* primary cpu is running at the moment.
* --------------------------------------------
*/
bl plat_set_my_stack
/* ---------------------------------------------
* Initialize the stack protector canary before
* any C code is called.
* ---------------------------------------------
*/
#if STACK_PROTECTOR_ENABLED
bl update_stack_protector_canary
#endif
/* ---------------------------------------------
* Perform early platform setup & platform
* specific early arch. setup e.g. mmu setup
* ---------------------------------------------
*/
mov x0, x20
mov x1, x21
bl bl2u_early_platform_setup
bl bl2u_plat_arch_setup
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1
* and enable pointer authentication.
* ---------------------------------------------
*/
bl pauth_init_enable_el1
#endif
/* ---------------------------------------------
* Jump to bl2u_main function.
* ---------------------------------------------
*/
bl bl2u_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2u_entrypoint

123
bl2u/bl2u.ld.S Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl2u_entrypoint)
MEMORY {
RAM (rwx): ORIGIN = BL2U_BASE, LENGTH = BL2U_LIMIT - BL2U_BASE
}
SECTIONS {
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
. = BL2U_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL2U_BASE address is not aligned on a page boundary.")
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
*bl2u_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >RAM
/* .ARM.extab and .ARM.exidx are only added because Clang needs them */
.ARM.extab . : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >RAM
.ARM.exidx . : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} >RAM
.rodata . : {
__RODATA_START__ = .;
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
__RODATA_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__RODATA_END__ = .;
} >RAM
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
*bl2u_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as read-only,
* executable. No RW data from the next section must creep in. Ensure
* that the rest of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__RO_END__ = .;
} >RAM
#endif /* SEPARATE_CODE_AND_RODATA */
__RW_START__ = .;
DATA_SECTION >RAM
STACK_SECTION >RAM
BSS_SECTION >RAM
XLAT_TABLE_SECTION >RAM
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure the rest of
* the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL2U_END__ = .;
__BSS_SIZE__ = SIZEOF(.bss);
ASSERT(. <= BL2U_LIMIT, "BL2U image has exceeded its limit.")
RAM_REGION_END = .;
}

21
bl2u/bl2u.mk Normal file
View File

@ -0,0 +1,21 @@
#
# Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
BL2U_SOURCES += bl2u/bl2u_main.c \
bl2u/${ARCH}/bl2u_entrypoint.S \
plat/common/${ARCH}/platform_up_stack.S
ifeq (${ARCH},aarch64)
BL2U_SOURCES += common/aarch64/early_exceptions.S
endif
BL2U_DEFAULT_LINKER_SCRIPT_SOURCE := bl2u/bl2u.ld.S
ifneq ($(findstring gcc,$(notdir $(LD))),)
BL2U_LDFLAGS += -Wl,--sort-section=alignment
else ifneq ($(findstring ld,$(notdir $(LD))),)
BL2U_LDFLAGS += --sort-section=alignment
endif

65
bl2u/bl2u_main.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdint.h>
#include <platform_def.h>
#include <arch.h>
#include <arch_helpers.h>
#include <bl1/bl1.h>
#include <bl2u/bl2u.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/auth/auth_mod.h>
#include <drivers/console.h>
#include <plat/common/platform.h>
/*******************************************************************************
* This function is responsible to:
* Load SCP_BL2U if platform has defined SCP_BL2U_BASE
* Perform platform setup.
* Go back to EL3.
******************************************************************************/
void bl2u_main(void)
{
NOTICE("BL2U: %s\n", version_string);
NOTICE("BL2U: %s\n", build_message);
#if SCP_BL2U_BASE
int rc;
/* Load the subsequent bootloader images */
rc = bl2u_plat_handle_scp_bl2u();
if (rc != 0) {
ERROR("Failed to load SCP_BL2U (%i)\n", rc);
panic();
}
#endif
/* Perform platform setup in BL2U after loading SCP_BL2U */
bl2u_platform_setup();
console_flush();
#ifndef __aarch64__
/*
* For AArch32 state BL1 and BL2U share the MMU setup.
* Given that BL2U does not map BL1 regions, MMU needs
* to be disabled in order to go back to BL1.
*/
disable_mmu_icache_secure();
#endif /* !__aarch64__ */
/*
* Indicate that BL2U is done and resume back to
* normal world via an SMC to BL1.
* x1 could be passed to Normal world,
* so DO NOT pass any secret information.
*/
smc(FWU_SMC_SEC_IMAGE_DONE, 0, 0, 0, 0, 0, 0, 0);
wfi();
}

View File

@ -0,0 +1,229 @@
/*
* Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <arch.h>
#include <common/bl_common.h>
#include <el3_common_macros.S>
#include <lib/pmf/aarch64/pmf_asm_macros.S>
#include <lib/runtime_instr.h>
#include <lib/xlat_tables/xlat_mmu_helpers.h>
.globl bl31_entrypoint
.globl bl31_warm_entrypoint
/* -----------------------------------------------------
* bl31_entrypoint() is the cold boot entrypoint,
* executed only by the primary cpu.
* -----------------------------------------------------
*/
func bl31_entrypoint
/* ---------------------------------------------------------------
* Stash the previous bootloader arguments x0 - x3 for later use.
* ---------------------------------------------------------------
*/
mov x20, x0
mov x21, x1
mov x22, x2
mov x23, x3
#if !RESET_TO_BL31
/* ---------------------------------------------------------------------
* For !RESET_TO_BL31 systems, only the primary CPU ever reaches
* bl31_entrypoint() during the cold boot flow, so the cold/warm boot
* and primary/secondary CPU logic should not be executed in this case.
*
* Also, assume that the previous bootloader has already initialised the
* SCTLR_EL3, including the endianness, and has initialised the memory.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=0 \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=1 \
_exception_vectors=runtime_exceptions \
_pie_fixup_size=BL31_LIMIT - BL31_BASE
#else
/* ---------------------------------------------------------------------
* For RESET_TO_BL31 systems which have a programmable reset address,
* bl31_entrypoint() is executed only on the cold boot path so we can
* skip the warm boot mailbox mechanism.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=runtime_exceptions \
_pie_fixup_size=BL31_LIMIT - BL31_BASE
#endif /* RESET_TO_BL31 */
/* --------------------------------------------------------------------
* Perform BL31 setup
* --------------------------------------------------------------------
*/
mov x0, x20
mov x1, x21
mov x2, x22
mov x3, x23
bl bl31_setup
#if ENABLE_PAUTH
/* --------------------------------------------------------------------
* Program APIAKey_EL1 and enable pointer authentication
* --------------------------------------------------------------------
*/
bl pauth_init_enable_el3
#endif /* ENABLE_PAUTH */
/* --------------------------------------------------------------------
* Jump to main function
* --------------------------------------------------------------------
*/
bl bl31_main
/* --------------------------------------------------------------------
* Clean the .data & .bss sections to main memory. This ensures
* that any global data which was initialised by the primary CPU
* is visible to secondary CPUs before they enable their data
* caches and participate in coherency.
* --------------------------------------------------------------------
*/
adrp x0, __DATA_START__
add x0, x0, :lo12:__DATA_START__
adrp x1, __DATA_END__
add x1, x1, :lo12:__DATA_END__
sub x1, x1, x0
bl clean_dcache_range
adrp x0, __BSS_START__
add x0, x0, :lo12:__BSS_START__
adrp x1, __BSS_END__
add x1, x1, :lo12:__BSS_END__
sub x1, x1, x0
bl clean_dcache_range
b el3_exit
endfunc bl31_entrypoint
/* --------------------------------------------------------------------
* This CPU has been physically powered up. It is either resuming from
* suspend or has simply been turned on. In both cases, call the BL31
* warmboot entrypoint
* --------------------------------------------------------------------
*/
func bl31_warm_entrypoint
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* This timestamp update happens with cache off. The next
* timestamp collection will need to do cache maintenance prior
* to timestamp update.
*/
pmf_calc_timestamp_addr rt_instr_svc, RT_INSTR_EXIT_HW_LOW_PWR
mrs x1, cntpct_el0
str x1, [x0]
#endif
/*
* On the warm boot path, most of the EL3 initialisations performed by
* 'el3_entrypoint_common' must be skipped:
*
* - Only when the platform bypasses the BL1/BL31 entrypoint by
* programming the reset address do we need to initialise SCTLR_EL3.
* In other cases, we assume this has been taken care by the
* entrypoint code.
*
* - No need to determine the type of boot, we know it is a warm boot.
*
* - Do not try to distinguish between primary and secondary CPUs, this
* notion only exists for a cold boot.
*
* - No need to initialise the memory or the C runtime environment,
* it has been done once and for all on the cold boot path.
*/
el3_entrypoint_common \
_init_sctlr=PROGRAMMABLE_RESET_ADDRESS \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=0 \
_exception_vectors=runtime_exceptions \
_pie_fixup_size=0
/*
* We're about to enable MMU and participate in PSCI state coordination.
*
* The PSCI implementation invokes platform routines that enable CPUs to
* participate in coherency. On a system where CPUs are not
* cache-coherent without appropriate platform specific programming,
* having caches enabled until such time might lead to coherency issues
* (resulting from stale data getting speculatively fetched, among
* others). Therefore we keep data caches disabled even after enabling
* the MMU for such platforms.
*
* On systems with hardware-assisted coherency, or on single cluster
* platforms, such platform specific programming is not required to
* enter coherency (as CPUs already are); and there's no reason to have
* caches disabled either.
*/
#if HW_ASSISTED_COHERENCY || WARMBOOT_ENABLE_DCACHE_EARLY
mov x0, xzr
#else
mov x0, #DISABLE_DCACHE
#endif
bl bl31_plat_enable_mmu
#if ENABLE_RME
/*
* At warm boot GPT data structures have already been initialized in RAM
* but the sysregs for this CPU need to be initialized. Note that the GPT
* accesses are controlled attributes in GPCCR and do not depend on the
* SCR_EL3.C bit.
*/
bl gpt_enable
cbz x0, 1f
no_ret plat_panic_handler
1:
#endif
#if ENABLE_PAUTH
/* --------------------------------------------------------------------
* Program APIAKey_EL1 and enable pointer authentication
* --------------------------------------------------------------------
*/
bl pauth_init_enable_el3
#endif /* ENABLE_PAUTH */
bl psci_warmboot_entrypoint
#if ENABLE_RUNTIME_INSTRUMENTATION
pmf_calc_timestamp_addr rt_instr_svc, RT_INSTR_EXIT_PSCI
mov x19, x0
/*
* Invalidate before updating timestamp to ensure previous timestamp
* updates on the same cache line with caches disabled are properly
* seen by the same core. Without the cache invalidate, the core might
* write into a stale cache line.
*/
mov x1, #PMF_TS_SIZE
mov x20, x30
bl inv_dcache_range
mov x30, x20
mrs x0, cntpct_el0
str x0, [x19]
#endif
b el3_exit
endfunc bl31_warm_entrypoint

View File

@ -0,0 +1,469 @@
/*
* Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <plat_macros.S>
#include <platform_def.h>
#include <arch.h>
#include <asm_macros.S>
#include <context.h>
#include <lib/el3_runtime/cpu_data.h>
#include <lib/utils_def.h>
.globl report_unhandled_exception
.globl report_unhandled_interrupt
.globl report_el3_panic
.globl report_elx_panic
#if CRASH_REPORTING
/* ------------------------------------------------------
* The below section deals with dumping the system state
* when an unhandled exception is taken in EL3.
* The layout and the names of the registers which will
* be dumped during a unhandled exception is given below.
* ------------------------------------------------------
*/
.section .rodata.crash_prints, "aS"
print_spacer:
.asciz " = 0x"
gp_regs:
.asciz "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",\
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",\
"x16", "x17", "x18", "x19", "x20", "x21", "x22",\
"x23", "x24", "x25", "x26", "x27", "x28", "x29", ""
el3_sys_regs:
.asciz "scr_el3", "sctlr_el3", "cptr_el3", "tcr_el3",\
"daif", "mair_el3", "spsr_el3", "elr_el3", "ttbr0_el3",\
"esr_el3", "far_el3", ""
non_el3_sys_regs:
.asciz "spsr_el1", "elr_el1", "spsr_abt", "spsr_und",\
"spsr_irq", "spsr_fiq", "sctlr_el1", "actlr_el1", "cpacr_el1",\
"csselr_el1", "sp_el1", "esr_el1", "ttbr0_el1", "ttbr1_el1",\
"mair_el1", "amair_el1", "tcr_el1", "tpidr_el1", "tpidr_el0",\
"tpidrro_el0", "par_el1", "mpidr_el1", "afsr0_el1", "afsr1_el1",\
"contextidr_el1", "vbar_el1", "cntp_ctl_el0", "cntp_cval_el0",\
"cntv_ctl_el0", "cntv_cval_el0", "cntkctl_el1", "sp_el0", "isr_el1", ""
#if CTX_INCLUDE_AARCH32_REGS
aarch32_regs:
.asciz "dacr32_el2", "ifsr32_el2", ""
#endif /* CTX_INCLUDE_AARCH32_REGS */
panic_msg:
.asciz "PANIC in EL3.\nx30"
excpt_msg:
.asciz "Unhandled Exception in EL3.\nx30"
intr_excpt_msg:
.ascii "Unhandled Interrupt Exception in EL3.\n"
x30_msg:
.asciz "x30"
excpt_msg_el:
.asciz "Unhandled Exception from lower EL.\n"
/*
* Helper function to print from crash buf.
* The print loop is controlled by the buf size and
* ascii reg name list which is passed in x6. The
* function returns the crash buf address in x0.
* Clobbers : x0 - x7, sp
*/
func size_controlled_print
/* Save the lr */
mov sp, x30
/* load the crash buf address */
mrs x7, tpidr_el3
test_size_list:
/* Calculate x5 always as it will be clobbered by asm_print_hex */
mrs x5, tpidr_el3
add x5, x5, #CPU_DATA_CRASH_BUF_SIZE
/* Test whether we have reached end of crash buf */
cmp x7, x5
b.eq exit_size_print
ldrb w4, [x6]
/* Test whether we are at end of list */
cbz w4, exit_size_print
mov x4, x6
/* asm_print_str updates x4 to point to next entry in list */
bl asm_print_str
/* x0 = number of symbols printed + 1 */
sub x0, x4, x6
/* update x6 with the updated list pointer */
mov x6, x4
bl print_alignment
ldr x4, [x7], #REGSZ
bl asm_print_hex
bl asm_print_newline
b test_size_list
exit_size_print:
mov x30, sp
ret
endfunc size_controlled_print
/* -----------------------------------------------------
* This function calculates and prints required number
* of space characters followed by "= 0x", based on the
* length of ascii register name.
* x0: length of ascii register name + 1
* ------------------------------------------------------
*/
func print_alignment
/* The minimum ascii length is 3, e.g. for "x0" */
adr x4, print_spacer - 3
add x4, x4, x0
b asm_print_str
endfunc print_alignment
/*
* Helper function to store x8 - x15 registers to
* the crash buf. The system registers values are
* copied to x8 to x15 by the caller which are then
* copied to the crash buf by this function.
* x0 points to the crash buf. It then calls
* size_controlled_print to print to console.
* Clobbers : x0 - x7, sp
*/
func str_in_crash_buf_print
/* restore the crash buf address in x0 */
mrs x0, tpidr_el3
stp x8, x9, [x0]
stp x10, x11, [x0, #REGSZ * 2]
stp x12, x13, [x0, #REGSZ * 4]
stp x14, x15, [x0, #REGSZ * 6]
b size_controlled_print
endfunc str_in_crash_buf_print
/* ------------------------------------------------------
* This macro calculates the offset to crash buf from
* cpu_data and stores it in tpidr_el3. It also saves x0
* and x1 in the crash buf by using sp as a temporary
* register.
* ------------------------------------------------------
*/
.macro prepare_crash_buf_save_x0_x1
/* we can corrupt this reg to free up x0 */
mov sp, x0
/* tpidr_el3 contains the address to cpu_data structure */
mrs x0, tpidr_el3
/* Calculate the Crash buffer offset in cpu_data */
add x0, x0, #CPU_DATA_CRASH_BUF_OFFSET
/* Store crash buffer address in tpidr_el3 */
msr tpidr_el3, x0
str x1, [x0, #REGSZ]
mov x1, sp
str x1, [x0]
.endm
/* -----------------------------------------------------
* This function allows to report a crash (if crash
* reporting is enabled) when an unhandled exception
* occurs. It prints the CPU state via the crash console
* making use of the crash buf. This function will
* not return.
* -----------------------------------------------------
*/
func report_unhandled_exception
prepare_crash_buf_save_x0_x1
adr x0, excpt_msg
mov sp, x0
/* This call will not return */
b do_crash_reporting
endfunc report_unhandled_exception
/* -----------------------------------------------------
* This function allows to report a crash (if crash
* reporting is enabled) when an unhandled interrupt
* occurs. It prints the CPU state via the crash console
* making use of the crash buf. This function will
* not return.
* -----------------------------------------------------
*/
func report_unhandled_interrupt
prepare_crash_buf_save_x0_x1
adr x0, intr_excpt_msg
mov sp, x0
/* This call will not return */
b do_crash_reporting
endfunc report_unhandled_interrupt
/* -----------------------------------------------------
* This function allows to report a crash from the lower
* exception level (if crash reporting is enabled) when
* lower_el_panic() is invoked from C Runtime.
* It prints the CPU state via the crash console making
* use of 'cpu_context' structure where general purpose
* registers are saved and the crash buf.
* This function will not return.
* -----------------------------------------------------
*/
func report_elx_panic
msr spsel, #MODE_SP_ELX
/* Print the crash message */
adr x4, excpt_msg_el
bl asm_print_str
/* Report x0 - x29 values stored in 'gpregs_ctx' structure */
/* Store the ascii list pointer in x6 */
adr x6, gp_regs
add x7, sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0
print_next:
ldrb w4, [x6]
/* Test whether we are at end of list */
cbz w4, print_x30
mov x4, x6
/* asm_print_str updates x4 to point to next entry in list */
bl asm_print_str
/* x0 = number of symbols printed + 1 */
sub x0, x4, x6
/* Update x6 with the updated list pointer */
mov x6, x4
bl print_alignment
ldr x4, [x7], #REGSZ
bl asm_print_hex
bl asm_print_newline
b print_next
print_x30:
adr x4, x30_msg
bl asm_print_str
/* Print spaces to align "x30" string */
mov x0, #4
bl print_alignment
/* Report x30 */
ldr x4, [x7]
/* ----------------------------------------------------------------
* Different virtual address space size can be defined for each EL.
* Ensure that we use the proper one by reading the corresponding
* TCR_ELx register.
* ----------------------------------------------------------------
*/
cmp x8, #MODE_EL2
b.lt from_el1 /* EL1 */
mrs x2, sctlr_el2
mrs x1, tcr_el2
/* ----------------------------------------------------------------
* Check if pointer authentication is enabled at the specified EL.
* If it isn't, we can then skip stripping a PAC code.
* ----------------------------------------------------------------
*/
test_pauth:
tst x2, #(SCTLR_EnIA_BIT | SCTLR_EnIB_BIT)
b.eq no_pauth
/* Demangle address */
and x1, x1, #0x3F /* T0SZ = TCR_ELx[5:0] */
sub x1, x1, #64
neg x1, x1 /* bottom_pac_bit = 64 - T0SZ */
mov x2, #-1
lsl x2, x2, x1
bic x4, x4, x2
no_pauth:
bl asm_print_hex
bl asm_print_newline
/* tpidr_el3 contains the address to cpu_data structure */
mrs x0, tpidr_el3
/* Calculate the Crash buffer offset in cpu_data */
add x0, x0, #CPU_DATA_CRASH_BUF_OFFSET
/* Store crash buffer address in tpidr_el3 */
msr tpidr_el3, x0
/* Print the rest of crash dump */
b print_el3_sys_regs
from_el1:
mrs x2, sctlr_el1
mrs x1, tcr_el1
b test_pauth
endfunc report_elx_panic
/* -----------------------------------------------------
* This function allows to report a crash (if crash
* reporting is enabled) when panic() is invoked from
* C Runtime. It prints the CPU state via the crash
* console making use of the crash buf. This function
* will not return.
* -----------------------------------------------------
*/
func report_el3_panic
msr spsel, #MODE_SP_ELX
prepare_crash_buf_save_x0_x1
adr x0, panic_msg
mov sp, x0
/* Fall through to 'do_crash_reporting' */
/* ------------------------------------------------------------
* The common crash reporting functionality. It requires x0
* and x1 has already been stored in crash buf, sp points to
* crash message and tpidr_el3 contains the crash buf address.
* The function does the following:
* - Retrieve the crash buffer from tpidr_el3
* - Store x2 to x6 in the crash buffer
* - Initialise the crash console.
* - Print the crash message by using the address in sp.
* - Print x30 value to the crash console.
* - Print x0 - x7 from the crash buf to the crash console.
* - Print x8 - x29 (in groups of 8 registers) using the
* crash buf to the crash console.
* - Print el3 sys regs (in groups of 8 registers) using the
* crash buf to the crash console.
* - Print non el3 sys regs (in groups of 8 registers) using
* the crash buf to the crash console.
* ------------------------------------------------------------
*/
do_crash_reporting:
/* Retrieve the crash buf from tpidr_el3 */
mrs x0, tpidr_el3
/* Store x2 - x6, x30 in the crash buffer */
stp x2, x3, [x0, #REGSZ * 2]
stp x4, x5, [x0, #REGSZ * 4]
stp x6, x30, [x0, #REGSZ * 6]
/* Initialize the crash console */
bl plat_crash_console_init
/* Verify the console is initialized */
cbz x0, crash_panic
/* Print the crash message. sp points to the crash message */
mov x4, sp
bl asm_print_str
/* Print spaces to align "x30" string */
mov x0, #4
bl print_alignment
/* Load the crash buf address */
mrs x0, tpidr_el3
/* Report x30 first from the crash buf */
ldr x4, [x0, #REGSZ * 7]
#if ENABLE_PAUTH
/* Demangle address */
xpaci x4
#endif
bl asm_print_hex
bl asm_print_newline
/* Load the crash buf address */
mrs x0, tpidr_el3
/* Now mov x7 into crash buf */
str x7, [x0, #REGSZ * 7]
/* Report x0 - x29 values stored in crash buf */
/* Store the ascii list pointer in x6 */
adr x6, gp_regs
/* Print x0 to x7 from the crash buf */
bl size_controlled_print
/* Store x8 - x15 in crash buf and print */
bl str_in_crash_buf_print
/* Load the crash buf address */
mrs x0, tpidr_el3
/* Store the rest of gp regs and print */
stp x16, x17, [x0]
stp x18, x19, [x0, #REGSZ * 2]
stp x20, x21, [x0, #REGSZ * 4]
stp x22, x23, [x0, #REGSZ * 6]
bl size_controlled_print
/* Load the crash buf address */
mrs x0, tpidr_el3
stp x24, x25, [x0]
stp x26, x27, [x0, #REGSZ * 2]
stp x28, x29, [x0, #REGSZ * 4]
bl size_controlled_print
/* Print the el3 sys registers */
print_el3_sys_regs:
adr x6, el3_sys_regs
mrs x8, scr_el3
mrs x9, sctlr_el3
mrs x10, cptr_el3
mrs x11, tcr_el3
mrs x12, daif
mrs x13, mair_el3
mrs x14, spsr_el3
mrs x15, elr_el3
bl str_in_crash_buf_print
mrs x8, ttbr0_el3
mrs x9, esr_el3
mrs x10, far_el3
bl str_in_crash_buf_print
/* Print the non el3 sys registers */
adr x6, non_el3_sys_regs
mrs x8, spsr_el1
mrs x9, elr_el1
mrs x10, spsr_abt
mrs x11, spsr_und
mrs x12, spsr_irq
mrs x13, spsr_fiq
mrs x14, sctlr_el1
mrs x15, actlr_el1
bl str_in_crash_buf_print
mrs x8, cpacr_el1
mrs x9, csselr_el1
mrs x10, sp_el1
mrs x11, esr_el1
mrs x12, ttbr0_el1
mrs x13, ttbr1_el1
mrs x14, mair_el1
mrs x15, amair_el1
bl str_in_crash_buf_print
mrs x8, tcr_el1
mrs x9, tpidr_el1
mrs x10, tpidr_el0
mrs x11, tpidrro_el0
mrs x12, par_el1
mrs x13, mpidr_el1
mrs x14, afsr0_el1
mrs x15, afsr1_el1
bl str_in_crash_buf_print
mrs x8, contextidr_el1
mrs x9, vbar_el1
mrs x10, cntp_ctl_el0
mrs x11, cntp_cval_el0
mrs x12, cntv_ctl_el0
mrs x13, cntv_cval_el0
mrs x14, cntkctl_el1
mrs x15, sp_el0
bl str_in_crash_buf_print
mrs x8, isr_el1
bl str_in_crash_buf_print
#if CTX_INCLUDE_AARCH32_REGS
/* Print the AArch32 registers */
adr x6, aarch32_regs
mrs x8, dacr32_el2
mrs x9, ifsr32_el2
bl str_in_crash_buf_print
#endif /* CTX_INCLUDE_AARCH32_REGS */
/* Get the cpu specific registers to report */
bl do_cpu_reg_dump
bl str_in_crash_buf_print
/* Print some platform registers */
plat_crash_print_regs
bl plat_crash_console_flush
/* Done reporting */
no_ret plat_panic_handler
endfunc report_el3_panic
#else /* CRASH_REPORTING */
func report_unhandled_exception
report_unhandled_interrupt:
no_ret plat_panic_handler
endfunc report_unhandled_exception
#endif /* CRASH_REPORTING */
func crash_panic
no_ret plat_panic_handler
endfunc crash_panic

305
bl31/aarch64/ea_delegate.S Normal file
View File

@ -0,0 +1,305 @@
/*
* Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2022, NVIDIA Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert_macros.S>
#include <asm_macros.S>
#include <assert_macros.S>
#include <bl31/ea_handle.h>
#include <context.h>
#include <lib/extensions/ras_arch.h>
#include <cpu_macros.S>
#include <context.h>
.globl handle_lower_el_ea_esb
.globl handle_lower_el_sync_ea
.globl handle_lower_el_async_ea
/*
* Function to delegate External Aborts synchronized by ESB instruction at EL3
* vector entry. This function assumes GP registers x0-x29 have been saved, and
* are available for use. It delegates the handling of the EA to platform
* handler, and returns only upon successfully handling the EA; otherwise
* panics. On return from this function, the original exception handler is
* expected to resume.
*/
func handle_lower_el_ea_esb
mov x0, #ERROR_EA_ESB
mrs x1, DISR_EL1
b ea_proceed
endfunc handle_lower_el_ea_esb
/*
* This function forms the tail end of Synchronous Exception entry from lower
* EL, and expects to handle Synchronous External Aborts from lower EL and CPU
* Implementation Defined Exceptions. If any other kind of exception is detected,
* then this function reports unhandled exception.
*
* It delegates the handling of the EA to platform handler, and upon successfully
* handling the EA, exits EL3; otherwise panics.
*
* This function assumes x30 has been saved.
*/
func handle_lower_el_sync_ea
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Check for I/D aborts from lower EL */
cmp x30, #EC_IABORT_LOWER_EL
b.eq 1f
cmp x30, #EC_DABORT_LOWER_EL
b.eq 1f
/* Save GP registers */
stp x0, x1, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
stp x2, x3, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X2]
stp x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]
/* Get the cpu_ops pointer */
bl get_cpu_ops_ptr
/* Get the cpu_ops exception handler */
ldr x0, [x0, #CPU_E_HANDLER_FUNC]
/*
* If the reserved function pointer is NULL, this CPU does not have an
* implementation defined exception handler function
*/
cbz x0, 2f
mrs x1, esr_el3
ubfx x1, x1, #ESR_EC_SHIFT, #ESR_EC_LENGTH
blr x0
b 2f
1:
/*
* Save general purpose and ARMv8.3-PAuth registers (if enabled).
* Also save PMCR_EL0 and set the PSTATE to a known state.
*/
bl prepare_el3_entry
#if ENABLE_PAUTH
/* Load and program APIAKey firmware key */
bl pauth_load_bl31_apiakey
#endif
/* Setup exception class and syndrome arguments for platform handler */
mov x0, #ERROR_EA_SYNC
mrs x1, esr_el3
bl delegate_sync_ea
/* el3_exit assumes SP_EL0 on entry */
msr spsel, #MODE_SP_EL0
b el3_exit
2:
ldp x0, x1, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
ldp x2, x3, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X2]
ldp x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]
/* Synchronous exceptions other than the above are assumed to be EA */
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
no_ret report_unhandled_exception
endfunc handle_lower_el_sync_ea
/*
* This function handles SErrors from lower ELs.
*
* It delegates the handling of the EA to platform handler, and upon successfully
* handling the EA, exits EL3; otherwise panics.
*
* This function assumes x30 has been saved.
*/
func handle_lower_el_async_ea
/*
* Save general purpose and ARMv8.3-PAuth registers (if enabled).
* Also save PMCR_EL0 and set the PSTATE to a known state.
*/
bl prepare_el3_entry
#if ENABLE_PAUTH
/* Load and program APIAKey firmware key */
bl pauth_load_bl31_apiakey
#endif
/* Setup exception class and syndrome arguments for platform handler */
mov x0, #ERROR_EA_ASYNC
mrs x1, esr_el3
bl delegate_async_ea
/* el3_exit assumes SP_EL0 on entry */
msr spsel, #MODE_SP_EL0
b el3_exit
endfunc handle_lower_el_async_ea
/*
* Prelude for Synchronous External Abort handling. This function assumes that
* all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func delegate_sync_ea
#if RAS_FFH_SUPPORT
/*
* Check for Uncontainable error type. If so, route to the platform
* fatal error handler rather than the generic EA one.
*/
ubfx x2, x1, #EABORT_SET_SHIFT, #EABORT_SET_WIDTH
cmp x2, #ERROR_STATUS_SET_UC
b.ne 1f
/* Check fault status code */
ubfx x3, x1, #EABORT_DFSC_SHIFT, #EABORT_DFSC_WIDTH
cmp x3, #SYNC_EA_FSC
b.ne 1f
no_ret plat_handle_uncontainable_ea
1:
#endif
b ea_proceed
endfunc delegate_sync_ea
/*
* Prelude for Asynchronous External Abort handling. This function assumes that
* all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func delegate_async_ea
#if RAS_FFH_SUPPORT
/* Check Exception Class to ensure SError, as this function should
* only be invoked for SError. If that is not the case, which implies
* either an HW error or programming error, panic.
*/
ubfx x2, x1, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x2, EC_SERROR
b.ne el3_panic
/*
* Check for Implementation Defined Syndrome. If so, skip checking
* Uncontainable error type from the syndrome as the format is unknown.
*/
tbnz x1, #SERROR_IDS_BIT, 1f
/* AET only valid when DFSC is 0x11 */
ubfx x2, x1, #EABORT_DFSC_SHIFT, #EABORT_DFSC_WIDTH
cmp x2, #DFSC_SERROR
b.ne 1f
/*
* Check for Uncontainable error type. If so, route to the platform
* fatal error handler rather than the generic EA one.
*/
ubfx x3, x1, #EABORT_AET_SHIFT, #EABORT_AET_WIDTH
cmp x3, #ERROR_STATUS_UET_UC
b.ne 1f
no_ret plat_handle_uncontainable_ea
1:
#endif
b ea_proceed
endfunc delegate_async_ea
/*
* Delegate External Abort handling to platform's EA handler. This function
* assumes that all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func ea_proceed
/*
* If the ESR loaded earlier is not zero, we were processing an EA
* already, and this is a double fault.
*/
ldr x5, [sp, #CTX_EL3STATE_OFFSET + CTX_ESR_EL3]
cbz x5, 1f
no_ret plat_handle_double_fault
1:
/* Save EL3 state */
mrs x2, spsr_el3
mrs x3, elr_el3
stp x2, x3, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
/*
* Save ESR as handling might involve lower ELs, and returning back to
* EL3 from there would trample the original ESR.
*/
mrs x4, scr_el3
mrs x5, esr_el3
stp x4, x5, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
/*
* Setup rest of arguments, and call platform External Abort handler.
*
* x0: EA reason (already in place)
* x1: Exception syndrome (already in place).
* x2: Cookie (unused for now).
* x3: Context pointer.
* x4: Flags (security state from SCR for now).
*/
mov x2, xzr
mov x3, sp
ubfx x4, x4, #0, #1
/* Switch to runtime stack */
ldr x5, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
msr spsel, #MODE_SP_EL0
mov sp, x5
mov x29, x30
#if ENABLE_ASSERTIONS
/* Stash the stack pointer */
mov x28, sp
#endif
bl plat_ea_handler
#if ENABLE_ASSERTIONS
/*
* Error handling flows might involve long jumps; so upon returning from
* the platform error handler, validate that the we've completely
* unwound the stack.
*/
mov x27, sp
cmp x28, x27
ASM_ASSERT(eq)
#endif
/* Make SP point to context */
msr spsel, #MODE_SP_ELX
/* Restore EL3 state and ESR */
ldp x1, x2, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
msr spsr_el3, x1
msr elr_el3, x2
/* Restore ESR_EL3 and SCR_EL3 */
ldp x3, x4, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
msr scr_el3, x3
msr esr_el3, x4
#if ENABLE_ASSERTIONS
cmp x4, xzr
ASM_ASSERT(ne)
#endif
/* Clear ESR storage */
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_ESR_EL3]
ret x29
endfunc ea_proceed

View File

@ -0,0 +1,645 @@
/*
* Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <arch.h>
#include <asm_macros.S>
#include <bl31/ea_handle.h>
#include <bl31/interrupt_mgmt.h>
#include <bl31/sync_handle.h>
#include <common/runtime_svc.h>
#include <context.h>
#include <el3_common_macros.S>
#include <lib/el3_runtime/cpu_data.h>
#include <lib/smccc.h>
.globl runtime_exceptions
.globl sync_exception_sp_el0
.globl irq_sp_el0
.globl fiq_sp_el0
.globl serror_sp_el0
.globl sync_exception_sp_elx
.globl irq_sp_elx
.globl fiq_sp_elx
.globl serror_sp_elx
.globl sync_exception_aarch64
.globl irq_aarch64
.globl fiq_aarch64
.globl serror_aarch64
.globl sync_exception_aarch32
.globl irq_aarch32
.globl fiq_aarch32
.globl serror_aarch32
/*
* Save LR and make x30 available as most of the routines in vector entry
* need a free register
*/
.macro save_x30
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
.endm
/*
* Macro that prepares entry to EL3 upon taking an exception.
*
* With RAS_FFH_SUPPORT, this macro synchronizes pending errors with an
* ESB instruction. When an error is thus synchronized, the handling is
* delegated to platform EA handler.
*
* Without RAS_FFH_SUPPORT, this macro synchronizes pending errors using
* a DSB, unmasks Asynchronous External Aborts and saves X30 before
* setting the flag CTX_IS_IN_EL3.
*/
.macro check_and_unmask_ea
#if RAS_FFH_SUPPORT
/* Synchronize pending External Aborts */
esb
/* Unmask the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
/* Check for SErrors synchronized by the ESB instruction */
mrs x30, DISR_EL1
tbz x30, #DISR_A_BIT, 1f
/*
* Save general purpose and ARMv8.3-PAuth registers (if enabled).
* Also save PMCR_EL0 and set the PSTATE to a known state.
*/
bl prepare_el3_entry
bl handle_lower_el_ea_esb
/* Restore general purpose, PMCR_EL0 and ARMv8.3-PAuth registers */
bl restore_gp_pmcr_pauth_regs
1:
#else
/*
* Note 1: The explicit DSB at the entry of various exception vectors
* for handling exceptions from lower ELs can inadvertently trigger an
* SError exception in EL3 due to pending asynchronous aborts in lower
* ELs. This will end up being handled by serror_sp_elx which will
* ultimately panic and die.
* The way to workaround is to update a flag to indicate if the exception
* truly came from EL3. This flag is allocated in the cpu_context
* structure and located at offset "CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3"
* This is not a bullet proof solution to the problem at hand because
* we assume the instructions following "isb" that help to update the
* flag execute without causing further exceptions.
*/
/*
* For SoCs which do not implement RAS, use DSB as a barrier to
* synchronize pending external aborts.
*/
dsb sy
/* Unmask the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
/* Use ISB for the above unmask operation to take effect immediately */
isb
/* Refer Note 1. */
mov x30, #1
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]
dmb sy
#endif
.endm
/* ---------------------------------------------------------------------
* This macro handles Synchronous exceptions.
* Only SMC exceptions are supported.
* ---------------------------------------------------------------------
*/
.macro handle_sync_exception
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Read the timestamp value and store it in per-cpu data. The value
* will be extracted from per-cpu data by the C level SMC handler and
* saved to the PMF timestamp region.
*/
mrs x30, cntpct_el0
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
mrs x29, tpidr_el3
str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Handle SMC exceptions separately from other synchronous exceptions */
cmp x30, #EC_AARCH32_SMC
b.eq smc_handler32
cmp x30, #EC_AARCH64_SMC
b.eq sync_handler64
cmp x30, #EC_AARCH64_SYS
b.eq sync_handler64
/* Synchronous exceptions other than the above are assumed to be EA */
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
b handle_lower_el_sync_ea
.endm
vector_base runtime_exceptions
/* ---------------------------------------------------------------------
* Current EL with SP_EL0 : 0x0 - 0x200
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_sp_el0
#ifdef MONITOR_TRAPS
stp x29, x30, [sp, #-16]!
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Check for BRK */
cmp x30, #EC_BRK
b.eq brk_handler
ldp x29, x30, [sp], #16
#endif /* MONITOR_TRAPS */
/* We don't expect any synchronous exceptions from EL3 */
b report_unhandled_exception
end_vector_entry sync_exception_sp_el0
vector_entry irq_sp_el0
/*
* EL3 code is non-reentrant. Any asynchronous exception is a serious
* error. Loop infinitely.
*/
b report_unhandled_interrupt
end_vector_entry irq_sp_el0
vector_entry fiq_sp_el0
b report_unhandled_interrupt
end_vector_entry fiq_sp_el0
vector_entry serror_sp_el0
no_ret plat_handle_el3_ea
end_vector_entry serror_sp_el0
/* ---------------------------------------------------------------------
* Current EL with SP_ELx: 0x200 - 0x400
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_sp_elx
/*
* This exception will trigger if anything went wrong during a previous
* exception entry or exit or while handling an earlier unexpected
* synchronous exception. There is a high probability that SP_EL3 is
* corrupted.
*/
b report_unhandled_exception
end_vector_entry sync_exception_sp_elx
vector_entry irq_sp_elx
b report_unhandled_interrupt
end_vector_entry irq_sp_elx
vector_entry fiq_sp_elx
b report_unhandled_interrupt
end_vector_entry fiq_sp_elx
vector_entry serror_sp_elx
#if !RAS_FFH_SUPPORT
/*
* This will trigger if the exception was taken due to SError in EL3 or
* because of pending asynchronous external aborts from lower EL that got
* triggered due to explicit synchronization in EL3. Refer Note 1.
*/
/* Assumes SP_EL3 on entry */
save_x30
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]
cbnz x30, 1f
/* Handle asynchronous external abort from lower EL */
b handle_lower_el_async_ea
1:
#endif
no_ret plat_handle_el3_ea
end_vector_entry serror_sp_elx
/* ---------------------------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_aarch64
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
save_x30
apply_at_speculative_wa
check_and_unmask_ea
handle_sync_exception
end_vector_entry sync_exception_aarch64
vector_entry irq_aarch64
save_x30
apply_at_speculative_wa
check_and_unmask_ea
b handle_interrupt_exception
end_vector_entry irq_aarch64
vector_entry fiq_aarch64
save_x30
apply_at_speculative_wa
check_and_unmask_ea
b handle_interrupt_exception
end_vector_entry fiq_aarch64
vector_entry serror_aarch64
save_x30
apply_at_speculative_wa
#if RAS_FFH_SUPPORT
msr daifclr, #DAIF_ABT_BIT
#else
check_and_unmask_ea
#endif
b handle_lower_el_async_ea
end_vector_entry serror_aarch64
/* ---------------------------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_aarch32
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
save_x30
apply_at_speculative_wa
check_and_unmask_ea
handle_sync_exception
end_vector_entry sync_exception_aarch32
vector_entry irq_aarch32
save_x30
apply_at_speculative_wa
check_and_unmask_ea
b handle_interrupt_exception
end_vector_entry irq_aarch32
vector_entry fiq_aarch32
save_x30
apply_at_speculative_wa
check_and_unmask_ea
b handle_interrupt_exception
end_vector_entry fiq_aarch32
vector_entry serror_aarch32
save_x30
apply_at_speculative_wa
#if RAS_FFH_SUPPORT
msr daifclr, #DAIF_ABT_BIT
#else
check_and_unmask_ea
#endif
b handle_lower_el_async_ea
end_vector_entry serror_aarch32
#ifdef MONITOR_TRAPS
.section .rodata.brk_string, "aS"
brk_location:
.asciz "Error at instruction 0x"
brk_message:
.asciz "Unexpected BRK instruction with value 0x"
#endif /* MONITOR_TRAPS */
/* ---------------------------------------------------------------------
* The following code handles secure monitor calls.
* Depending upon the execution state from where the SMC has been
* invoked, it frees some general purpose registers to perform the
* remaining tasks. They involve finding the runtime service handler
* that is the target of the SMC & switching to runtime stacks (SP_EL0)
* before calling the handler.
*
* Note that x30 has been explicitly saved and can be used here
* ---------------------------------------------------------------------
*/
func sync_exception_handler
smc_handler32:
/* Check whether aarch32 issued an SMC64 */
tbnz x0, #FUNCID_CC_SHIFT, smc_prohibited
sync_handler64:
/* NOTE: The code below must preserve x0-x4 */
/*
* Save general purpose and ARMv8.3-PAuth registers (if enabled).
* Also save PMCR_EL0 and set the PSTATE to a known state.
*/
bl prepare_el3_entry
#if ENABLE_PAUTH
/* Load and program APIAKey firmware key */
bl pauth_load_bl31_apiakey
#endif
/*
* Populate the parameters for the SMC handler.
* We already have x0-x4 in place. x5 will point to a cookie (not used
* now). x6 will point to the context structure (SP_EL3) and x7 will
* contain flags we need to pass to the handler.
*/
mov x5, xzr
mov x6, sp
/*
* Restore the saved C runtime stack value which will become the new
* SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context'
* structure prior to the last ERET from EL3.
*/
ldr x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
/* Switch to SP_EL0 */
msr spsel, #MODE_SP_EL0
/*
* Save the SPSR_EL3 and ELR_EL3 in case there is a world
* switch during SMC handling.
* TODO: Revisit if all system registers can be saved later.
*/
mrs x16, spsr_el3
mrs x17, elr_el3
stp x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
/* Load SCR_EL3 */
mrs x18, scr_el3
/* check for system register traps */
mrs x16, esr_el3
ubfx x17, x16, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x17, #EC_AARCH64_SYS
b.eq sysreg_handler64
/* Clear flag register */
mov x7, xzr
#if ENABLE_RME
/* Copy SCR_EL3.NSE bit to the flag to indicate caller's security */
ubfx x7, x18, #SCR_NSE_SHIFT, 1
/*
* Shift copied SCR_EL3.NSE bit by 5 to create space for
* SCR_EL3.NS bit. Bit 5 of the flag corresponds to
* the SCR_EL3.NSE bit.
*/
lsl x7, x7, #5
#endif /* ENABLE_RME */
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
bfi x7, x18, #0, #1
mov sp, x12
/*
* Per SMCCC documentation, bits [23:17] must be zero for Fast
* SMCs. Other values are reserved for future use. Ensure that
* these bits are zeroes, if not report as unknown SMC.
*/
tbz x0, #FUNCID_TYPE_SHIFT, 2f /* Skip check if its a Yield Call*/
tst x0, #(FUNCID_FC_RESERVED_MASK << FUNCID_FC_RESERVED_SHIFT)
b.ne smc_unknown
/*
* Per SMCCCv1.3 a caller can set the SVE hint bit in the SMC FID
* passed through x0. Copy the SVE hint bit to flags and mask the
* bit in smc_fid passed to the standard service dispatcher.
* A service/dispatcher can retrieve the SVE hint bit state from
* flags using the appropriate helper.
*/
2:
and x16, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT)
orr x7, x7, x16
bic x0, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT)
/* Get the unique owning entity number */
ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH
ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH
/* Load descriptor index from array of indices */
adrp x14, rt_svc_descs_indices
add x14, x14, :lo12:rt_svc_descs_indices
ldrb w15, [x14, x16]
/* Any index greater than 127 is invalid. Check bit 7. */
tbnz w15, 7, smc_unknown
/*
* Get the descriptor using the index
* x11 = (base + off), w15 = index
*
* handler = (base + off) + (index << log2(size))
*/
adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
lsl w10, w15, #RT_SVC_SIZE_LOG2
ldr x15, [x11, w10, uxtw]
/*
* Call the Secure Monitor Call handler and then drop directly into
* el3_exit() which will program any remaining architectural state
* prior to issuing the ERET to the desired lower EL.
*/
#if DEBUG
cbz x15, rt_svc_fw_critical_error
#endif
blr x15
b el3_exit
sysreg_handler64:
mov x0, x16 /* ESR_EL3, containing syndrome information */
mov x1, x6 /* lower EL's context */
mov x19, x6 /* save context pointer for after the call */
mov sp, x12 /* EL3 runtime stack, as loaded above */
/* int handle_sysreg_trap(uint64_t esr_el3, cpu_context_t *ctx); */
bl handle_sysreg_trap
/*
* returns:
* -1: unhandled trap, panic
* 0: handled trap, return to the trapping instruction (repeating it)
* 1: handled trap, return to the next instruction
*/
tst w0, w0
b.mi elx_panic /* negative return value: panic */
b.eq 1f /* zero: do not change ELR_EL3 */
/* advance the PC to continue after the instruction */
ldr x1, [x19, #CTX_EL3STATE_OFFSET + CTX_ELR_EL3]
add x1, x1, #4
str x1, [x19, #CTX_EL3STATE_OFFSET + CTX_ELR_EL3]
1:
b el3_exit
smc_unknown:
/*
* Unknown SMC call. Populate return value with SMC_UNK and call
* el3_exit() which will restore the remaining architectural state
* i.e., SYS, GP and PAuth registers(if any) prior to issuing the ERET
* to the desired lower EL.
*/
mov x0, #SMC_UNK
str x0, [x6, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
b el3_exit
smc_prohibited:
restore_ptw_el1_sys_regs
ldp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
mov x0, #SMC_UNK
exception_return
#if DEBUG
rt_svc_fw_critical_error:
/* Switch to SP_ELx */
msr spsel, #MODE_SP_ELX
no_ret report_unhandled_exception
#endif
endfunc sync_exception_handler
/* ---------------------------------------------------------------------
* This function handles FIQ or IRQ interrupts i.e. EL3, S-EL1 and NS
* interrupts.
*
* Note that x30 has been explicitly saved and can be used here
* ---------------------------------------------------------------------
*/
func handle_interrupt_exception
/*
* Save general purpose and ARMv8.3-PAuth registers (if enabled).
* Also save PMCR_EL0 and set the PSTATE to a known state.
*/
bl prepare_el3_entry
#if ENABLE_PAUTH
/* Load and program APIAKey firmware key */
bl pauth_load_bl31_apiakey
#endif
/* Save the EL3 system registers needed to return from this exception */
mrs x0, spsr_el3
mrs x1, elr_el3
stp x0, x1, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
/* Switch to the runtime stack i.e. SP_EL0 */
ldr x2, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
mov x20, sp
msr spsel, #MODE_SP_EL0
mov sp, x2
/*
* Find out whether this is a valid interrupt type.
* If the interrupt controller reports a spurious interrupt then return
* to where we came from.
*/
bl plat_ic_get_pending_interrupt_type
cmp x0, #INTR_TYPE_INVAL
b.eq interrupt_exit
/*
* Get the registered handler for this interrupt type.
* A NULL return value could be 'cause of the following conditions:
*
* a. An interrupt of a type was routed correctly but a handler for its
* type was not registered.
*
* b. An interrupt of a type was not routed correctly so a handler for
* its type was not registered.
*
* c. An interrupt of a type was routed correctly to EL3, but was
* deasserted before its pending state could be read. Another
* interrupt of a different type pended at the same time and its
* type was reported as pending instead. However, a handler for this
* type was not registered.
*
* a. and b. can only happen due to a programming error. The
* occurrence of c. could be beyond the control of Trusted Firmware.
* It makes sense to return from this exception instead of reporting an
* error.
*/
bl get_interrupt_type_handler
cbz x0, interrupt_exit
mov x21, x0
mov x0, #INTR_ID_UNAVAILABLE
/* Set the current security state in the 'flags' parameter */
mrs x2, scr_el3
ubfx x1, x2, #0, #1
/* Restore the reference to the 'handle' i.e. SP_EL3 */
mov x2, x20
/* x3 will point to a cookie (not used now) */
mov x3, xzr
/* Call the interrupt type handler */
blr x21
interrupt_exit:
/* Return from exception, possibly in a different security state */
b el3_exit
endfunc handle_interrupt_exception
/* ---------------------------------------------------------------------
* The following code handles exceptions caused by BRK instructions.
* Following a BRK instruction, the only real valid cause of action is
* to print some information and panic, as the code that caused it is
* likely in an inconsistent internal state.
*
* This is initially intended to be used in conjunction with
* __builtin_trap.
* ---------------------------------------------------------------------
*/
#ifdef MONITOR_TRAPS
func brk_handler
/* Extract the ISS */
mrs x10, esr_el3
ubfx x10, x10, #ESR_ISS_SHIFT, #ESR_ISS_LENGTH
/* Ensure the console is initialized */
bl plat_crash_console_init
adr x4, brk_location
bl asm_print_str
mrs x4, elr_el3
bl asm_print_hex
bl asm_print_newline
adr x4, brk_message
bl asm_print_str
mov x4, x10
mov x5, #28
bl asm_print_hex_bits
bl asm_print_newline
no_ret plat_panic_handler
endfunc brk_handler
#endif /* MONITOR_TRAPS */

210
bl31/bl31.ld.S Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl31_entrypoint)
MEMORY {
RAM (rwx): ORIGIN = BL31_BASE, LENGTH = BL31_LIMIT - BL31_BASE
#if SEPARATE_NOBITS_REGION
NOBITS (rw!a): ORIGIN = BL31_NOBITS_BASE, LENGTH = BL31_NOBITS_LIMIT - BL31_NOBITS_BASE
#else /* SEPARATE_NOBITS_REGION */
# define NOBITS RAM
#endif /* SEPARATE_NOBITS_REGION */
}
#ifdef PLAT_EXTRA_LD_SCRIPT
# include <plat.ld.S>
#endif /* PLAT_EXTRA_LD_SCRIPT */
SECTIONS {
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
. = BL31_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL31_BASE address is not aligned on a page boundary.")
__BL31_START__ = .;
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
*bl31_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(SORT(.text*)))
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >RAM
.rodata . : {
__RODATA_START__ = .;
*(SORT_BY_ALIGNMENT(.rodata*))
# if PLAT_EXTRA_RODATA_INCLUDES
# include <plat.ld.rodata.inc>
# endif /* PLAT_EXTRA_RODATA_INCLUDES */
RODATA_COMMON
. = ALIGN(8);
# include <lib/el3_runtime/pubsub_events.h>
__RODATA_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__RODATA_END__ = .;
} >RAM
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
*bl31_entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
. = ALIGN(8);
# include <lib/el3_runtime/pubsub_events.h>
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as read-only,
* executable. No RW data from the next section must creep in. Ensure
* that the rest of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__RO_END__ = .;
} >RAM
#endif /* SEPARATE_CODE_AND_RODATA */
ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__,
"cpu_ops not defined for this platform.")
#if SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP)
# ifndef SPM_SHIM_EXCEPTIONS_VMA
# define SPM_SHIM_EXCEPTIONS_VMA RAM
# endif /* SPM_SHIM_EXCEPTIONS_VMA */
/*
* Exception vectors of the SPM shim layer. They must be aligned to a 2K
* address but we need to place them in a separate page so that we can set
* individual permissions on them, so the actual alignment needed is the
* page size.
*
* There's no need to include this into the RO section of BL31 because it
* doesn't need to be accessed by BL31.
*/
.spm_shim_exceptions : ALIGN(PAGE_SIZE) {
__SPM_SHIM_EXCEPTIONS_START__ = .;
*(.spm_shim_exceptions)
. = ALIGN(PAGE_SIZE);
__SPM_SHIM_EXCEPTIONS_END__ = .;
} >SPM_SHIM_EXCEPTIONS_VMA AT>RAM
PROVIDE(__SPM_SHIM_EXCEPTIONS_LMA__ = LOADADDR(.spm_shim_exceptions));
. = LOADADDR(.spm_shim_exceptions) + SIZEOF(.spm_shim_exceptions);
#endif /* SPM_MM || (SPMC_AT_EL3 && SPMC_AT_EL3_SEL0_SP) */
__RW_START__ = .;
DATA_SECTION >RAM
RELA_SECTION >RAM
#ifdef BL31_PROGBITS_LIMIT
ASSERT(
. <= BL31_PROGBITS_LIMIT,
"BL31 progbits has exceeded its limit. Consider disabling some features."
)
#endif /* BL31_PROGBITS_LIMIT */
#if SEPARATE_NOBITS_REGION
. = ALIGN(PAGE_SIZE);
__RW_END__ = .;
__BL31_END__ = .;
ASSERT(. <= BL31_LIMIT, "BL31 image has exceeded its limit.")
. = BL31_NOBITS_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL31 NOBITS base address is not aligned on a page boundary.")
__NOBITS_START__ = .;
#endif /* SEPARATE_NOBITS_REGION */
STACK_SECTION >NOBITS
BSS_SECTION >NOBITS
XLAT_TABLE_SECTION >NOBITS
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
/*
* Bakery locks are stored in coherent memory. Each lock's data is
* contiguous and fully allocated by the compiler.
*/
*(.bakery_lock)
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure the rest of
* the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >NOBITS
#endif /* USE_COHERENT_MEM */
#if SEPARATE_NOBITS_REGION
__NOBITS_END__ = .;
ASSERT(. <= BL31_NOBITS_LIMIT, "BL31 NOBITS region has exceeded its limit.")
#else /* SEPARATE_NOBITS_REGION */
__RW_END__ = .;
__BL31_END__ = .;
ASSERT(. <= BL31_LIMIT, "BL31 image has exceeded its limit.")
#endif /* SEPARATE_NOBITS_REGION */
RAM_REGION_END = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
}

184
bl31/bl31.mk Normal file
View File

@ -0,0 +1,184 @@
#
# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
################################################################################
# Include Makefile for the SPM-MM implementation
################################################################################
ifeq (${SUPPORT_UNKNOWN_MPID},1)
ifeq (${DEBUG},0)
$(warning WARNING: SUPPORT_UNKNOWN_MPID enabled)
endif
endif
ifeq (${SPM_MM},1)
ifeq (${EL3_EXCEPTION_HANDLING},0)
$(error EL3_EXCEPTION_HANDLING must be 1 for SPM-MM support)
else
$(info Including SPM Management Mode (MM) makefile)
include services/std_svc/spm/common/spm.mk
include services/std_svc/spm/spm_mm/spm_mm.mk
endif
endif
include lib/extensions/amu/amu.mk
include lib/mpmm/mpmm.mk
ifeq (${SPMC_AT_EL3},1)
$(warning "EL3 SPMC is an experimental feature")
$(info Including EL3 SPMC makefile)
include services/std_svc/spm/common/spm.mk
include services/std_svc/spm/el3_spmc/spmc.mk
endif
include lib/psci/psci_lib.mk
BL31_SOURCES += bl31/bl31_main.c \
bl31/interrupt_mgmt.c \
bl31/aarch64/bl31_entrypoint.S \
bl31/aarch64/crash_reporting.S \
bl31/aarch64/ea_delegate.S \
bl31/aarch64/runtime_exceptions.S \
bl31/bl31_context_mgmt.c \
bl31/bl31_traps.c \
common/runtime_svc.c \
lib/cpus/aarch64/dsu_helpers.S \
plat/common/aarch64/platform_mp_stack.S \
services/arm_arch_svc/arm_arch_svc_setup.c \
services/std_svc/std_svc_setup.c \
${PSCI_LIB_SOURCES} \
${SPMD_SOURCES} \
${SPM_MM_SOURCES} \
${SPMC_SOURCES} \
${SPM_SOURCES}
ifeq (${ENABLE_PMF}, 1)
BL31_SOURCES += lib/pmf/pmf_main.c
endif
include lib/debugfs/debugfs.mk
ifeq (${USE_DEBUGFS},1)
BL31_SOURCES += $(DEBUGFS_SRCS)
endif
ifeq (${EL3_EXCEPTION_HANDLING},1)
BL31_SOURCES += bl31/ehf.c
endif
ifeq (${SDEI_SUPPORT},1)
ifeq (${EL3_EXCEPTION_HANDLING},0)
$(error EL3_EXCEPTION_HANDLING must be 1 for SDEI support)
endif
BL31_SOURCES += services/std_svc/sdei/sdei_dispatch.S \
services/std_svc/sdei/sdei_event.c \
services/std_svc/sdei/sdei_intr_mgmt.c \
services/std_svc/sdei/sdei_main.c \
services/std_svc/sdei/sdei_state.c
endif
ifeq (${TRNG_SUPPORT},1)
BL31_SOURCES += services/std_svc/trng/trng_main.c \
services/std_svc/trng/trng_entropy_pool.c
endif
ifneq (${ENABLE_SPE_FOR_NS},0)
BL31_SOURCES += lib/extensions/spe/spe.c
endif
ifeq (${ERRATA_ABI_SUPPORT},1)
BL31_SOURCES += services/std_svc/errata_abi/errata_abi_main.c
endif
ifneq (${ENABLE_FEAT_AMU},0)
BL31_SOURCES += ${AMU_SOURCES}
endif
ifeq (${ENABLE_MPMM},1)
BL31_SOURCES += ${MPMM_SOURCES}
endif
ifneq (${ENABLE_SME_FOR_NS},0)
BL31_SOURCES += lib/extensions/sme/sme.c
endif
ifneq (${ENABLE_SVE_FOR_NS},0)
BL31_SOURCES += lib/extensions/sve/sve.c
endif
ifneq (${ENABLE_MPAM_FOR_LOWER_ELS},0)
BL31_SOURCES += lib/extensions/mpam/mpam.c
endif
ifneq (${ENABLE_TRBE_FOR_NS},0)
BL31_SOURCES += lib/extensions/trbe/trbe.c
endif
ifneq (${ENABLE_BRBE_FOR_NS},0)
BL31_SOURCES += lib/extensions/brbe/brbe.c
endif
ifneq (${ENABLE_SYS_REG_TRACE_FOR_NS},0)
BL31_SOURCES += lib/extensions/sys_reg_trace/aarch64/sys_reg_trace.c
endif
ifneq (${ENABLE_TRF_FOR_NS},0)
BL31_SOURCES += lib/extensions/trf/aarch64/trf.c
endif
ifeq (${WORKAROUND_CVE_2017_5715},1)
BL31_SOURCES += lib/cpus/aarch64/wa_cve_2017_5715_bpiall.S \
lib/cpus/aarch64/wa_cve_2017_5715_mmu.S
endif
ifeq ($(SMC_PCI_SUPPORT),1)
BL31_SOURCES += services/std_svc/pci_svc.c
endif
ifeq (${ENABLE_RME},1)
include lib/gpt_rme/gpt_rme.mk
BL31_SOURCES += ${GPT_LIB_SRCS} \
${RMMD_SOURCES}
endif
ifeq ($(FEATURE_DETECTION),1)
BL31_SOURCES += common/feat_detect.c
endif
ifeq (${DRTM_SUPPORT},1)
BL31_SOURCES += services/std_svc/drtm/drtm_main.c \
services/std_svc/drtm/drtm_dma_prot.c \
services/std_svc/drtm/drtm_res_address_map.c \
services/std_svc/drtm/drtm_measurements.c \
services/std_svc/drtm/drtm_remediation.c \
${MBEDTLS_SOURCES}
endif
BL31_DEFAULT_LINKER_SCRIPT_SOURCE := bl31/bl31.ld.S
ifneq ($(findstring gcc,$(notdir $(LD))),)
BL31_LDFLAGS += -Wl,--sort-section=alignment
else ifneq ($(findstring ld,$(notdir $(LD))),)
BL31_LDFLAGS += --sort-section=alignment
endif
# Flag used to indicate if Crash reporting via console should be included
# in BL31. This defaults to being present in DEBUG builds only
ifndef CRASH_REPORTING
CRASH_REPORTING := $(DEBUG)
endif
$(eval $(call assert_booleans,\
$(sort \
CRASH_REPORTING \
EL3_EXCEPTION_HANDLING \
SDEI_SUPPORT \
)))
$(eval $(call add_defines,\
$(sort \
CRASH_REPORTING \
EL3_EXCEPTION_HANDLING \
SDEI_SUPPORT \
)))

66
bl31/bl31_context_mgmt.c Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <bl31/bl31.h>
#include <common/bl_common.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/el3_runtime/cpu_data.h>
/*******************************************************************************
* This function returns a pointer to the most recent 'cpu_context' structure
* for the calling CPU that was set as the context for the specified security
* state. NULL is returned if no such structure has been specified.
******************************************************************************/
void *cm_get_context(uint32_t security_state)
{
assert(sec_state_is_valid(security_state));
return get_cpu_data(cpu_context[get_cpu_context_index(security_state)]);
}
/*******************************************************************************
* This function sets the pointer to the current 'cpu_context' structure for the
* specified security state for the calling CPU
******************************************************************************/
void cm_set_context(void *context, uint32_t security_state)
{
assert(sec_state_is_valid(security_state));
set_cpu_data(cpu_context[get_cpu_context_index(security_state)],
context);
}
/*******************************************************************************
* This function returns a pointer to the most recent 'cpu_context' structure
* for the CPU identified by `cpu_idx` that was set as the context for the
* specified security state. NULL is returned if no such structure has been
* specified.
******************************************************************************/
void *cm_get_context_by_index(unsigned int cpu_idx,
unsigned int security_state)
{
assert(sec_state_is_valid(security_state));
return get_cpu_data_by_index(cpu_idx,
cpu_context[get_cpu_context_index(security_state)]);
}
/*******************************************************************************
* This function sets the pointer to the current 'cpu_context' structure for the
* specified security state for the CPU identified by CPU index.
******************************************************************************/
void cm_set_context_by_index(unsigned int cpu_idx, void *context,
unsigned int security_state)
{
assert(sec_state_is_valid(security_state));
set_cpu_data_by_index(cpu_idx,
cpu_context[get_cpu_context_index(security_state)],
context);
}

303
bl31/bl31_main.c Normal file
View File

@ -0,0 +1,303 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include <arch.h>
#include <arch_features.h>
#include <arch_helpers.h>
#include <bl31/bl31.h>
#include <bl31/ehf.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/feat_detect.h>
#include <common/runtime_svc.h>
#include <drivers/console.h>
#include <lib/bootmarker_capture.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/pmf/pmf.h>
#include <lib/runtime_instr.h>
#include <plat/common/platform.h>
#include <services/std_svc.h>
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_REGISTER_SERVICE_SMC(rt_instr_svc, PMF_RT_INSTR_SVC_ID,
RT_INSTR_TOTAL_IDS, PMF_STORE_ENABLE)
#endif
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_REGISTER_SERVICE(bl_svc, PMF_RT_INSTR_SVC_ID,
BL_TOTAL_IDS, PMF_DUMP_ENABLE)
#endif
/*******************************************************************************
* This function pointer is used to initialise the BL32 image. It's initialized
* by SPD calling bl31_register_bl32_init after setting up all things necessary
* for SP execution. In cases where both SPD and SP are absent, or when SPD
* finds it impossible to execute SP, this pointer is left as NULL
******************************************************************************/
static int32_t (*bl32_init)(void);
/*****************************************************************************
* Function used to initialise RMM if RME is enabled
*****************************************************************************/
#if ENABLE_RME
static int32_t (*rmm_init)(void);
#endif
/*******************************************************************************
* Variable to indicate whether next image to execute after BL31 is BL33
* (non-secure & default) or BL32 (secure).
******************************************************************************/
static uint32_t next_image_type = NON_SECURE;
#ifdef SUPPORT_UNKNOWN_MPID
/*
* Flag to know whether an unsupported MPID has been detected. To avoid having it
* landing on the .bss section, it is initialized to a non-zero value, this way
* we avoid potential WAW hazards during system bring up.
* */
volatile uint32_t unsupported_mpid_flag = 1;
#endif
/*
* Implement the ARM Standard Service function to get arguments for a
* particular service.
*/
uintptr_t get_arm_std_svc_args(unsigned int svc_mask)
{
/* Setup the arguments for PSCI Library */
DEFINE_STATIC_PSCI_LIB_ARGS_V1(psci_args, bl31_warm_entrypoint);
/* PSCI is the only ARM Standard Service implemented */
assert(svc_mask == PSCI_FID_MASK);
return (uintptr_t)&psci_args;
}
/*******************************************************************************
* Simple function to initialise all BL31 helper libraries.
******************************************************************************/
void __init bl31_lib_init(void)
{
cm_init();
}
/*******************************************************************************
* Setup function for BL31.
******************************************************************************/
void bl31_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
/* Perform early platform-specific setup */
bl31_early_platform_setup2(arg0, arg1, arg2, arg3);
/* Perform late platform-specific setup */
bl31_plat_arch_setup();
#if CTX_INCLUDE_PAUTH_REGS
/*
* Assert that the ARMv8.3-PAuth registers are present or an access
* fault will be triggered when they are being saved or restored.
*/
assert(is_armv8_3_pauth_present());
#endif /* CTX_INCLUDE_PAUTH_REGS */
}
/*******************************************************************************
* BL31 is responsible for setting up the runtime services for the primary cpu
* before passing control to the bootloader or an Operating System. This
* function calls runtime_svc_init() which initializes all registered runtime
* services. The run time services would setup enough context for the core to
* switch to the next exception level. When this function returns, the core will
* switch to the programmed exception level via an ERET.
******************************************************************************/
void bl31_main(void)
{
/* Init registers that never change for the lifetime of TF-A */
cm_manage_extensions_el3();
NOTICE("BL31: %s\n", version_string);
NOTICE("BL31: %s\n", build_message);
#if FEATURE_DETECTION
/* Detect if features enabled during compilation are supported by PE. */
detect_arch_features();
#endif /* FEATURE_DETECTION */
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_ENTRY, PMF_CACHE_MAINT);
#endif
#ifdef SUPPORT_UNKNOWN_MPID
if (unsupported_mpid_flag == 0) {
NOTICE("Unsupported MPID detected!\n");
}
#endif
/* Perform platform setup in BL31 */
bl31_platform_setup();
/* Initialise helper libraries */
bl31_lib_init();
#if EL3_EXCEPTION_HANDLING
INFO("BL31: Initialising Exception Handling Framework\n");
ehf_init();
#endif
/* Initialize the runtime services e.g. psci. */
INFO("BL31: Initializing runtime services\n");
runtime_svc_init();
/*
* All the cold boot actions on the primary cpu are done. We now need to
* decide which is the next image and how to execute it.
* If the SPD runtime service is present, it would want to pass control
* to BL32 first in S-EL1. In that case, SPD would have registered a
* function to initialize bl32 where it takes responsibility of entering
* S-EL1 and returning control back to bl31_main. Similarly, if RME is
* enabled and a function is registered to initialize RMM, control is
* transferred to RMM in R-EL2. After RMM initialization, control is
* returned back to bl31_main. Once this is done we can prepare entry
* into BL33 as normal.
*/
/*
* If SPD had registered an init hook, invoke it.
*/
if (bl32_init != NULL) {
INFO("BL31: Initializing BL32\n");
console_flush();
int32_t rc = (*bl32_init)();
if (rc == 0) {
WARN("BL31: BL32 initialization failed\n");
}
}
/*
* If RME is enabled and init hook is registered, initialize RMM
* in R-EL2.
*/
#if ENABLE_RME
if (rmm_init != NULL) {
INFO("BL31: Initializing RMM\n");
console_flush();
int32_t rc = (*rmm_init)();
if (rc == 0) {
WARN("BL31: RMM initialization failed\n");
}
}
#endif
/*
* We are ready to enter the next EL. Prepare entry into the image
* corresponding to the desired security state after the next ERET.
*/
bl31_prepare_next_image_entry();
console_flush();
/*
* Perform any platform specific runtime setup prior to cold boot exit
* from BL31
*/
bl31_plat_runtime_setup();
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_EXIT, PMF_CACHE_MAINT);
console_flush();
#endif
}
/*******************************************************************************
* Accessor functions to help runtime services decide which image should be
* executed after BL31. This is BL33 or the non-secure bootloader image by
* default but the Secure payload dispatcher could override this by requesting
* an entry into BL32 (Secure payload) first. If it does so then it should use
* the same API to program an entry into BL33 once BL32 initialisation is
* complete.
******************************************************************************/
void bl31_set_next_image_type(uint32_t security_state)
{
assert(sec_state_is_valid(security_state));
next_image_type = security_state;
}
uint32_t bl31_get_next_image_type(void)
{
return next_image_type;
}
/*******************************************************************************
* This function programs EL3 registers and performs other setup to enable entry
* into the next image after BL31 at the next ERET.
******************************************************************************/
void __init bl31_prepare_next_image_entry(void)
{
entry_point_info_t *next_image_info;
uint32_t image_type;
#if CTX_INCLUDE_AARCH32_REGS
/*
* Ensure that the build flag to save AArch32 system registers in CPU
* context is not set for AArch64-only platforms.
*/
if (el_implemented(1) == EL_IMPL_A64ONLY) {
ERROR("EL1 supports AArch64-only. Please set build flag "
"CTX_INCLUDE_AARCH32_REGS = 0\n");
panic();
}
#endif
/* Determine which image to execute next */
image_type = bl31_get_next_image_type();
/* Program EL3 registers to enable entry into the next EL */
next_image_info = bl31_plat_get_next_image_ep_info(image_type);
assert(next_image_info != NULL);
assert(image_type == GET_SECURITY_STATE(next_image_info->h.attr));
INFO("BL31: Preparing for EL3 exit to %s world\n",
(image_type == SECURE) ? "secure" : "normal");
print_entry_point_info(next_image_info);
cm_init_my_context(next_image_info);
/*
* If we are entering the Non-secure world, use
* 'cm_prepare_el3_exit_ns' to exit.
*/
if (image_type == NON_SECURE) {
cm_prepare_el3_exit_ns();
} else {
cm_prepare_el3_exit(image_type);
}
}
/*******************************************************************************
* This function initializes the pointer to BL32 init function. This is expected
* to be called by the SPD after it finishes all its initialization
******************************************************************************/
void bl31_register_bl32_init(int32_t (*func)(void))
{
bl32_init = func;
}
#if ENABLE_RME
/*******************************************************************************
* This function initializes the pointer to RMM init function. This is expected
* to be called by the RMMD after it finishes all its initialization
******************************************************************************/
void bl31_register_rmm_init(int32_t (*func)(void))
{
rmm_init = func;
}
#endif

30
bl31/bl31_traps.c Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2022, ARM Limited. All rights reserved.
* Copyright (c) 2023, NVIDIA Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Dispatch synchronous system register traps from lower ELs.
*/
#include <bl31/sync_handle.h>
#include <context.h>
int handle_sysreg_trap(uint64_t esr_el3, cpu_context_t *ctx)
{
uint64_t __unused opcode = esr_el3 & ISS_SYSREG_OPCODE_MASK;
#if ENABLE_FEAT_RNG_TRAP
if ((opcode == ISS_SYSREG_OPCODE_RNDR) || (opcode == ISS_SYSREG_OPCODE_RNDRRS)) {
return plat_handle_rng_trap(esr_el3, ctx);
}
#endif
#if IMPDEF_SYSREG_TRAP
if ((opcode & ISS_SYSREG_OPCODE_IMPDEF) == ISS_SYSREG_OPCODE_IMPDEF) {
return plat_handle_impdef_trap(esr_el3, ctx);
}
#endif
return TRAP_RET_UNHANDLED;
}

533
bl31/ehf.c Normal file
View File

@ -0,0 +1,533 @@
/*
* Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Exception handlers at EL3, their priority levels, and management.
*/
#include <assert.h>
#include <stdbool.h>
#include <bl31/ehf.h>
#include <bl31/interrupt_mgmt.h>
#include <context.h>
#include <common/debug.h>
#include <drivers/arm/gic_common.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/el3_runtime/cpu_data.h>
#include <lib/el3_runtime/pubsub_events.h>
#include <plat/common/platform.h>
/* Output EHF logs as verbose */
#define EHF_LOG(...) VERBOSE("EHF: " __VA_ARGS__)
#define EHF_INVALID_IDX (-1)
/* For a valid handler, return the actual function pointer; otherwise, 0. */
#define RAW_HANDLER(h) \
((ehf_handler_t) ((((h) & EHF_PRI_VALID_) != 0U) ? \
((h) & ~EHF_PRI_VALID_) : 0U))
#define PRI_BIT(idx) (((ehf_pri_bits_t) 1u) << (idx))
/*
* Convert index into secure priority using the platform-defined priority bits
* field.
*/
#define IDX_TO_PRI(idx) \
((((unsigned) idx) << (7u - exception_data.pri_bits)) & 0x7fU)
/* Check whether a given index is valid */
#define IS_IDX_VALID(idx) \
((exception_data.ehf_priorities[idx].ehf_handler & EHF_PRI_VALID_) != 0U)
/* Returns whether given priority is in secure priority range */
#define IS_PRI_SECURE(pri) (((pri) & 0x80U) == 0U)
/* To be defined by the platform */
extern const ehf_priorities_t exception_data;
/* Translate priority to the index in the priority array */
static unsigned int pri_to_idx(unsigned int priority)
{
unsigned int idx;
idx = EHF_PRI_TO_IDX(priority, exception_data.pri_bits);
assert(idx < exception_data.num_priorities);
assert(IS_IDX_VALID(idx));
return idx;
}
/* Return whether there are outstanding priority activation */
static bool has_valid_pri_activations(pe_exc_data_t *pe_data)
{
return pe_data->active_pri_bits != 0U;
}
static pe_exc_data_t *this_cpu_data(void)
{
return &get_cpu_data(ehf_data);
}
/*
* Return the current priority index of this CPU. If no priority is active,
* return EHF_INVALID_IDX.
*/
static int get_pe_highest_active_idx(pe_exc_data_t *pe_data)
{
if (!has_valid_pri_activations(pe_data))
return EHF_INVALID_IDX;
/* Current priority is the right-most bit */
return (int) __builtin_ctz(pe_data->active_pri_bits);
}
/*
* Mark priority active by setting the corresponding bit in active_pri_bits and
* programming the priority mask.
*
* This API is to be used as part of delegating to lower ELs other than for
* interrupts; e.g. while handling synchronous exceptions.
*
* This API is expected to be invoked before restoring context (Secure or
* Non-secure) in preparation for the respective dispatch.
*/
void ehf_activate_priority(unsigned int priority)
{
int cur_pri_idx;
unsigned int old_mask, run_pri, idx;
pe_exc_data_t *pe_data = this_cpu_data();
/*
* Query interrupt controller for the running priority, or idle priority
* if no interrupts are being handled. The requested priority must be
* less (higher priority) than the active running priority.
*/
run_pri = plat_ic_get_running_priority();
if (priority >= run_pri) {
ERROR("Running priority higher (0x%x) than requested (0x%x)\n",
run_pri, priority);
panic();
}
/*
* If there were priority activations already, the requested priority
* must be less (higher priority) than the current highest priority
* activation so far.
*/
cur_pri_idx = get_pe_highest_active_idx(pe_data);
idx = pri_to_idx(priority);
if ((cur_pri_idx != EHF_INVALID_IDX) &&
(idx >= ((unsigned int) cur_pri_idx))) {
ERROR("Activation priority mismatch: req=0x%x current=0x%x\n",
priority, IDX_TO_PRI(cur_pri_idx));
panic();
}
/* Set the bit corresponding to the requested priority */
pe_data->active_pri_bits |= PRI_BIT(idx);
/*
* Program priority mask for the activated level. Check that the new
* priority mask is setting a higher priority level than the existing
* mask.
*/
old_mask = plat_ic_set_priority_mask(priority);
if (priority >= old_mask) {
ERROR("Requested priority (0x%x) lower than Priority Mask (0x%x)\n",
priority, old_mask);
panic();
}
/*
* If this is the first activation, save the priority mask. This will be
* restored after the last deactivation.
*/
if (cur_pri_idx == EHF_INVALID_IDX)
pe_data->init_pri_mask = (uint8_t) old_mask;
EHF_LOG("activate prio=%d\n", get_pe_highest_active_idx(pe_data));
}
/*
* Mark priority inactive by clearing the corresponding bit in active_pri_bits,
* and programming the priority mask.
*
* This API is expected to be used as part of delegating to to lower ELs other
* than for interrupts; e.g. while handling synchronous exceptions.
*
* This API is expected to be invoked after saving context (Secure or
* Non-secure), having concluded the respective dispatch.
*/
void ehf_deactivate_priority(unsigned int priority)
{
int cur_pri_idx;
pe_exc_data_t *pe_data = this_cpu_data();
unsigned int old_mask, run_pri, idx;
/*
* Query interrupt controller for the running priority, or idle priority
* if no interrupts are being handled. The requested priority must be
* less (higher priority) than the active running priority.
*/
run_pri = plat_ic_get_running_priority();
if (priority >= run_pri) {
ERROR("Running priority higher (0x%x) than requested (0x%x)\n",
run_pri, priority);
panic();
}
/*
* Deactivation is allowed only when there are priority activations, and
* the deactivation priority level must match the current activated
* priority.
*/
cur_pri_idx = get_pe_highest_active_idx(pe_data);
idx = pri_to_idx(priority);
if ((cur_pri_idx == EHF_INVALID_IDX) ||
(idx != ((unsigned int) cur_pri_idx))) {
ERROR("Deactivation priority mismatch: req=0x%x current=0x%x\n",
priority, IDX_TO_PRI(cur_pri_idx));
panic();
}
/* Clear bit corresponding to highest priority */
pe_data->active_pri_bits &= (pe_data->active_pri_bits - 1u);
/*
* Restore priority mask corresponding to the next priority, or the
* one stashed earlier if there are no more to deactivate.
*/
cur_pri_idx = get_pe_highest_active_idx(pe_data);
if (cur_pri_idx == EHF_INVALID_IDX)
old_mask = plat_ic_set_priority_mask(pe_data->init_pri_mask);
else
old_mask = plat_ic_set_priority_mask(priority);
if (old_mask > priority) {
ERROR("Deactivation priority (0x%x) lower than Priority Mask (0x%x)\n",
priority, old_mask);
panic();
}
EHF_LOG("deactivate prio=%d\n", get_pe_highest_active_idx(pe_data));
}
/*
* After leaving Non-secure world, stash current Non-secure Priority Mask, and
* set Priority Mask to the highest Non-secure priority so that Non-secure
* interrupts cannot preempt Secure execution.
*
* If the current running priority is in the secure range, or if there are
* outstanding priority activations, this function does nothing.
*
* This function subscribes to the 'cm_exited_normal_world' event published by
* the Context Management Library.
*/
static void *ehf_exited_normal_world(const void *arg)
{
unsigned int run_pri;
pe_exc_data_t *pe_data = this_cpu_data();
/* If the running priority is in the secure range, do nothing */
run_pri = plat_ic_get_running_priority();
if (IS_PRI_SECURE(run_pri))
return NULL;
/* Do nothing if there are explicit activations */
if (has_valid_pri_activations(pe_data))
return NULL;
assert(pe_data->ns_pri_mask == 0u);
pe_data->ns_pri_mask =
(uint8_t) plat_ic_set_priority_mask(GIC_HIGHEST_NS_PRIORITY);
/* The previous Priority Mask is not expected to be in secure range */
if (IS_PRI_SECURE(pe_data->ns_pri_mask)) {
ERROR("Priority Mask (0x%x) already in secure range\n",
pe_data->ns_pri_mask);
panic();
}
EHF_LOG("Priority Mask: 0x%x => 0x%x\n", pe_data->ns_pri_mask,
GIC_HIGHEST_NS_PRIORITY);
return NULL;
}
/*
* Conclude Secure execution and prepare for return to Non-secure world. Restore
* the Non-secure Priority Mask previously stashed upon leaving Non-secure
* world.
*
* If there the current running priority is in the secure range, or if there are
* outstanding priority activations, this function does nothing.
*
* This function subscribes to the 'cm_entering_normal_world' event published by
* the Context Management Library.
*/
static void *ehf_entering_normal_world(const void *arg)
{
unsigned int old_pmr, run_pri;
pe_exc_data_t *pe_data = this_cpu_data();
/* If the running priority is in the secure range, do nothing */
run_pri = plat_ic_get_running_priority();
if (IS_PRI_SECURE(run_pri))
return NULL;
/*
* If there are explicit activations, do nothing. The Priority Mask will
* be restored upon the last deactivation.
*/
if (has_valid_pri_activations(pe_data))
return NULL;
/* Do nothing if we don't have a valid Priority Mask to restore */
if (pe_data->ns_pri_mask == 0U)
return NULL;
old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask);
/*
* When exiting secure world, the current Priority Mask must be
* GIC_HIGHEST_NS_PRIORITY (as set during entry), or the Non-secure
* priority mask set upon calling ehf_allow_ns_preemption()
*/
if ((old_pmr != GIC_HIGHEST_NS_PRIORITY) &&
(old_pmr != pe_data->ns_pri_mask)) {
ERROR("Invalid Priority Mask (0x%x) restored\n", old_pmr);
panic();
}
EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask);
pe_data->ns_pri_mask = 0;
return NULL;
}
/*
* Program Priority Mask to the original Non-secure priority such that
* Non-secure interrupts may preempt Secure execution (for example, during
* Yielding SMC calls). The 'preempt_ret_code' parameter indicates the Yielding
* SMC's return value in case the call was preempted.
*
* This API is expected to be invoked before delegating a yielding SMC to Secure
* EL1. I.e. within the window of secure execution after Non-secure context is
* saved (after entry into EL3) and Secure context is restored (before entering
* Secure EL1).
*/
void ehf_allow_ns_preemption(uint64_t preempt_ret_code)
{
cpu_context_t *ns_ctx;
unsigned int old_pmr __unused;
pe_exc_data_t *pe_data = this_cpu_data();
/*
* We should have been notified earlier of entering secure world, and
* therefore have stashed the Non-secure priority mask.
*/
assert(pe_data->ns_pri_mask != 0U);
/* Make sure no priority levels are active when requesting this */
if (has_valid_pri_activations(pe_data)) {
ERROR("PE %lx has priority activations: 0x%x\n",
read_mpidr_el1(), pe_data->active_pri_bits);
panic();
}
/*
* Program preempted return code to x0 right away so that, if the
* Yielding SMC was indeed preempted before a dispatcher gets a chance
* to populate it, the caller would find the correct return value.
*/
ns_ctx = cm_get_context(NON_SECURE);
assert(ns_ctx != NULL);
write_ctx_reg(get_gpregs_ctx(ns_ctx), CTX_GPREG_X0, preempt_ret_code);
old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask);
EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask);
pe_data->ns_pri_mask = 0;
}
/*
* Return whether Secure execution has explicitly allowed Non-secure interrupts
* to preempt itself (for example, during Yielding SMC calls).
*/
unsigned int ehf_is_ns_preemption_allowed(void)
{
unsigned int run_pri;
pe_exc_data_t *pe_data = this_cpu_data();
/* If running priority is in secure range, return false */
run_pri = plat_ic_get_running_priority();
if (IS_PRI_SECURE(run_pri))
return 0;
/*
* If Non-secure preemption was permitted by calling
* ehf_allow_ns_preemption() earlier:
*
* - There wouldn't have been priority activations;
* - We would have cleared the stashed the Non-secure Priority Mask.
*/
if (has_valid_pri_activations(pe_data))
return 0;
if (pe_data->ns_pri_mask != 0U)
return 0;
return 1;
}
/*
* Top-level EL3 interrupt handler.
*/
static uint64_t ehf_el3_interrupt_handler(uint32_t id, uint32_t flags,
void *handle, void *cookie)
{
int ret = 0;
uint32_t intr_raw;
unsigned int intr, pri, idx;
ehf_handler_t handler;
/*
* Top-level interrupt type handler from Interrupt Management Framework
* doesn't acknowledge the interrupt; so the interrupt ID must be
* invalid.
*/
assert(id == INTR_ID_UNAVAILABLE);
/*
* Acknowledge interrupt. Proceed with handling only for valid interrupt
* IDs. This situation may arise because of Interrupt Management
* Framework identifying an EL3 interrupt, but before it's been
* acknowledged here, the interrupt was either deasserted, or there was
* a higher-priority interrupt of another type.
*/
intr_raw = plat_ic_acknowledge_interrupt();
intr = plat_ic_get_interrupt_id(intr_raw);
if (intr == INTR_ID_UNAVAILABLE)
return 0;
/* Having acknowledged the interrupt, get the running priority */
pri = plat_ic_get_running_priority();
/* Check EL3 interrupt priority is in secure range */
assert(IS_PRI_SECURE(pri));
/*
* Translate the priority to a descriptor index. We do this by masking
* and shifting the running priority value (platform-supplied).
*/
idx = pri_to_idx(pri);
/* Validate priority */
assert(pri == IDX_TO_PRI(idx));
handler = (ehf_handler_t) RAW_HANDLER(
exception_data.ehf_priorities[idx].ehf_handler);
if (handler == NULL) {
ERROR("No EL3 exception handler for priority 0x%x\n",
IDX_TO_PRI(idx));
panic();
}
/*
* Call registered handler. Pass the raw interrupt value to registered
* handlers.
*/
ret = handler(intr_raw, flags, handle, cookie);
return (uint64_t) ret;
}
/*
* Initialize the EL3 exception handling.
*/
void __init ehf_init(void)
{
unsigned int flags = 0;
int ret __unused;
/* Ensure EL3 interrupts are supported */
assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3));
/*
* Make sure that priority water mark has enough bits to represent the
* whole priority array.
*/
assert(exception_data.num_priorities <= (sizeof(ehf_pri_bits_t) * 8U));
assert(exception_data.ehf_priorities != NULL);
/*
* Bit 7 of GIC priority must be 0 for secure interrupts. This means
* platforms must use at least 1 of the remaining 7 bits.
*/
assert((exception_data.pri_bits >= 1U) ||
(exception_data.pri_bits < 8U));
/* Route EL3 interrupts when in Non-secure. */
set_interrupt_rm_flag(flags, NON_SECURE);
/*
* Route EL3 interrupts when in secure, only when SPMC is not present
* in S-EL2.
*/
#if !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1))
set_interrupt_rm_flag(flags, SECURE);
#endif /* !(defined(SPD_spmd) && (SPMD_SPM_AT_SEL2 == 1)) */
/* Register handler for EL3 interrupts */
ret = register_interrupt_type_handler(INTR_TYPE_EL3,
ehf_el3_interrupt_handler, flags);
assert(ret == 0);
}
/*
* Register a handler at the supplied priority. Registration is allowed only if
* a handler hasn't been registered before, or one wasn't provided at build
* time. The priority for which the handler is being registered must also accord
* with the platform-supplied data.
*/
void ehf_register_priority_handler(unsigned int pri, ehf_handler_t handler)
{
unsigned int idx;
/* Sanity check for handler */
assert(handler != NULL);
/* Handler ought to be 4-byte aligned */
assert((((uintptr_t) handler) & 3U) == 0U);
/* Ensure we register for valid priority */
idx = pri_to_idx(pri);
assert(idx < exception_data.num_priorities);
assert(IDX_TO_PRI(idx) == pri);
/* Return failure if a handler was already registered */
if (exception_data.ehf_priorities[idx].ehf_handler != EHF_NO_HANDLER_) {
ERROR("Handler already registered for priority 0x%x\n", pri);
panic();
}
/*
* Install handler, and retain the valid bit. We assume that the handler
* is 4-byte aligned, which is usually the case.
*/
exception_data.ehf_priorities[idx].ehf_handler =
(((uintptr_t) handler) | EHF_PRI_VALID_);
EHF_LOG("register pri=0x%x handler=%p\n", pri, handler);
}
SUBSCRIBE_TO_EVENT(cm_entering_normal_world, ehf_entering_normal_world);
SUBSCRIBE_TO_EVENT(cm_exited_normal_world, ehf_exited_normal_world);

227
bl31/interrupt_mgmt.c Normal file
View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <common/bl_common.h>
#include <bl31/interrupt_mgmt.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <plat/common/platform.h>
/*******************************************************************************
* Local structure and corresponding array to keep track of the state of the
* registered interrupt handlers for each interrupt type.
* The field descriptions are:
*
* 'scr_el3[2]' : Mapping of the routing model in the 'flags' field to the
* value of the SCR_EL3.IRQ or FIQ bit for each security state.
* There are two instances of this field corresponding to the
* two security states.
*
* 'flags' : Bit[0], Routing model for this interrupt type when execution is
* not in EL3 in the secure state. '1' implies that this
* interrupt will be routed to EL3. '0' implies that this
* interrupt will be routed to the current exception level.
*
* Bit[1], Routing model for this interrupt type when execution is
* not in EL3 in the non-secure state. '1' implies that this
* interrupt will be routed to EL3. '0' implies that this
* interrupt will be routed to the current exception level.
*
* All other bits are reserved and SBZ.
******************************************************************************/
typedef struct intr_type_desc {
interrupt_type_handler_t handler;
u_register_t scr_el3[2];
uint32_t flags;
} intr_type_desc_t;
static intr_type_desc_t intr_type_descs[MAX_INTR_TYPES];
/*******************************************************************************
* This function validates the interrupt type.
******************************************************************************/
static int32_t validate_interrupt_type(uint32_t type)
{
if (plat_ic_has_interrupt_type(type)) {
return 0;
}
return -EINVAL;
}
/*******************************************************************************
* This function validates the routing model for this type of interrupt
******************************************************************************/
static int32_t validate_routing_model(uint32_t type, uint32_t flags)
{
uint32_t rm_flags = (flags >> INTR_RM_FLAGS_SHIFT) & INTR_RM_FLAGS_MASK;
if (type == INTR_TYPE_S_EL1)
return validate_sel1_interrupt_rm(rm_flags);
if (type == INTR_TYPE_NS)
return validate_ns_interrupt_rm(rm_flags);
if (type == INTR_TYPE_EL3)
return validate_el3_interrupt_rm(rm_flags);
return -EINVAL;
}
/*******************************************************************************
* This function returns the cached copy of the SCR_EL3 which contains the
* routing model (expressed through the IRQ and FIQ bits) for a security state
* which was stored through a call to 'set_routing_model()' earlier.
******************************************************************************/
u_register_t get_scr_el3_from_routing_model(uint32_t security_state)
{
u_register_t scr_el3;
assert(sec_state_is_valid(security_state));
scr_el3 = intr_type_descs[INTR_TYPE_NS].scr_el3[security_state];
scr_el3 |= intr_type_descs[INTR_TYPE_S_EL1].scr_el3[security_state];
scr_el3 |= intr_type_descs[INTR_TYPE_EL3].scr_el3[security_state];
return scr_el3;
}
/*******************************************************************************
* This function uses the 'interrupt_type_flags' parameter to obtain the value
* of the trap bit (IRQ/FIQ) in the SCR_EL3 for a security state for this
* interrupt type. It uses it to update the SCR_EL3 in the cpu context and the
* 'intr_type_desc' for that security state.
******************************************************************************/
static void set_scr_el3_from_rm(uint32_t type,
uint32_t interrupt_type_flags,
uint32_t security_state)
{
uint32_t flag, bit_pos;
flag = get_interrupt_rm_flag(interrupt_type_flags, security_state);
bit_pos = plat_interrupt_type_to_line(type, security_state);
intr_type_descs[type].scr_el3[security_state] = (u_register_t)flag << bit_pos;
/*
* Update scr_el3 only if there is a context available. If not, it
* will be updated later during context initialization which will obtain
* the scr_el3 value to be used via get_scr_el3_from_routing_model()
*/
if (cm_get_context(security_state) != NULL)
cm_write_scr_el3_bit(security_state, bit_pos, flag);
}
/*******************************************************************************
* This function validates the routing model specified in the 'flags' and
* updates internal data structures to reflect the new routing model. It also
* updates the copy of SCR_EL3 for each security state with the new routing
* model in the 'cpu_context' structure for this cpu.
******************************************************************************/
int32_t set_routing_model(uint32_t type, uint32_t flags)
{
int32_t rc;
rc = validate_interrupt_type(type);
if (rc != 0)
return rc;
rc = validate_routing_model(type, flags);
if (rc != 0)
return rc;
/* Update the routing model in internal data structures */
intr_type_descs[type].flags = flags;
set_scr_el3_from_rm(type, flags, SECURE);
set_scr_el3_from_rm(type, flags, NON_SECURE);
return 0;
}
/******************************************************************************
* This function disables the routing model of interrupt 'type' from the
* specified 'security_state' on the local core. The disable is in effect
* till the core powers down or till the next enable for that interrupt
* type.
*****************************************************************************/
int disable_intr_rm_local(uint32_t type, uint32_t security_state)
{
uint32_t bit_pos, flag;
assert(intr_type_descs[type].handler != NULL);
flag = get_interrupt_rm_flag(INTR_DEFAULT_RM, security_state);
bit_pos = plat_interrupt_type_to_line(type, security_state);
cm_write_scr_el3_bit(security_state, bit_pos, flag);
return 0;
}
/******************************************************************************
* This function enables the routing model of interrupt 'type' from the
* specified 'security_state' on the local core.
*****************************************************************************/
int enable_intr_rm_local(uint32_t type, uint32_t security_state)
{
uint32_t bit_pos, flag;
assert(intr_type_descs[type].handler != NULL);
flag = get_interrupt_rm_flag(intr_type_descs[type].flags,
security_state);
bit_pos = plat_interrupt_type_to_line(type, security_state);
cm_write_scr_el3_bit(security_state, bit_pos, flag);
return 0;
}
/*******************************************************************************
* This function registers a handler for the 'type' of interrupt specified. It
* also validates the routing model specified in the 'flags' for this type of
* interrupt.
******************************************************************************/
int32_t register_interrupt_type_handler(uint32_t type,
interrupt_type_handler_t handler,
uint32_t flags)
{
int32_t rc;
/* Validate the 'handler' parameter */
if (handler == NULL)
return -EINVAL;
/* Validate the 'flags' parameter */
if ((flags & INTR_TYPE_FLAGS_MASK) != 0U)
return -EINVAL;
/* Check if a handler has already been registered */
if (intr_type_descs[type].handler != NULL)
return -EALREADY;
rc = set_routing_model(type, flags);
if (rc != 0)
return rc;
/* Save the handler */
intr_type_descs[type].handler = handler;
return 0;
}
/*******************************************************************************
* This function is called when an interrupt is generated and returns the
* handler for the interrupt type (if registered). It returns NULL if the
* interrupt type is not supported or its handler has not been registered.
******************************************************************************/
interrupt_type_handler_t get_interrupt_type_handler(uint32_t type)
{
if (validate_interrupt_type(type) != 0)
return NULL;
return intr_type_descs[type].handler;
}

15
bl32/optee/optee.mk Normal file
View File

@ -0,0 +1,15 @@
#
# Copyright (c) 2016-2019, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# This makefile only aims at complying with Trusted Firmware-A build process so
# that "optee" is a valid TF-A AArch32 Secure Playload identifier.
ifneq ($(ARCH),aarch32)
$(error This directory targets AArch32 support)
endif
$(eval $(call add_define,AARCH32_SP_OPTEE))
$(info Trusted Firmware-A built for OP-TEE payload support)

View File

@ -0,0 +1,367 @@
/*
* Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
#include <common/runtime_svc.h>
#include <context.h>
#include <el3_common_macros.S>
#include <lib/el3_runtime/cpu_data.h>
#include <lib/pmf/aarch32/pmf_asm_macros.S>
#include <lib/runtime_instr.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
#include <smccc_helpers.h>
#include <smccc_macros.S>
.globl sp_min_vector_table
.globl sp_min_entrypoint
.globl sp_min_warm_entrypoint
.globl sp_min_handle_smc
.globl sp_min_handle_fiq
#define FIXUP_SIZE ((BL32_LIMIT) - (BL32_BASE))
.macro route_fiq_to_sp_min reg
/* -----------------------------------------------------
* FIQs are secure interrupts trapped by Monitor and non
* secure is not allowed to mask the FIQs.
* -----------------------------------------------------
*/
ldcopr \reg, SCR
orr \reg, \reg, #SCR_FIQ_BIT
bic \reg, \reg, #SCR_FW_BIT
stcopr \reg, SCR
.endm
.macro clrex_on_monitor_entry
#if (ARM_ARCH_MAJOR == 7)
/*
* ARMv7 architectures need to clear the exclusive access when
* entering Monitor mode.
*/
clrex
#endif
.endm
vector_base sp_min_vector_table
b sp_min_entrypoint
b plat_panic_handler /* Undef */
b sp_min_handle_smc /* Syscall */
b report_prefetch_abort /* Prefetch abort */
b report_data_abort /* Data abort */
b plat_panic_handler /* Reserved */
b plat_panic_handler /* IRQ */
b sp_min_handle_fiq /* FIQ */
/*
* The Cold boot/Reset entrypoint for SP_MIN
*/
func sp_min_entrypoint
/* ---------------------------------------------------------------
* Stash the previous bootloader arguments r0 - r3 for later use.
* ---------------------------------------------------------------
*/
mov r9, r0
mov r10, r1
mov r11, r2
mov r12, r3
#if !RESET_TO_SP_MIN
/* ---------------------------------------------------------------------
* For !RESET_TO_SP_MIN systems, only the primary CPU ever reaches
* sp_min_entrypoint() during the cold boot flow, so the cold/warm boot
* and primary/secondary CPU logic should not be executed in this case.
*
* Also, assume that the previous bootloader has already initialised the
* SCTLR, including the CPU endianness, and has initialised the memory.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=0 \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=1 \
_exception_vectors=sp_min_vector_table \
_pie_fixup_size=FIXUP_SIZE
#else
/* ---------------------------------------------------------------------
* For RESET_TO_SP_MIN systems which have a programmable reset address,
* sp_min_entrypoint() is executed only on the cold boot path so we can
* skip the warm boot mailbox mechanism.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=sp_min_vector_table \
_pie_fixup_size=FIXUP_SIZE
#endif /* RESET_TO_SP_MIN */
#if SP_MIN_WITH_SECURE_FIQ
route_fiq_to_sp_min r4
#endif
/* ---------------------------------------------------------------------
* Relay the previous bootloader's arguments to the platform layer
* ---------------------------------------------------------------------
*/
mov r0, r9
mov r1, r10
mov r2, r11
mov r3, r12
bl sp_min_early_platform_setup2
bl sp_min_plat_arch_setup
/* Jump to the main function */
bl sp_min_main
/* -------------------------------------------------------------
* Clean the .data & .bss sections to main memory. This ensures
* that any global data which was initialised by the primary CPU
* is visible to secondary CPUs before they enable their data
* caches and participate in coherency.
* -------------------------------------------------------------
*/
ldr r0, =__DATA_START__
ldr r1, =__DATA_END__
sub r1, r1, r0
bl clean_dcache_range
ldr r0, =__BSS_START__
ldr r1, =__BSS_END__
sub r1, r1, r0
bl clean_dcache_range
bl smc_get_next_ctx
/* r0 points to `smc_ctx_t` */
/* The PSCI cpu_context registers have been copied to `smc_ctx_t` */
b sp_min_exit
endfunc sp_min_entrypoint
/*
* SMC handling function for SP_MIN.
*/
func sp_min_handle_smc
/* On SMC entry, `sp` points to `smc_ctx_t`. Save `lr`. */
str lr, [sp, #SMC_CTX_LR_MON]
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Read the timestamp value and store it on top of the C runtime stack.
* The value will be saved to the per-cpu data once the C stack is
* available, as a valid stack is needed to call _cpu_data()
*/
strd r0, r1, [sp, #SMC_CTX_GPREG_R0]
ldcopr16 r0, r1, CNTPCT_64
ldr lr, [sp, #SMC_CTX_SP_MON]
strd r0, r1, [lr, #-8]!
str lr, [sp, #SMC_CTX_SP_MON]
ldrd r0, r1, [sp, #SMC_CTX_GPREG_R0]
#endif
smccc_save_gp_mode_regs
clrex_on_monitor_entry
/*
* `sp` still points to `smc_ctx_t`. Save it to a register
* and restore the C runtime stack pointer to `sp`.
*/
mov r2, sp /* handle */
ldr sp, [r2, #SMC_CTX_SP_MON]
#if ENABLE_RUNTIME_INSTRUMENTATION
/* Save handle to a callee saved register */
mov r6, r2
/*
* Restore the timestamp value and store it in per-cpu data. The value
* will be extracted from per-cpu data by the C level SMC handler and
* saved to the PMF timestamp region.
*/
ldrd r4, r5, [sp], #8
bl _cpu_data
strd r4, r5, [r0, #CPU_DATA_PMF_TS0_OFFSET]
/* Restore handle */
mov r2, r6
#endif
ldr r0, [r2, #SMC_CTX_SCR]
and r3, r0, #SCR_NS_BIT /* flags */
/* Switch to Secure Mode*/
bic r0, #SCR_NS_BIT
stcopr r0, SCR
isb
ldr r0, [r2, #SMC_CTX_GPREG_R0] /* smc_fid */
/* Check whether an SMC64 is issued */
tst r0, #(FUNCID_CC_MASK << FUNCID_CC_SHIFT)
beq 1f
/* SMC32 is not detected. Return error back to caller */
mov r0, #SMC_UNK
str r0, [r2, #SMC_CTX_GPREG_R0]
mov r0, r2
b sp_min_exit
1:
/* SMC32 is detected */
mov r1, #0 /* cookie */
bl handle_runtime_svc
/* `r0` points to `smc_ctx_t` */
b sp_min_exit
endfunc sp_min_handle_smc
/*
* Secure Interrupts handling function for SP_MIN.
*/
func sp_min_handle_fiq
#if !SP_MIN_WITH_SECURE_FIQ
b plat_panic_handler
#else
/* FIQ has a +4 offset for lr compared to preferred return address */
sub lr, lr, #4
/* On SMC entry, `sp` points to `smc_ctx_t`. Save `lr`. */
str lr, [sp, #SMC_CTX_LR_MON]
smccc_save_gp_mode_regs
clrex_on_monitor_entry
/* load run-time stack */
mov r2, sp
ldr sp, [r2, #SMC_CTX_SP_MON]
/* Switch to Secure Mode */
ldr r0, [r2, #SMC_CTX_SCR]
bic r0, #SCR_NS_BIT
stcopr r0, SCR
isb
push {r2, r3}
bl sp_min_fiq
pop {r0, r3}
b sp_min_exit
#endif
endfunc sp_min_handle_fiq
/*
* The Warm boot entrypoint for SP_MIN.
*/
func sp_min_warm_entrypoint
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* This timestamp update happens with cache off. The next
* timestamp collection will need to do cache maintenance prior
* to timestamp update.
*/
pmf_calc_timestamp_addr rt_instr_svc, RT_INSTR_EXIT_HW_LOW_PWR
ldcopr16 r2, r3, CNTPCT_64
strd r2, r3, [r0]
#endif
/*
* On the warm boot path, most of the EL3 initialisations performed by
* 'el3_entrypoint_common' must be skipped:
*
* - Only when the platform bypasses the BL1/BL32 (SP_MIN) entrypoint by
* programming the reset address do we need to initialied the SCTLR.
* In other cases, we assume this has been taken care by the
* entrypoint code.
*
* - No need to determine the type of boot, we know it is a warm boot.
*
* - Do not try to distinguish between primary and secondary CPUs, this
* notion only exists for a cold boot.
*
* - No need to initialise the memory or the C runtime environment,
* it has been done once and for all on the cold boot path.
*/
el3_entrypoint_common \
_init_sctlr=PROGRAMMABLE_RESET_ADDRESS \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=0 \
_exception_vectors=sp_min_vector_table \
_pie_fixup_size=0
/*
* We're about to enable MMU and participate in PSCI state coordination.
*
* The PSCI implementation invokes platform routines that enable CPUs to
* participate in coherency. On a system where CPUs are not
* cache-coherent without appropriate platform specific programming,
* having caches enabled until such time might lead to coherency issues
* (resulting from stale data getting speculatively fetched, among
* others). Therefore we keep data caches disabled even after enabling
* the MMU for such platforms.
*
* On systems with hardware-assisted coherency, or on single cluster
* platforms, such platform specific programming is not required to
* enter coherency (as CPUs already are); and there's no reason to have
* caches disabled either.
*/
#if HW_ASSISTED_COHERENCY || WARMBOOT_ENABLE_DCACHE_EARLY
mov r0, #0
#else
mov r0, #DISABLE_DCACHE
#endif
bl bl32_plat_enable_mmu
#if SP_MIN_WITH_SECURE_FIQ
route_fiq_to_sp_min r0
#endif
bl sp_min_warm_boot
bl smc_get_next_ctx
/* r0 points to `smc_ctx_t` */
/* The PSCI cpu_context registers have been copied to `smc_ctx_t` */
#if ENABLE_RUNTIME_INSTRUMENTATION
/* Save smc_ctx_t */
mov r5, r0
pmf_calc_timestamp_addr rt_instr_svc, RT_INSTR_EXIT_PSCI
mov r4, r0
/*
* Invalidate before updating timestamp to ensure previous timestamp
* updates on the same cache line with caches disabled are properly
* seen by the same core. Without the cache invalidate, the core might
* write into a stale cache line.
*/
mov r1, #PMF_TS_SIZE
bl inv_dcache_range
ldcopr16 r0, r1, CNTPCT_64
strd r0, r1, [r4]
/* Restore smc_ctx_t */
mov r0, r5
#endif
b sp_min_exit
endfunc sp_min_warm_entrypoint
/*
* The function to restore the registers from SMC context and return
* to the mode restored to SPSR.
*
* Arguments : r0 must point to the SMC context to restore from.
*/
func sp_min_exit
monitor_exit
endfunc sp_min_exit

157
bl32/sp_min/sp_min.ld.S Normal file
View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
ENTRY(sp_min_vector_table)
MEMORY {
RAM (rwx): ORIGIN = BL32_BASE, LENGTH = BL32_LIMIT - BL32_BASE
}
#ifdef PLAT_SP_MIN_EXTRA_LD_SCRIPT
# include <plat_sp_min.ld.S>
#endif /* PLAT_SP_MIN_EXTRA_LD_SCRIPT */
SECTIONS {
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
. = BL32_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL32_BASE address is not aligned on a page boundary.")
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
*entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >RAM
/* .ARM.extab and .ARM.exidx are only added because Clang needs them */
.ARM.extab . : {
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >RAM
.ARM.exidx . : {
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} >RAM
.rodata . : {
__RODATA_START__ = .;
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
. = ALIGN(8);
# include <lib/el3_runtime/pubsub_events.h>
__RODATA_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__RODATA_END__ = .;
} >RAM
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
*entrypoint.o(.text*)
*(SORT_BY_ALIGNMENT(.text*))
*(SORT_BY_ALIGNMENT(.rodata*))
RODATA_COMMON
. = ALIGN(8);
# include <lib/el3_runtime/pubsub_events.h>
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure that the rest
* of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__RO_END__ = .;
} >RAM
#endif /* SEPARATE_CODE_AND_RODATA */
ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__,
"cpu_ops not defined for this platform.")
__RW_START__ = .;
DATA_SECTION >RAM
RELA_SECTION >RAM
#ifdef BL32_PROGBITS_LIMIT
ASSERT(. <= BL32_PROGBITS_LIMIT, "BL32 progbits has exceeded its limit.")
#endif /* BL32_PROGBITS_LIMIT */
STACK_SECTION >RAM
BSS_SECTION >RAM
XLAT_TABLE_SECTION >RAM
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
/*
* Bakery locks are stored in coherent memory. Each lock's data is
* contiguous and fully allocated by the compiler.
*/
*(.bakery_lock)
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure that the rest
* of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL32_END__ = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
ASSERT(. <= BL32_LIMIT, "BL32 image has exceeded its limit.")
RAM_REGION_END = .;
}

84
bl32/sp_min/sp_min.mk Normal file
View File

@ -0,0 +1,84 @@
#
# Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
ifneq (${ARCH}, aarch32)
$(error SP_MIN is only supported on AArch32 platforms)
endif
include lib/extensions/amu/amu.mk
include lib/psci/psci_lib.mk
INCLUDES += -Iinclude/bl32/sp_min
BL32_SOURCES += bl32/sp_min/sp_min_main.c \
bl32/sp_min/aarch32/entrypoint.S \
common/runtime_svc.c \
plat/common/aarch32/plat_sp_min_common.c\
services/arm_arch_svc/arm_arch_svc_setup.c \
services/std_svc/std_svc_setup.c \
${PSCI_LIB_SOURCES}
ifeq (${ENABLE_PMF}, 1)
BL32_SOURCES += lib/pmf/pmf_main.c
endif
ifneq (${ENABLE_FEAT_AMU},0)
BL32_SOURCES += ${AMU_SOURCES}
endif
ifeq (${WORKAROUND_CVE_2017_5715},1)
BL32_SOURCES += bl32/sp_min/wa_cve_2017_5715_bpiall.S \
bl32/sp_min/wa_cve_2017_5715_icache_inv.S
else
ifeq (${WORKAROUND_CVE_2022_23960},1)
BL32_SOURCES += bl32/sp_min/wa_cve_2017_5715_icache_inv.S
endif
endif
ifeq (${TRNG_SUPPORT},1)
BL32_SOURCES += services/std_svc/trng/trng_main.c \
services/std_svc/trng/trng_entropy_pool.c
endif
ifeq (${ERRATA_ABI_SUPPORT}, 1)
BL32_SOURCES += services/std_svc/errata_abi/errata_abi_main.c
endif
ifneq (${ENABLE_SYS_REG_TRACE_FOR_NS},0)
BL32_SOURCES += lib/extensions/sys_reg_trace/aarch32/sys_reg_trace.c
endif
ifneq (${ENABLE_TRF_FOR_NS},0)
BL32_SOURCES += lib/extensions/trf/aarch32/trf.c
endif
BL32_DEFAULT_LINKER_SCRIPT_SOURCE := bl32/sp_min/sp_min.ld.S
ifneq ($(findstring gcc,$(notdir $(LD))),)
BL32_LDFLAGS += -Wl,--sort-section=alignment
else ifneq ($(findstring ld,$(notdir $(LD))),)
BL32_LDFLAGS += --sort-section=alignment
endif
# Include the platform-specific SP_MIN Makefile
# If no platform-specific SP_MIN Makefile exists, it means SP_MIN is not supported
# on this platform.
SP_MIN_PLAT_MAKEFILE := $(wildcard ${PLAT_DIR}/sp_min/sp_min-${PLAT}.mk)
ifeq (,${SP_MIN_PLAT_MAKEFILE})
$(error SP_MIN is not supported on platform ${PLAT})
else
include ${SP_MIN_PLAT_MAKEFILE}
endif
RESET_TO_SP_MIN := 0
$(eval $(call add_define,RESET_TO_SP_MIN))
$(eval $(call assert_boolean,RESET_TO_SP_MIN))
# Flag to allow SP_MIN to handle FIQ interrupts in monitor mode. The platform
# port is free to override this value. It is default disabled.
SP_MIN_WITH_SECURE_FIQ ?= 0
$(eval $(call add_define,SP_MIN_WITH_SECURE_FIQ))
$(eval $(call assert_boolean,SP_MIN_WITH_SECURE_FIQ))

250
bl32/sp_min/sp_min_main.c Normal file
View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <platform_def.h>
#include <arch.h>
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <context.h>
#include <drivers/console.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/pmf/pmf.h>
#include <lib/psci/psci.h>
#include <lib/runtime_instr.h>
#include <lib/utils.h>
#include <plat/common/platform.h>
#include <platform_sp_min.h>
#include <services/std_svc.h>
#include <smccc_helpers.h>
#include "sp_min_private.h"
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_REGISTER_SERVICE_SMC(rt_instr_svc, PMF_RT_INSTR_SVC_ID,
RT_INSTR_TOTAL_IDS, PMF_STORE_ENABLE)
#endif
/* Pointers to per-core cpu contexts */
static void *sp_min_cpu_ctx_ptr[PLATFORM_CORE_COUNT];
/* SP_MIN only stores the non secure smc context */
static smc_ctx_t sp_min_smc_context[PLATFORM_CORE_COUNT];
/******************************************************************************
* Define the smccc helper library APIs
*****************************************************************************/
void *smc_get_ctx(unsigned int security_state)
{
assert(security_state == NON_SECURE);
return &sp_min_smc_context[plat_my_core_pos()];
}
void smc_set_next_ctx(unsigned int security_state)
{
assert(security_state == NON_SECURE);
/* SP_MIN stores only non secure smc context. Nothing to do here */
}
void *smc_get_next_ctx(void)
{
return &sp_min_smc_context[plat_my_core_pos()];
}
/*******************************************************************************
* This function returns a pointer to the most recent 'cpu_context' structure
* for the calling CPU that was set as the context for the specified security
* state. NULL is returned if no such structure has been specified.
******************************************************************************/
void *cm_get_context(uint32_t security_state)
{
assert(security_state == NON_SECURE);
return sp_min_cpu_ctx_ptr[plat_my_core_pos()];
}
/*******************************************************************************
* This function sets the pointer to the current 'cpu_context' structure for the
* specified security state for the calling CPU
******************************************************************************/
void cm_set_context(void *context, uint32_t security_state)
{
assert(security_state == NON_SECURE);
sp_min_cpu_ctx_ptr[plat_my_core_pos()] = context;
}
/*******************************************************************************
* This function returns a pointer to the most recent 'cpu_context' structure
* for the CPU identified by `cpu_idx` that was set as the context for the
* specified security state. NULL is returned if no such structure has been
* specified.
******************************************************************************/
void *cm_get_context_by_index(unsigned int cpu_idx,
unsigned int security_state)
{
assert(security_state == NON_SECURE);
return sp_min_cpu_ctx_ptr[cpu_idx];
}
/*******************************************************************************
* This function sets the pointer to the current 'cpu_context' structure for the
* specified security state for the CPU identified by CPU index.
******************************************************************************/
void cm_set_context_by_index(unsigned int cpu_idx, void *context,
unsigned int security_state)
{
assert(security_state == NON_SECURE);
sp_min_cpu_ctx_ptr[cpu_idx] = context;
}
static void copy_cpu_ctx_to_smc_stx(const regs_t *cpu_reg_ctx,
smc_ctx_t *next_smc_ctx)
{
next_smc_ctx->r0 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R0);
next_smc_ctx->r1 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R1);
next_smc_ctx->r2 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R2);
next_smc_ctx->lr_mon = read_ctx_reg(cpu_reg_ctx, CTX_LR);
next_smc_ctx->spsr_mon = read_ctx_reg(cpu_reg_ctx, CTX_SPSR);
next_smc_ctx->scr = read_ctx_reg(cpu_reg_ctx, CTX_SCR);
}
/*******************************************************************************
* This function invokes the PSCI library interface to initialize the
* non secure cpu context and copies the relevant cpu context register values
* to smc context. These registers will get programmed during `smc_exit`.
******************************************************************************/
static void sp_min_prepare_next_image_entry(void)
{
entry_point_info_t *next_image_info;
cpu_context_t *ctx = cm_get_context(NON_SECURE);
u_register_t ns_sctlr;
/* Program system registers to proceed to non-secure */
next_image_info = sp_min_plat_get_bl33_ep_info();
assert(next_image_info);
assert(NON_SECURE == GET_SECURITY_STATE(next_image_info->h.attr));
INFO("SP_MIN: Preparing exit to normal world\n");
print_entry_point_info(next_image_info);
psci_prepare_next_non_secure_ctx(next_image_info);
smc_set_next_ctx(NON_SECURE);
/* Copy r0, lr and spsr from cpu context to SMC context */
copy_cpu_ctx_to_smc_stx(get_regs_ctx(cm_get_context(NON_SECURE)),
smc_get_next_ctx());
/* Temporarily set the NS bit to access NS SCTLR */
write_scr(read_scr() | SCR_NS_BIT);
isb();
ns_sctlr = read_ctx_reg(get_regs_ctx(ctx), CTX_NS_SCTLR);
write_sctlr(ns_sctlr);
isb();
write_scr(read_scr() & ~SCR_NS_BIT);
isb();
}
/******************************************************************************
* Implement the ARM Standard Service function to get arguments for a
* particular service.
*****************************************************************************/
uintptr_t get_arm_std_svc_args(unsigned int svc_mask)
{
/* Setup the arguments for PSCI Library */
DEFINE_STATIC_PSCI_LIB_ARGS_V1(psci_args, sp_min_warm_entrypoint);
/* PSCI is the only ARM Standard Service implemented */
assert(svc_mask == PSCI_FID_MASK);
return (uintptr_t)&psci_args;
}
/******************************************************************************
* The SP_MIN main function. Do the platform and PSCI Library setup. Also
* initialize the runtime service framework.
*****************************************************************************/
void sp_min_main(void)
{
NOTICE("SP_MIN: %s\n", version_string);
NOTICE("SP_MIN: %s\n", build_message);
/* Perform the SP_MIN platform setup */
sp_min_platform_setup();
/* Initialize the runtime services e.g. psci */
INFO("SP_MIN: Initializing runtime services\n");
runtime_svc_init();
/*
* We are ready to enter the next EL. Prepare entry into the image
* corresponding to the desired security state after the next ERET.
*/
sp_min_prepare_next_image_entry();
/*
* Perform any platform specific runtime setup prior to cold boot exit
* from SP_MIN.
*/
sp_min_plat_runtime_setup();
console_flush();
}
/******************************************************************************
* This function is invoked during warm boot. Invoke the PSCI library
* warm boot entry point which takes care of Architectural and platform setup/
* restore. Copy the relevant cpu_context register values to smc context which
* will get programmed during `smc_exit`.
*****************************************************************************/
void sp_min_warm_boot(void)
{
smc_ctx_t *next_smc_ctx;
cpu_context_t *ctx = cm_get_context(NON_SECURE);
u_register_t ns_sctlr;
psci_warmboot_entrypoint();
smc_set_next_ctx(NON_SECURE);
next_smc_ctx = smc_get_next_ctx();
zeromem(next_smc_ctx, sizeof(smc_ctx_t));
copy_cpu_ctx_to_smc_stx(get_regs_ctx(cm_get_context(NON_SECURE)),
next_smc_ctx);
/* Temporarily set the NS bit to access NS SCTLR */
write_scr(read_scr() | SCR_NS_BIT);
isb();
ns_sctlr = read_ctx_reg(get_regs_ctx(ctx), CTX_NS_SCTLR);
write_sctlr(ns_sctlr);
isb();
write_scr(read_scr() & ~SCR_NS_BIT);
isb();
}
#if SP_MIN_WITH_SECURE_FIQ
/******************************************************************************
* This function is invoked on secure interrupts. By construction of the
* SP_MIN, secure interrupts can only be handled when core executes in non
* secure state.
*****************************************************************************/
void sp_min_fiq(void)
{
uint32_t id;
id = plat_ic_acknowledge_interrupt();
sp_min_plat_fiq_handler(id);
plat_ic_end_of_interrupt(id);
}
#endif /* SP_MIN_WITH_SECURE_FIQ */

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SP_MIN_PRIVATE_H
#define SP_MIN_PRIVATE_H
void sp_min_main(void);
void sp_min_warm_boot(void);
void sp_min_fiq(void);
#endif /* SP_MIN_PRIVATE_H */

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
.globl wa_cve_2017_5715_bpiall_vbar
vector_base wa_cve_2017_5715_bpiall_vbar
/* We encode the exception entry in the bottom 3 bits of SP */
add sp, sp, #1 /* Reset: 0b111 */
add sp, sp, #1 /* Undef: 0b110 */
add sp, sp, #1 /* Syscall: 0b101 */
add sp, sp, #1 /* Prefetch abort: 0b100 */
add sp, sp, #1 /* Data abort: 0b011 */
add sp, sp, #1 /* Reserved: 0b010 */
add sp, sp, #1 /* IRQ: 0b001 */
nop /* FIQ: 0b000 */
/*
* Invalidate the branch predictor, `r0` is a dummy register
* and is unused.
*/
stcopr r0, BPIALL
isb
/*
* As we cannot use any temporary registers and cannot
* clobber SP, we can decode the exception entry using
* an unrolled binary search.
*
* Note, if this code is re-used by other secure payloads,
* the below exception entry vectors must be changed to
* the vectors specific to that secure payload.
*/
tst sp, #4
bne 1f
tst sp, #2
bne 3f
/* Expected encoding: 0x1 and 0x0 */
tst sp, #1
/* Restore original value of SP by clearing the bottom 3 bits */
bic sp, sp, #0x7
bne plat_panic_handler /* IRQ */
b sp_min_handle_fiq /* FIQ */
1:
tst sp, #2
bne 2f
/* Expected encoding: 0x4 and 0x5 */
tst sp, #1
bic sp, sp, #0x7
bne sp_min_handle_smc /* Syscall */
b plat_panic_handler /* Prefetch abort */
2:
/* Expected encoding: 0x7 and 0x6 */
tst sp, #1
bic sp, sp, #0x7
bne sp_min_entrypoint /* Reset */
b plat_panic_handler /* Undef */
3:
/* Expected encoding: 0x2 and 0x3 */
tst sp, #1
bic sp, sp, #0x7
bne plat_panic_handler /* Data abort */
b plat_panic_handler /* Reserved */

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
.globl wa_cve_2017_5715_icache_inv_vbar
vector_base wa_cve_2017_5715_icache_inv_vbar
/* We encode the exception entry in the bottom 3 bits of SP */
add sp, sp, #1 /* Reset: 0b111 */
add sp, sp, #1 /* Undef: 0b110 */
add sp, sp, #1 /* Syscall: 0b101 */
add sp, sp, #1 /* Prefetch abort: 0b100 */
add sp, sp, #1 /* Data abort: 0b011 */
add sp, sp, #1 /* Reserved: 0b010 */
add sp, sp, #1 /* IRQ: 0b001 */
nop /* FIQ: 0b000 */
/*
* Invalidate the instruction cache, which we assume also
* invalidates the branch predictor. This may depend on
* other CPU specific changes (e.g. an ACTLR setting).
*/
stcopr r0, ICIALLU
isb
/*
* As we cannot use any temporary registers and cannot
* clobber SP, we can decode the exception entry using
* an unrolled binary search.
*
* Note, if this code is re-used by other secure payloads,
* the below exception entry vectors must be changed to
* the vectors specific to that secure payload.
*/
tst sp, #4
bne 1f
tst sp, #2
bne 3f
/* Expected encoding: 0x1 and 0x0 */
tst sp, #1
/* Restore original value of SP by clearing the bottom 3 bits */
bic sp, sp, #0x7
bne plat_panic_handler /* IRQ */
b sp_min_handle_fiq /* FIQ */
1:
/* Expected encoding: 0x4 and 0x5 */
tst sp, #2
bne 2f
tst sp, #1
bic sp, sp, #0x7
bne sp_min_handle_smc /* Syscall */
b plat_panic_handler /* Prefetch abort */
2:
/* Expected encoding: 0x7 and 0x6 */
tst sp, #1
bic sp, sp, #0x7
bne sp_min_entrypoint /* Reset */
b plat_panic_handler /* Undef */
3:
/* Expected encoding: 0x2 and 0x3 */
tst sp, #1
bic sp, sp, #0x7
bne plat_panic_handler /* Data abort */
b plat_panic_handler /* Reserved */

View File

@ -0,0 +1,489 @@
/*
* Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <platform_def.h>
#include <arch.h>
#include <asm_macros.S>
#include <bl32/tsp/tsp.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
#include <smccc_helpers.h>
#include "../tsp_private.h"
.globl tsp_entrypoint
.globl tsp_vector_table
#if SPMC_AT_EL3
.globl tsp_cpu_on_entry
#endif
/* ---------------------------------------------
* Populate the params in x0-x7 from the pointer
* to the smc args structure in x0.
* ---------------------------------------------
*/
.macro restore_args_call_smc
ldp x6, x7, [x0, #SMC_ARG6]
ldp x4, x5, [x0, #SMC_ARG4]
ldp x2, x3, [x0, #SMC_ARG2]
ldp x0, x1, [x0, #SMC_ARG0]
smc #0
.endm
.macro save_eret_context reg1 reg2
mrs \reg1, elr_el1
mrs \reg2, spsr_el1
stp \reg1, \reg2, [sp, #-0x10]!
stp x30, x18, [sp, #-0x10]!
.endm
.macro restore_eret_context reg1 reg2
ldp x30, x18, [sp], #0x10
ldp \reg1, \reg2, [sp], #0x10
msr elr_el1, \reg1
msr spsr_el1, \reg2
.endm
func tsp_entrypoint _align=3
#if ENABLE_PIE
/*
* ------------------------------------------------------------
* If PIE is enabled fixup the Global descriptor Table only
* once during primary core cold boot path.
*
* Compile time base address, required for fixup, is calculated
* using "pie_fixup" label present within first page.
* ------------------------------------------------------------
*/
pie_fixup:
ldr x0, =pie_fixup
and x0, x0, #~(PAGE_SIZE_MASK)
mov_imm x1, (BL32_LIMIT - BL32_BASE)
add x1, x1, x0
bl fixup_gdt_reloc
#endif /* ENABLE_PIE */
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, tsp_exceptions
msr vbar_el1, x0
isb
/* ---------------------------------------------
* Enable the SError interrupt now that the
* exception vectors have been setup.
* ---------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks and disable
* speculative loads.
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
bic x0, x0, #SCTLR_DSSBS_BIT
msr sctlr_el1, x0
isb
/* ---------------------------------------------
* Invalidate the RW memory used by the BL32
* image. This includes the data and NOBITS
* sections. This is done to safeguard against
* possible corruption of this memory by dirty
* cache lines in a system cache as a result of
* use by an earlier boot loader stage. If PIE
* is enabled however, RO sections including the
* GOT may be modified during pie fixup.
* Therefore, to be on the safe side, invalidate
* the entire image region if PIE is enabled.
* ---------------------------------------------
*/
#if ENABLE_PIE
#if SEPARATE_CODE_AND_RODATA
adrp x0, __TEXT_START__
add x0, x0, :lo12:__TEXT_START__
#else
adrp x0, __RO_START__
add x0, x0, :lo12:__RO_START__
#endif /* SEPARATE_CODE_AND_RODATA */
#else
adrp x0, __RW_START__
add x0, x0, :lo12:__RW_START__
#endif /* ENABLE_PIE */
adrp x1, __RW_END__
add x1, x1, :lo12:__RW_END__
sub x1, x1, x0
bl inv_dcache_range
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
adrp x0, __BSS_START__
add x0, x0, :lo12:__BSS_START__
adrp x1, __BSS_END__
add x1, x1, :lo12:__BSS_END__
sub x1, x1, x0
bl zeromem
#if USE_COHERENT_MEM
adrp x0, __COHERENT_RAM_START__
add x0, x0, :lo12:__COHERENT_RAM_START__
adrp x1, __COHERENT_RAM_END_UNALIGNED__
add x1, x1, :lo12:__COHERENT_RAM_END_UNALIGNED__
sub x1, x1, x0
bl zeromem
#endif
/* --------------------------------------------
* Allocate a stack whose memory will be marked
* as Normal-IS-WBWA when the MMU is enabled.
* There is no risk of reading stale stack
* memory after enabling the MMU as only the
* primary cpu is running at the moment.
* --------------------------------------------
*/
bl plat_set_my_stack
/* ---------------------------------------------
* Initialize the stack protector canary before
* any C code is called.
* ---------------------------------------------
*/
#if STACK_PROTECTOR_ENABLED
bl update_stack_protector_canary
#endif
/* ---------------------------------------------
* Perform TSP setup
* ---------------------------------------------
*/
bl tsp_setup
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1
* and enable pointer authentication
* ---------------------------------------------
*/
bl pauth_init_enable_el1
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl tsp_main
/* ---------------------------------------------
* Tell TSPD that we are done initialising
* ---------------------------------------------
*/
mov x1, x0
mov x0, #TSP_ENTRY_DONE
smc #0
tsp_entrypoint_panic:
b tsp_entrypoint_panic
endfunc tsp_entrypoint
/* -------------------------------------------
* Table of entrypoint vectors provided to the
* TSPD for the various entrypoints
* -------------------------------------------
*/
vector_base tsp_vector_table
b tsp_yield_smc_entry
b tsp_fast_smc_entry
b tsp_cpu_on_entry
b tsp_cpu_off_entry
b tsp_cpu_resume_entry
b tsp_cpu_suspend_entry
b tsp_sel1_intr_entry
b tsp_system_off_entry
b tsp_system_reset_entry
b tsp_abort_yield_smc_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is to be turned off through a CPU_OFF
* psci call to ask the TSP to perform any
* bookeeping necessary. In the current
* implementation, the TSPD expects the TSP to
* re-initialise its state so nothing is done
* here except for acknowledging the request.
* ---------------------------------------------
*/
func tsp_cpu_off_entry
bl tsp_cpu_off_main
restore_args_call_smc
endfunc tsp_cpu_off_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when the
* system is about to be switched off (through
* a SYSTEM_OFF psci call) to ask the TSP to
* perform any necessary bookkeeping.
* ---------------------------------------------
*/
func tsp_system_off_entry
bl tsp_system_off_main
restore_args_call_smc
endfunc tsp_system_off_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when the
* system is about to be reset (through a
* SYSTEM_RESET psci call) to ask the TSP to
* perform any necessary bookkeeping.
* ---------------------------------------------
*/
func tsp_system_reset_entry
bl tsp_system_reset_main
restore_args_call_smc
endfunc tsp_system_reset_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is turned on using a CPU_ON psci call to
* ask the TSP to initialise itself i.e. setup
* the mmu, stacks etc. Minimal architectural
* state will be initialised by the TSPD when
* this function is entered i.e. Caches and MMU
* will be turned off, the execution state
* will be aarch64 and exceptions masked.
* ---------------------------------------------
*/
func tsp_cpu_on_entry
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, tsp_exceptions
msr vbar_el1, x0
isb
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
msr sctlr_el1, x0
isb
/* --------------------------------------------
* Give ourselves a stack whose memory will be
* marked as Normal-IS-WBWA when the MMU is
* enabled.
* --------------------------------------------
*/
bl plat_set_my_stack
/* --------------------------------------------
* Enable MMU and D-caches together.
* --------------------------------------------
*/
mov x0, #0
bl bl32_plat_enable_mmu
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1
* and enable pointer authentication
* ---------------------------------------------
*/
bl pauth_init_enable_el1
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Enter C runtime to perform any remaining
* book keeping
* ---------------------------------------------
*/
bl tsp_cpu_on_main
restore_args_call_smc
/* Should never reach here */
tsp_cpu_on_entry_panic:
b tsp_cpu_on_entry_panic
endfunc tsp_cpu_on_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is to be suspended through a CPU_SUSPEND
* psci call to ask the TSP to perform any
* bookeeping necessary. In the current
* implementation, the TSPD saves and restores
* the EL1 state.
* ---------------------------------------------
*/
func tsp_cpu_suspend_entry
bl tsp_cpu_suspend_main
restore_args_call_smc
endfunc tsp_cpu_suspend_entry
/*-------------------------------------------------
* This entrypoint is used by the TSPD to pass
* control for `synchronously` handling a S-EL1
* Interrupt which was triggered while executing
* in normal world. 'x0' contains a magic number
* which indicates this. TSPD expects control to
* be handed back at the end of interrupt
* processing. This is done through an SMC.
* The handover agreement is:
*
* 1. PSTATE.DAIF are set upon entry. 'x1' has
* the ELR_EL3 from the non-secure state.
* 2. TSP has to preserve the callee saved
* general purpose registers, SP_EL1/EL0 and
* LR.
* 3. TSP has to preserve the system and vfp
* registers (if applicable).
* 4. TSP can use 'x0-x18' to enable its C
* runtime.
* 5. TSP returns to TSPD using an SMC with
* 'x0' = TSP_HANDLED_S_EL1_INTR
* ------------------------------------------------
*/
func tsp_sel1_intr_entry
#if DEBUG
mov_imm x2, TSP_HANDLE_SEL1_INTR_AND_RETURN
cmp x0, x2
b.ne tsp_sel1_int_entry_panic
#endif
/*-------------------------------------------------
* Save any previous context needed to perform
* an exception return from S-EL1 e.g. context
* from a previous Non secure Interrupt.
* Update statistics and handle the S-EL1
* interrupt before returning to the TSPD.
* IRQ/FIQs are not enabled since that will
* complicate the implementation. Execution
* will be transferred back to the normal world
* in any case. The handler can return 0
* if the interrupt was handled or TSP_PREEMPTED
* if the expected interrupt was preempted
* by an interrupt that should be handled in EL3
* e.g. Group 0 interrupt in GICv3. In both
* the cases switch to EL3 using SMC with id
* TSP_HANDLED_S_EL1_INTR. Any other return value
* from the handler will result in panic.
* ------------------------------------------------
*/
save_eret_context x2 x3
bl tsp_update_sync_sel1_intr_stats
bl tsp_common_int_handler
/* Check if the S-EL1 interrupt has been handled */
cbnz x0, tsp_sel1_intr_check_preemption
b tsp_sel1_intr_return
tsp_sel1_intr_check_preemption:
/* Check if the S-EL1 interrupt has been preempted */
mov_imm x1, TSP_PREEMPTED
cmp x0, x1
b.ne tsp_sel1_int_entry_panic
tsp_sel1_intr_return:
mov_imm x0, TSP_HANDLED_S_EL1_INTR
restore_eret_context x2 x3
smc #0
/* Should never reach here */
tsp_sel1_int_entry_panic:
no_ret plat_panic_handler
endfunc tsp_sel1_intr_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu resumes execution after an earlier
* CPU_SUSPEND psci call to ask the TSP to
* restore its saved context. In the current
* implementation, the TSPD saves and restores
* EL1 state so nothing is done here apart from
* acknowledging the request.
* ---------------------------------------------
*/
func tsp_cpu_resume_entry
bl tsp_cpu_resume_main
restore_args_call_smc
/* Should never reach here */
no_ret plat_panic_handler
endfunc tsp_cpu_resume_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD to ask
* the TSP to service a fast smc request.
* ---------------------------------------------
*/
func tsp_fast_smc_entry
bl tsp_smc_handler
restore_args_call_smc
/* Should never reach here */
no_ret plat_panic_handler
endfunc tsp_fast_smc_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD to ask
* the TSP to service a Yielding SMC request.
* We will enable preemption during execution
* of tsp_smc_handler.
* ---------------------------------------------
*/
func tsp_yield_smc_entry
msr daifclr, #DAIF_FIQ_BIT | DAIF_IRQ_BIT
bl tsp_smc_handler
msr daifset, #DAIF_FIQ_BIT | DAIF_IRQ_BIT
restore_args_call_smc
/* Should never reach here */
no_ret plat_panic_handler
endfunc tsp_yield_smc_entry
/*---------------------------------------------------------------------
* This entrypoint is used by the TSPD to abort a pre-empted Yielding
* SMC. It could be on behalf of non-secure world or because a CPU
* suspend/CPU off request needs to abort the preempted SMC.
* --------------------------------------------------------------------
*/
func tsp_abort_yield_smc_entry
/*
* Exceptions masking is already done by the TSPD when entering this
* hook so there is no need to do it here.
*/
/* Reset the stack used by the pre-empted SMC */
bl plat_set_my_stack
/*
* Allow some cleanup such as releasing locks.
*/
bl tsp_abort_smc_handler
restore_args_call_smc
/* Should never reach here */
bl plat_panic_handler
endfunc tsp_abort_yield_smc_entry

View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <bl32/tsp/tsp.h>
#include <common/bl_common.h>
/* ----------------------------------------------------
* The caller-saved registers x0-x18 and LR are saved
* here.
* ----------------------------------------------------
*/
#define SCRATCH_REG_SIZE #(20 * 8)
.macro save_caller_regs_and_lr
sub sp, sp, SCRATCH_REG_SIZE
stp x0, x1, [sp]
stp x2, x3, [sp, #0x10]
stp x4, x5, [sp, #0x20]
stp x6, x7, [sp, #0x30]
stp x8, x9, [sp, #0x40]
stp x10, x11, [sp, #0x50]
stp x12, x13, [sp, #0x60]
stp x14, x15, [sp, #0x70]
stp x16, x17, [sp, #0x80]
stp x18, x30, [sp, #0x90]
.endm
.macro restore_caller_regs_and_lr
ldp x0, x1, [sp]
ldp x2, x3, [sp, #0x10]
ldp x4, x5, [sp, #0x20]
ldp x6, x7, [sp, #0x30]
ldp x8, x9, [sp, #0x40]
ldp x10, x11, [sp, #0x50]
ldp x12, x13, [sp, #0x60]
ldp x14, x15, [sp, #0x70]
ldp x16, x17, [sp, #0x80]
ldp x18, x30, [sp, #0x90]
add sp, sp, SCRATCH_REG_SIZE
.endm
/* ----------------------------------------------------
* Common TSP interrupt handling routine
* ----------------------------------------------------
*/
.macro handle_tsp_interrupt label
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
save_caller_regs_and_lr
bl tsp_common_int_handler
cbz x0, interrupt_exit_\label
/*
* This interrupt was not targetted to S-EL1 so send it to
* the monitor and wait for execution to resume.
*/
smc #0
interrupt_exit_\label:
restore_caller_regs_and_lr
exception_return
.endm
.globl tsp_exceptions
/* -----------------------------------------------------
* TSP exception handlers.
* -----------------------------------------------------
*/
vector_base tsp_exceptions
/* -----------------------------------------------------
* Current EL with _sp_el0 : 0x0 - 0x200. No exceptions
* are expected and treated as irrecoverable errors.
* -----------------------------------------------------
*/
vector_entry sync_exception_sp_el0
b plat_panic_handler
end_vector_entry sync_exception_sp_el0
vector_entry irq_sp_el0
b plat_panic_handler
end_vector_entry irq_sp_el0
vector_entry fiq_sp_el0
b plat_panic_handler
end_vector_entry fiq_sp_el0
vector_entry serror_sp_el0
b plat_panic_handler
end_vector_entry serror_sp_el0
/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x400. Only IRQs/FIQs
* are expected and handled
* -----------------------------------------------------
*/
vector_entry sync_exception_sp_elx
b plat_panic_handler
end_vector_entry sync_exception_sp_elx
vector_entry irq_sp_elx
handle_tsp_interrupt irq_sp_elx
end_vector_entry irq_sp_elx
vector_entry fiq_sp_elx
handle_tsp_interrupt fiq_sp_elx
end_vector_entry fiq_sp_elx
vector_entry serror_sp_elx
b plat_panic_handler
end_vector_entry serror_sp_elx
/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600. No exceptions
* are handled since TSP does not implement a lower EL
* -----------------------------------------------------
*/
vector_entry sync_exception_aarch64
b plat_panic_handler
end_vector_entry sync_exception_aarch64
vector_entry irq_aarch64
b plat_panic_handler
end_vector_entry irq_aarch64
vector_entry fiq_aarch64
b plat_panic_handler
end_vector_entry fiq_aarch64
vector_entry serror_aarch64
b plat_panic_handler
end_vector_entry serror_aarch64
/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800. No exceptions
* handled since the TSP does not implement a lower EL.
* -----------------------------------------------------
*/
vector_entry sync_exception_aarch32
b plat_panic_handler
end_vector_entry sync_exception_aarch32
vector_entry irq_aarch32
b plat_panic_handler
end_vector_entry irq_aarch32
vector_entry fiq_aarch32
b plat_panic_handler
end_vector_entry fiq_aarch32
vector_entry serror_aarch32
b plat_panic_handler
end_vector_entry serror_aarch32

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
#include <bl32/tsp/tsp.h>
.globl tsp_get_magic
/*
* This function raises an SMC to retrieve arguments from secure
* monitor/dispatcher, saves the returned arguments the array received in x0,
* and then returns to the caller
*/
func tsp_get_magic
/* Load arguments */
ldr w0, _tsp_fid_get_magic
/* Raise SMC */
smc #0
/* Return arguments in x1:x0 */
ret
endfunc tsp_get_magic
.align 2
_tsp_fid_get_magic:
.word TSP_GET_ARGS

252
bl32/tsp/ffa_helpers.c Normal file
View File

@ -0,0 +1,252 @@
/*
* Copyright (c) 2022, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/debug.h>
#include "ffa_helpers.h"
#include <services/ffa_svc.h>
#include "tsp_private.h"
/*******************************************************************************
* Wrapper function to send a direct request.
******************************************************************************/
smc_args_t ffa_msg_send_direct_req(ffa_endpoint_id16_t sender,
ffa_endpoint_id16_t receiver,
uint32_t arg3,
uint32_t arg4,
uint32_t arg5,
uint32_t arg6,
uint32_t arg7)
{
uint32_t src_dst_ids = (sender << FFA_DIRECT_MSG_SOURCE_SHIFT) |
(receiver << FFA_DIRECT_MSG_DESTINATION_SHIFT);
/* Send Direct Request. */
return smc_helper(FFA_MSG_SEND_DIRECT_REQ_SMC64, src_dst_ids,
0, arg3, arg4, arg5, arg6, arg7);
}
/*******************************************************************************
* Wrapper function to send a direct response.
******************************************************************************/
smc_args_t *ffa_msg_send_direct_resp(ffa_endpoint_id16_t sender,
ffa_endpoint_id16_t receiver,
uint32_t arg3,
uint32_t arg4,
uint32_t arg5,
uint32_t arg6,
uint32_t arg7)
{
uint32_t src_dst_ids = (sender << FFA_DIRECT_MSG_SOURCE_SHIFT) |
(receiver << FFA_DIRECT_MSG_DESTINATION_SHIFT);
return set_smc_args(FFA_MSG_SEND_DIRECT_RESP_SMC64, src_dst_ids,
0, arg3, arg4, arg5, arg6, arg7);
}
/*******************************************************************************
* Memory Management Helpers.
******************************************************************************/
/**
* Initialises the header of the given `ffa_mtd`, not including the
* composite memory region offset.
*/
static void ffa_memory_region_init_header(
struct ffa_mtd *memory_region, ffa_endpoint_id16_t sender,
ffa_mem_attr16_t attributes, ffa_mtd_flag32_t flags,
uint64_t handle, uint64_t tag, ffa_endpoint_id16_t *receivers,
uint32_t receiver_count, ffa_mem_perm8_t permissions)
{
struct ffa_emad_v1_0 *emad;
memory_region->emad_offset = sizeof(struct ffa_mtd);
memory_region->emad_size = sizeof(struct ffa_emad_v1_0);
emad = (struct ffa_emad_v1_0 *)
((uint8_t *) memory_region +
memory_region->emad_offset);
memory_region->sender_id = sender;
memory_region->memory_region_attributes = attributes;
memory_region->reserved_36_39 = 0;
memory_region->flags = flags;
memory_region->handle = handle;
memory_region->tag = tag;
memory_region->reserved_40_47 = 0;
memory_region->emad_count = receiver_count;
for (uint32_t i = 0U; i < receiver_count; i++) {
emad[i].mapd.endpoint_id = receivers[i];
emad[i].mapd.memory_access_permissions = permissions;
emad[i].mapd.flags = 0;
emad[i].comp_mrd_offset = 0;
emad[i].reserved_8_15 = 0;
}
}
/**
* Initialises the given `ffa_mtd` to be used for an
* `FFA_MEM_RETRIEVE_REQ` by the receiver of a memory transaction.
* TODO: Support differing attributes per receiver.
*
* Returns the size of the descriptor written.
*/
static uint32_t ffa_memory_retrieve_request_init(
struct ffa_mtd *memory_region, uint64_t handle,
ffa_endpoint_id16_t sender, ffa_endpoint_id16_t *receivers, uint32_t receiver_count,
uint64_t tag, ffa_mtd_flag32_t flags,
ffa_mem_perm8_t permissions,
ffa_mem_attr16_t attributes)
{
ffa_memory_region_init_header(memory_region, sender, attributes, flags,
handle, tag, receivers,
receiver_count, permissions);
return sizeof(struct ffa_mtd) +
memory_region->emad_count * sizeof(struct ffa_emad_v1_0);
}
/* Relinquish access to memory region. */
bool ffa_mem_relinquish(void)
{
smc_args_t ret;
ret = smc_helper(FFA_MEM_RELINQUISH, 0, 0, 0, 0, 0, 0, 0);
if (ffa_func_id(ret) != FFA_SUCCESS_SMC32) {
ERROR("%s failed to relinquish memory! error: (%x) %x\n",
__func__, ffa_func_id(ret), ffa_error_code(ret));
return false;
}
return true;
}
/* Retrieve memory shared by another partition. */
smc_args_t ffa_mem_retrieve_req(uint32_t descriptor_length,
uint32_t fragment_length)
{
return smc_helper(FFA_MEM_RETRIEVE_REQ_SMC32,
descriptor_length,
fragment_length,
0, 0, 0, 0, 0);
}
/* Retrieve the next memory descriptor fragment. */
smc_args_t ffa_mem_frag_rx(uint64_t handle, uint32_t recv_length)
{
return smc_helper(FFA_MEM_FRAG_RX,
FFA_MEM_HANDLE_LOW(handle),
FFA_MEM_HANDLE_HIGH(handle),
recv_length,
0, 0, 0, 0);
}
bool memory_retrieve(struct mailbox *mb,
struct ffa_mtd **retrieved,
uint64_t handle, ffa_endpoint_id16_t sender,
ffa_endpoint_id16_t *receivers, uint32_t receiver_count,
ffa_mtd_flag32_t flags, uint32_t *frag_length,
uint32_t *total_length)
{
smc_args_t ret;
uint32_t descriptor_size;
struct ffa_mtd *memory_region;
if (retrieved == NULL || mb == NULL) {
ERROR("Invalid parameters!\n");
return false;
}
memory_region = (struct ffa_mtd *)mb->tx_buffer;
/* Clear TX buffer. */
memset(memory_region, 0, PAGE_SIZE);
/* Clear local buffer. */
memset(mem_region_buffer, 0, REGION_BUF_SIZE);
descriptor_size = ffa_memory_retrieve_request_init(
memory_region, handle, sender, receivers, receiver_count, 0, flags,
FFA_MEM_PERM_RW | FFA_MEM_PERM_NX,
FFA_MEM_ATTR_NORMAL_MEMORY_CACHED_WB |
FFA_MEM_ATTR_INNER_SHAREABLE);
ret = ffa_mem_retrieve_req(descriptor_size, descriptor_size);
if (ffa_func_id(ret) == FFA_ERROR) {
ERROR("Couldn't retrieve the memory page. Error: %x\n",
ffa_error_code(ret));
return false;
}
/*
* Following total_size and fragment_size are useful to keep track
* of the state of transaction. When the sum of all fragment_size of all
* fragments is equal to total_size, the memory transaction has been
* completed.
*/
*total_length = ret._regs[1];
*frag_length = ret._regs[2];
/* Validate frag_length is less than total_length and mailbox size. */
if (*frag_length == 0U || *total_length == 0U ||
*frag_length > *total_length || *frag_length > (mb->rxtx_page_count * PAGE_SIZE)) {
ERROR("Invalid parameters!\n");
return false;
}
/* Copy response to local buffer. */
memcpy(mem_region_buffer, mb->rx_buffer, *frag_length);
if (ffa_rx_release()) {
ERROR("Failed to release buffer!\n");
return false;
}
*retrieved = (struct ffa_mtd *) mem_region_buffer;
if ((*retrieved)->emad_count > MAX_MEM_SHARE_RECIPIENTS) {
VERBOSE("SPMC memory sharing supports max of %u receivers!\n",
MAX_MEM_SHARE_RECIPIENTS);
return false;
}
/*
* We are sharing memory from the normal world therefore validate the NS
* bit was set by the SPMC.
*/
if (((*retrieved)->memory_region_attributes & FFA_MEM_ATTR_NS_BIT) == 0U) {
ERROR("SPMC has not set the NS bit! 0x%x\n",
(*retrieved)->memory_region_attributes);
return false;
}
VERBOSE("Memory Descriptor Retrieved!\n");
return true;
}
/* Relinquish the memory region. */
bool memory_relinquish(struct ffa_mem_relinquish_descriptor *m, uint64_t handle,
ffa_endpoint_id16_t id)
{
ffa_mem_relinquish_init(m, handle, 0, id);
return ffa_mem_relinquish();
}
/* Query SPMC that the rx buffer of the partition can be released. */
bool ffa_rx_release(void)
{
smc_args_t ret;
ret = smc_helper(FFA_RX_RELEASE, 0, 0, 0, 0, 0, 0, 0);
return ret._regs[SMC_ARG0] != FFA_SUCCESS_SMC32;
}
/* Map the provided buffers with the SPMC. */
bool ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages)
{
smc_args_t ret;
ret = smc_helper(FFA_RXTX_MAP_SMC64, send, recv, pages, 0, 0, 0, 0);
return ret._regs[0] != FFA_SUCCESS_SMC32;
}

116
bl32/tsp/ffa_helpers.h Normal file
View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2022, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FFA_HELPERS_H
#define FFA_HELPERS_H
#include <stdint.h>
#include "../../services/std_svc/spm/el3_spmc/spmc.h"
#include "../../services/std_svc/spm/el3_spmc/spmc_shared_mem.h"
#include <services/el3_spmc_ffa_memory.h>
#include <services/ffa_svc.h>
#include "tsp_private.h"
static inline uint32_t ffa_func_id(smc_args_t val)
{
return (uint32_t) val._regs[0];
}
static inline int32_t ffa_error_code(smc_args_t val)
{
return (uint32_t) val._regs[2];
}
extern uint8_t mem_region_buffer[4096 * 2] __aligned(PAGE_SIZE);
#define REGION_BUF_SIZE sizeof(mem_region_buffer)
/** The maximum number of recipients a memory region may be sent to. */
#define MAX_MEM_SHARE_RECIPIENTS 2U
/* FFA Memory Management mode flags. */
#define FFA_FLAG_SHARE_MEMORY (1U << 3)
#define FFA_FLAG_LEND_MEMORY (1U << 4)
#define FFA_FLAG_MEMORY_MASK (3U << 3)
#define FFA_MEM_HANDLE_LOW(x) (x & 0xFFFFFFFF)
#define FFA_MEM_HANDLE_HIGH(x) (x >> 32)
#define FFA_MEM_PERM_DATA_OFFSET 0
#define FFA_MEM_PERM_DATA_MASK 0x3
static inline uint32_t ffa_mem_relinquish_init(
struct ffa_mem_relinquish_descriptor *relinquish_request,
uint64_t handle, ffa_mtd_flag32_t flags,
ffa_endpoint_id16_t sender)
{
relinquish_request->handle = handle;
relinquish_request->flags = flags;
relinquish_request->endpoint_count = 1;
relinquish_request->endpoint_array[0] = sender;
return sizeof(struct ffa_mem_relinquish_descriptor) + sizeof(ffa_endpoint_id16_t);
}
/**
* Gets the `ffa_comp_mrd` for the given receiver from an
* `ffa_mtd`, or NULL if it is not valid.
*/
static inline struct ffa_comp_mrd *
ffa_memory_region_get_composite(struct ffa_mtd *memory_region,
uint32_t receiver_index)
{
struct ffa_emad_v1_0 *receivers;
uint32_t offset;
receivers = (struct ffa_emad_v1_0 *)
((uint8_t *) memory_region +
memory_region->emad_offset +
(memory_region->emad_size * receiver_index));
offset = receivers->comp_mrd_offset;
if (offset == 0U) {
return NULL;
}
return (struct ffa_comp_mrd *)
((uint8_t *) memory_region + offset);
}
static inline uint32_t ffa_get_data_access_attr(ffa_mem_perm8_t perm)
{
return ((perm >> FFA_MEM_PERM_DATA_OFFSET) & FFA_MEM_PERM_DATA_MASK);
}
smc_args_t ffa_mem_frag_rx(uint64_t handle, uint32_t recv_length);
bool ffa_mem_relinquish(void);
bool ffa_rx_release(void);
bool memory_relinquish(struct ffa_mem_relinquish_descriptor *m, uint64_t handle,
ffa_endpoint_id16_t id);
bool ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages);
bool memory_retrieve(struct mailbox *mb,
struct ffa_mtd **retrieved,
uint64_t handle, ffa_endpoint_id16_t sender,
ffa_endpoint_id16_t *receivers, uint32_t receiver_count,
ffa_mtd_flag32_t flags, uint32_t *frag_length,
uint32_t *total_length);
smc_args_t ffa_msg_send_direct_req(ffa_endpoint_id16_t sender,
ffa_endpoint_id16_t receiver,
uint32_t arg3,
uint32_t arg4,
uint32_t arg5,
uint32_t arg6,
uint32_t arg7);
smc_args_t *ffa_msg_send_direct_resp(ffa_endpoint_id16_t sender,
ffa_endpoint_id16_t receiver,
uint32_t arg3,
uint32_t arg4,
uint32_t arg5,
uint32_t arg6,
uint32_t arg7);
#endif /* FFA_HELPERS_H */

129
bl32/tsp/tsp.ld.S Normal file
View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(tsp_entrypoint)
MEMORY {
RAM (rwx): ORIGIN = TSP_SEC_MEM_BASE, LENGTH = TSP_SEC_MEM_SIZE
}
SECTIONS {
RAM_REGION_START = ORIGIN(RAM);
RAM_REGION_LENGTH = LENGTH(RAM);
. = BL32_BASE;
ASSERT(. == ALIGN(PAGE_SIZE),
"BL32_BASE address is not aligned on a page boundary.")
#if SEPARATE_CODE_AND_RODATA
.text . : {
__TEXT_START__ = .;
*tsp_entrypoint.o(.text*)
*(.text*)
*(.vectors)
__TEXT_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__TEXT_END__ = .;
} >RAM
.rodata . : {
__RODATA_START__ = .;
*(.rodata*)
RODATA_COMMON
__RODATA_END_UNALIGNED__ = .;
. = ALIGN(PAGE_SIZE);
__RODATA_END__ = .;
} >RAM
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
__RO_START__ = .;
*tsp_entrypoint.o(.text*)
*(.text*)
*(.rodata*)
RODATA_COMMON
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as read-only,
* executable. No RW data from the next section must creep in. Ensure
* that the rest of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__RO_END__ = .;
} >RAM
#endif /* SEPARATE_CODE_AND_RODATA */
__RW_START__ = .;
DATA_SECTION >RAM
RELA_SECTION >RAM
#ifdef TSP_PROGBITS_LIMIT
ASSERT(. <= TSP_PROGBITS_LIMIT, "TSP progbits has exceeded its limit.")
#endif /* TSP_PROGBITS_LIMIT */
STACK_SECTION >RAM
BSS_SECTION >RAM
XLAT_TABLE_SECTION >RAM
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct memory
* attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure that the rest
* of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL32_END__ = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
ASSERT(. <= BL32_LIMIT, "BL32 image has exceeded its limit.")
RAM_REGION_END = .;
}

49
bl32/tsp/tsp.mk Normal file
View File

@ -0,0 +1,49 @@
#
# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
INCLUDES += -Iinclude/bl32/tsp
ifeq (${SPMC_AT_EL3},1)
BL32_SOURCES += bl32/tsp/tsp_ffa_main.c \
bl32/tsp/ffa_helpers.c
else
BL32_SOURCES += bl32/tsp/tsp_main.c
endif
BL32_SOURCES += bl32/tsp/aarch64/tsp_entrypoint.S \
bl32/tsp/aarch64/tsp_exceptions.S \
bl32/tsp/aarch64/tsp_request.S \
bl32/tsp/tsp_interrupt.c \
bl32/tsp/tsp_timer.c \
bl32/tsp/tsp_common.c \
common/aarch64/early_exceptions.S \
lib/locks/exclusive/aarch64/spinlock.S
BL32_DEFAULT_LINKER_SCRIPT_SOURCE := bl32/tsp/tsp.ld.S
ifneq ($(findstring gcc,$(notdir $(LD))),)
BL32_LDFLAGS += -Wl,--sort-section=alignment
else ifneq ($(findstring ld,$(notdir $(LD))),)
BL32_LDFLAGS += --sort-section=alignment
endif
# This flag determines if the TSPD initializes BL32 in tspd_init() (synchronous
# method) or configures BL31 to pass control to BL32 instead of BL33
# (asynchronous method).
TSP_INIT_ASYNC := 0
$(eval $(call assert_boolean,TSP_INIT_ASYNC))
$(eval $(call add_define,TSP_INIT_ASYNC))
# Include the platform-specific TSP Makefile
# If no platform-specific TSP Makefile exists, it means TSP is not supported
# on this platform.
TSP_PLAT_MAKEFILE := $(wildcard ${PLAT_DIR}/tsp/tsp-${PLAT}.mk)
ifeq (,${TSP_PLAT_MAKEFILE})
$(error TSP is not supported on platform ${PLAT})
else
include ${TSP_PLAT_MAKEFILE}
endif

156
bl32/tsp/tsp_common.c Normal file
View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <arch_features.h>
#include <arch_helpers.h>
#include <bl32/tsp/tsp.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <lib/spinlock.h>
#include <plat/common/platform.h>
#include <platform_tsp.h>
#include "tsp_private.h"
#include <platform_def.h>
/*******************************************************************************
* Per cpu data structure to populate parameters for an SMC in C code and use
* a pointer to this structure in assembler code to populate x0-x7.
******************************************************************************/
static smc_args_t tsp_smc_args[PLATFORM_CORE_COUNT];
/*******************************************************************************
* Per cpu data structure to keep track of TSP activity
******************************************************************************/
work_statistics_t tsp_stats[PLATFORM_CORE_COUNT];
smc_args_t *set_smc_args(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id;
smc_args_t *pcpu_smc_args;
/*
* Return to Secure Monitor by raising an SMC. The results of the
* service are passed as an arguments to the SMC.
*/
linear_id = plat_my_core_pos();
pcpu_smc_args = &tsp_smc_args[linear_id];
write_sp_arg(pcpu_smc_args, SMC_ARG0, arg0);
write_sp_arg(pcpu_smc_args, SMC_ARG1, arg1);
write_sp_arg(pcpu_smc_args, SMC_ARG2, arg2);
write_sp_arg(pcpu_smc_args, SMC_ARG3, arg3);
write_sp_arg(pcpu_smc_args, SMC_ARG4, arg4);
write_sp_arg(pcpu_smc_args, SMC_ARG5, arg5);
write_sp_arg(pcpu_smc_args, SMC_ARG6, arg6);
write_sp_arg(pcpu_smc_args, SMC_ARG7, arg7);
return pcpu_smc_args;
}
/*******************************************************************************
* Setup function for TSP.
******************************************************************************/
void tsp_setup(void)
{
/* Perform early platform-specific setup. */
tsp_early_platform_setup();
/* Perform late platform-specific setup. */
tsp_plat_arch_setup();
#if ENABLE_PAUTH
/*
* Assert that the ARMv8.3-PAuth registers are present or an access
* fault will be triggered when they are being saved or restored.
*/
assert(is_armv8_3_pauth_present());
#endif /* ENABLE_PAUTH */
}
/*******************************************************************************
* This function performs any remaining bookkeeping in the test secure payload
* before the system is switched off (in response to a psci SYSTEM_OFF request).
******************************************************************************/
smc_args_t *tsp_system_off_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
INFO("TSP: cpu 0x%lx SYSTEM_OFF request\n", read_mpidr());
INFO("TSP: cpu 0x%lx: %d smcs, %d erets requests\n", read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count);
/* Indicate to the SPD that we have completed this request. */
return set_smc_args(TSP_SYSTEM_OFF_DONE, 0, 0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
* This function performs any remaining bookkeeping in the test secure payload
* before the system is reset (in response to a psci SYSTEM_RESET request).
******************************************************************************/
smc_args_t *tsp_system_reset_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
INFO("TSP: cpu 0x%lx SYSTEM_RESET request\n", read_mpidr());
INFO("TSP: cpu 0x%lx: %d smcs, %d erets requests\n", read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count);
/* Indicate to the SPD that we have completed this request. */
return set_smc_args(TSP_SYSTEM_RESET_DONE, 0, 0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
* TSP smc abort handler. This function is called when aborting a preempted
* yielding SMC request. It should cleanup all resources owned by the SMC
* handler such as locks or dynamically allocated memory so following SMC
* request are executed in a clean environment.
******************************************************************************/
smc_args_t *tsp_abort_smc_handler(uint64_t func,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
return set_smc_args(TSP_ABORT_DONE, 0, 0, 0, 0, 0, 0, 0);
}

656
bl32/tsp/tsp_ffa_main.c Normal file
View File

@ -0,0 +1,656 @@
/*
* Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include "../../services/std_svc/spm/el3_spmc/spmc.h"
#include "../../services/std_svc/spm/el3_spmc/spmc_shared_mem.h"
#include <arch_features.h>
#include <arch_helpers.h>
#include <bl32/tsp/tsp.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include "ffa_helpers.h"
#include <lib/psci/psci.h>
#include <lib/spinlock.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <plat/common/platform.h>
#include <platform_tsp.h>
#include <services/ffa_svc.h>
#include "tsp_private.h"
#include <platform_def.h>
static ffa_endpoint_id16_t tsp_id, spmc_id;
uint8_t mem_region_buffer[4096 * 2] __aligned(PAGE_SIZE);
/* Partition Mailbox. */
static uint8_t send_page[PAGE_SIZE] __aligned(PAGE_SIZE);
static uint8_t recv_page[PAGE_SIZE] __aligned(PAGE_SIZE);
/*
* Declare a global mailbox for use within the TSP.
* This will be initialized appropriately when the buffers
* are mapped with the SPMC.
*/
static struct mailbox mailbox;
/*******************************************************************************
* This enum is used to handle test cases driven from the FF-A Test Driver.
******************************************************************************/
/* Keep in Sync with FF-A Test Driver. */
enum message_t {
/* Partition Only Messages. */
FF_A_RELAY_MESSAGE = 0,
/* Basic Functionality. */
FF_A_ECHO_MESSAGE,
FF_A_RELAY_MESSAGE_EL3,
/* Memory Sharing. */
FF_A_MEMORY_SHARE,
FF_A_MEMORY_SHARE_FRAGMENTED,
FF_A_MEMORY_LEND,
FF_A_MEMORY_LEND_FRAGMENTED,
FF_A_MEMORY_SHARE_MULTI_ENDPOINT,
FF_A_MEMORY_LEND_MULTI_ENDPOINT,
LAST,
FF_A_RUN_ALL = 255,
FF_A_OP_MAX = 256
};
#if SPMC_AT_EL3
extern void tsp_cpu_on_entry(void);
#endif
/*******************************************************************************
* Test Functions.
******************************************************************************/
/*******************************************************************************
* Enable the TSP to forward the received message to another partition and ask
* it to echo the value back in order to validate direct messages functionality.
******************************************************************************/
static int ffa_test_relay(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
smc_args_t ffa_forward_result;
ffa_endpoint_id16_t receiver = arg5;
ffa_forward_result = ffa_msg_send_direct_req(tsp_id,
receiver,
FF_A_ECHO_MESSAGE, arg4,
0, 0, 0);
return ffa_forward_result._regs[3];
}
/*******************************************************************************
* This function handles memory management tests, currently share and lend.
* This test supports the use of FRAG_RX to use memory descriptors that do not
* fit in a single 4KB buffer.
******************************************************************************/
static int test_memory_send(ffa_endpoint_id16_t sender, uint64_t handle,
ffa_mtd_flag32_t flags, bool multi_endpoint)
{
struct ffa_mtd *m;
struct ffa_emad_v1_0 *receivers;
struct ffa_comp_mrd *composite;
int ret, status = 0;
unsigned int mem_attrs;
char *ptr;
ffa_endpoint_id16_t source = sender;
uint32_t total_length, recv_length = 0;
/*
* In the case that we're testing multiple endpoints choose a partition
* ID that resides in the normal world so the SPMC won't detect it as
* invalid.
* TODO: Should get endpoint receiver id and flag as input from NWd.
*/
uint32_t receiver_count = multi_endpoint ? 2 : 1;
ffa_endpoint_id16_t test_receivers[2] = { tsp_id, 0x10 };
/* Ensure that the sender ID resides in the normal world. */
if (ffa_is_secure_world_id(sender)) {
ERROR("Invalid sender ID 0x%x.\n", sender);
return FFA_ERROR_DENIED;
}
if (!memory_retrieve(&mailbox, &m, handle, source, test_receivers,
receiver_count, flags, &recv_length,
&total_length)) {
return FFA_ERROR_INVALID_PARAMETER;
}
receivers = (struct ffa_emad_v1_0 *)
((uint8_t *) m + m->emad_offset);
while (total_length != recv_length) {
smc_args_t ffa_return;
uint32_t frag_length;
ffa_return = ffa_mem_frag_rx(handle, recv_length);
if (ffa_return._regs[0] == FFA_ERROR) {
WARN("TSP: failed to resume mem with handle %lx\n",
handle);
return ffa_return._regs[2];
}
frag_length = ffa_return._regs[3];
/* Validate frag_length is less than total_length and mailbox size. */
if (frag_length > total_length ||
frag_length > (mailbox.rxtx_page_count * PAGE_SIZE)) {
ERROR("Invalid parameters!\n");
return FFA_ERROR_INVALID_PARAMETER;
}
/* Validate frag_length is less than remaining mem_region_buffer size. */
if (frag_length + recv_length >= REGION_BUF_SIZE) {
ERROR("Out of memory!\n");
return FFA_ERROR_INVALID_PARAMETER;
}
memcpy(&mem_region_buffer[recv_length], mailbox.rx_buffer,
frag_length);
if (ffa_rx_release()) {
ERROR("Failed to release buffer!\n");
return FFA_ERROR_DENIED;
}
recv_length += frag_length;
assert(recv_length <= total_length);
}
composite = ffa_memory_region_get_composite(m, 0);
if (composite == NULL) {
WARN("Failed to get composite descriptor!\n");
return FFA_ERROR_INVALID_PARAMETER;
}
VERBOSE("Address: %p; page_count: %x %lx\n",
(void *)composite->address_range_array[0].address,
composite->address_range_array[0].page_count, PAGE_SIZE);
/* This test is only concerned with RW permissions. */
if (ffa_get_data_access_attr(
receivers[0].mapd.memory_access_permissions) != FFA_MEM_PERM_RW) {
ERROR("Data permission in retrieve response %x does not match share/lend %x!\n",
ffa_get_data_access_attr(receivers[0].mapd.memory_access_permissions),
FFA_MEM_PERM_RW);
return FFA_ERROR_INVALID_PARAMETER;
}
mem_attrs = MT_RW_DATA | MT_EXECUTE_NEVER;
/* Only expecting to be sent memory from NWd so map accordingly. */
mem_attrs |= MT_NS;
for (int32_t i = 0; i < (int32_t)composite->address_range_count; i++) {
size_t size = composite->address_range_array[i].page_count * PAGE_SIZE;
ptr = (char *) composite->address_range_array[i].address;
ret = mmap_add_dynamic_region(
(uint64_t)ptr,
(uint64_t)ptr,
size, mem_attrs);
if (ret != 0) {
ERROR("Failed [%d] mmap_add_dynamic_region %u (%lx) (%lx) (%x)!\n",
i, ret,
(uint64_t)composite->address_range_array[i].address,
size, mem_attrs);
/* Remove mappings previously created in this transaction. */
for (i--; i >= 0; i--) {
ret = mmap_remove_dynamic_region(
(uint64_t)composite->address_range_array[i].address,
composite->address_range_array[i].page_count * PAGE_SIZE);
if (ret != 0) {
ERROR("Failed [%d] mmap_remove_dynamic_region!\n", i);
panic();
}
}
return FFA_ERROR_NO_MEMORY;
}
/* Increment memory region for validation purposes. */
++(*ptr);
/*
* Read initial magic number from memory region for
* validation purposes.
*/
if (!i) {
status = *ptr;
}
}
for (uint32_t i = 0U; i < composite->address_range_count; i++) {
ret = mmap_remove_dynamic_region(
(uint64_t)composite->address_range_array[i].address,
composite->address_range_array[i].page_count * PAGE_SIZE);
if (ret != 0) {
ERROR("Failed [%d] mmap_remove_dynamic_region!\n", i);
return FFA_ERROR_NO_MEMORY;
}
}
if (!memory_relinquish((struct ffa_mem_relinquish_descriptor *)mailbox.tx_buffer,
m->handle, tsp_id)) {
ERROR("Failed to relinquish memory region!\n");
return FFA_ERROR_INVALID_PARAMETER;
}
return status;
}
static smc_args_t *send_ffa_pm_success(void)
{
return set_smc_args(FFA_MSG_SEND_DIRECT_RESP_SMC32,
((tsp_id & FFA_DIRECT_MSG_ENDPOINT_ID_MASK)
<< FFA_DIRECT_MSG_SOURCE_SHIFT) | spmc_id,
FFA_FWK_MSG_BIT |
(FFA_PM_MSG_PM_RESP & FFA_FWK_MSG_MASK),
0, 0, 0, 0, 0);
}
/*******************************************************************************
* This function performs any remaining book keeping in the test secure payload
* before this cpu is turned off in response to a psci cpu_off request.
******************************************************************************/
smc_args_t *tsp_cpu_off_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/*
* This cpu is being turned off, so disable the timer to prevent the
* secure timer interrupt from interfering with power down. A pending
* interrupt will be lost but we do not care as we are turning off.
*/
tsp_generic_timer_stop();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_off_count++;
VERBOSE("TSP: cpu 0x%lx off request\n", read_mpidr());
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu off requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_off_count);
return send_ffa_pm_success();
}
/*******************************************************************************
* This function performs any book keeping in the test secure payload before
* this cpu's architectural state is saved in response to an earlier psci
* cpu_suspend request.
******************************************************************************/
smc_args_t *tsp_cpu_suspend_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/*
* Save the time context and disable it to prevent the secure timer
* interrupt from interfering with wakeup from the suspend state.
*/
tsp_generic_timer_save();
tsp_generic_timer_stop();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_suspend_count++;
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu suspend requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_suspend_count);
return send_ffa_pm_success();
}
/*******************************************************************************
* This function performs any bookkeeping in the test secure payload after this
* cpu's architectural state has been restored after wakeup from an earlier psci
* cpu_suspend request.
******************************************************************************/
smc_args_t *tsp_cpu_resume_main(uint64_t max_off_pwrlvl,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/* Restore the generic timer context. */
tsp_generic_timer_restore();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_resume_count++;
VERBOSE("TSP: cpu 0x%lx resumed. maximum off power level %" PRId64 "\n",
read_mpidr(), max_off_pwrlvl);
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu resume requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_resume_count);
return send_ffa_pm_success();
}
/*******************************************************************************
* This function handles framework messages. Currently only PM.
******************************************************************************/
static smc_args_t *handle_framework_message(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
/* Check if it is a power management message from the SPMC. */
if (ffa_endpoint_source(arg1) != spmc_id) {
goto err;
}
/* Check if it is a PM request message. */
if ((arg2 & FFA_FWK_MSG_MASK) == FFA_FWK_MSG_PSCI) {
/* Check if it is a PSCI CPU_OFF request. */
if (arg3 == PSCI_CPU_OFF) {
return tsp_cpu_off_main(arg0, arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
} else if (arg3 == PSCI_CPU_SUSPEND_AARCH64) {
return tsp_cpu_suspend_main(arg0, arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
}
} else if ((arg2 & FFA_FWK_MSG_MASK) == FFA_PM_MSG_WB_REQ) {
/* Check it is a PSCI Warm Boot request. */
if (arg3 == FFA_WB_TYPE_NOTS2RAM) {
return tsp_cpu_resume_main(arg0, arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
}
}
err:
ERROR("%s: Unknown framework message!\n", __func__);
panic();
}
/*******************************************************************************
* Handles partition messages. Exercised from the FF-A Test Driver.
******************************************************************************/
static smc_args_t *handle_partition_message(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint16_t sender = ffa_endpoint_source(arg1);
uint16_t receiver = ffa_endpoint_destination(arg1);
int status = -1;
const bool multi_endpoint = true;
switch (arg3) {
case FF_A_MEMORY_SHARE:
INFO("TSP Tests: Memory Share Request--\n");
status = test_memory_send(sender, arg4, FFA_FLAG_SHARE_MEMORY, !multi_endpoint);
break;
case FF_A_MEMORY_LEND:
INFO("TSP Tests: Memory Lend Request--\n");
status = test_memory_send(sender, arg4, FFA_FLAG_LEND_MEMORY, !multi_endpoint);
break;
case FF_A_MEMORY_SHARE_MULTI_ENDPOINT:
INFO("TSP Tests: Multi Endpoint Memory Share Request--\n");
status = test_memory_send(sender, arg4, FFA_FLAG_SHARE_MEMORY, multi_endpoint);
break;
case FF_A_MEMORY_LEND_MULTI_ENDPOINT:
INFO("TSP Tests: Multi Endpoint Memory Lend Request--\n");
status = test_memory_send(sender, arg4, FFA_FLAG_LEND_MEMORY, multi_endpoint);
break;
case FF_A_RELAY_MESSAGE:
INFO("TSP Tests: Relaying message--\n");
status = ffa_test_relay(arg0, arg1, arg2, arg3, arg4,
arg5, arg6, arg7);
break;
case FF_A_ECHO_MESSAGE:
INFO("TSP Tests: echo message--\n");
status = arg4;
break;
default:
INFO("TSP Tests: Unknown request ID %d--\n", (int) arg3);
}
/* Swap the sender and receiver in the response. */
return ffa_msg_send_direct_resp(receiver, sender, status, 0, 0, 0, 0);
}
/*******************************************************************************
* This function implements the event loop for handling FF-A ABI invocations.
******************************************************************************/
static smc_args_t *tsp_event_loop(uint64_t smc_fid,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
/* Panic if the SPMC did not forward an FF-A call. */
if (!is_ffa_fid(smc_fid)) {
ERROR("%s: Unknown SMC FID (0x%lx)\n", __func__, smc_fid);
panic();
}
switch (smc_fid) {
case FFA_INTERRUPT:
/*
* IRQs were enabled upon re-entry into the TSP. The interrupt
* must have been handled by now. Return to the SPMC indicating
* the same.
*/
return set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0);
case FFA_MSG_SEND_DIRECT_REQ_SMC64:
case FFA_MSG_SEND_DIRECT_REQ_SMC32:
/* Check if a framework message, handle accordingly. */
if ((arg2 & FFA_FWK_MSG_BIT)) {
return handle_framework_message(smc_fid, arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
}
return handle_partition_message(smc_fid, arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
}
ERROR("%s: Unsupported FF-A FID (0x%lx)\n", __func__, smc_fid);
panic();
}
static smc_args_t *tsp_loop(smc_args_t *args)
{
smc_args_t ret;
do {
/* --------------------------------------------
* Mask FIQ interrupts to avoid preemption
* in case EL3 SPMC delegates an IRQ next or a
* managed exit. Lastly, unmask IRQs so that
* they can be handled immediately upon re-entry.
* ---------------------------------------------
*/
write_daifset(DAIF_FIQ_BIT);
write_daifclr(DAIF_IRQ_BIT);
ret = smc_helper(args->_regs[0], args->_regs[1], args->_regs[2],
args->_regs[3], args->_regs[4], args->_regs[5],
args->_regs[6], args->_regs[7]);
args = tsp_event_loop(ret._regs[0], ret._regs[1], ret._regs[2],
ret._regs[3], ret._regs[4], ret._regs[5],
ret._regs[6], ret._regs[7]);
} while (1);
/* Not Reached. */
return NULL;
}
/*******************************************************************************
* TSP main entry point where it gets the opportunity to initialize its secure
* state/applications. Once the state is initialized, it must return to the
* SPD with a pointer to the 'tsp_vector_table' jump table.
******************************************************************************/
uint64_t tsp_main(void)
{
smc_args_t smc_args = {0};
NOTICE("TSP: %s\n", version_string);
NOTICE("TSP: %s\n", build_message);
INFO("TSP: Total memory base : 0x%lx\n", (unsigned long) BL32_BASE);
INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_SIZE);
uint32_t linear_id = plat_my_core_pos();
/* Initialize the platform. */
tsp_platform_setup();
/* Initialize secure/applications state here. */
tsp_generic_timer_start();
/* Register secondary entrypoint with the SPMC. */
smc_args = smc_helper(FFA_SECONDARY_EP_REGISTER_SMC64,
(uint64_t) tsp_cpu_on_entry,
0, 0, 0, 0, 0, 0);
if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) {
ERROR("TSP could not register secondary ep (0x%lx)\n",
smc_args._regs[2]);
panic();
}
/* Get TSP's endpoint id. */
smc_args = smc_helper(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0);
if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) {
ERROR("TSP could not get own ID (0x%lx) on core%d\n",
smc_args._regs[2], linear_id);
panic();
}
tsp_id = smc_args._regs[2];
INFO("TSP FF-A endpoint id = 0x%x\n", tsp_id);
/* Get the SPMC ID. */
smc_args = smc_helper(FFA_SPM_ID_GET, 0, 0, 0, 0, 0, 0, 0);
if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) {
ERROR("TSP could not get SPMC ID (0x%lx) on core%d\n",
smc_args._regs[2], linear_id);
panic();
}
spmc_id = smc_args._regs[2];
/* Call RXTX_MAP to map a 4k RX and TX buffer. */
if (ffa_rxtx_map((uintptr_t) send_page,
(uintptr_t) recv_page, 1)) {
ERROR("TSP could not map it's RX/TX Buffers\n");
panic();
}
mailbox.tx_buffer = send_page;
mailbox.rx_buffer = recv_page;
mailbox.rxtx_page_count = 1;
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_on_count++;
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_on_count);
/* Tell SPMD that we are done initialising. */
tsp_loop(set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0));
/* Not reached. */
return 0;
}
/*******************************************************************************
* This function performs any remaining book keeping in the test secure payload
* after this cpu's architectural state has been setup in response to an earlier
* psci cpu_on request.
******************************************************************************/
smc_args_t *tsp_cpu_on_main(void)
{
uint32_t linear_id = plat_my_core_pos();
/* Initialize secure/applications state here. */
tsp_generic_timer_start();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_on_count++;
VERBOSE("TSP: cpu 0x%lx turned on\n", read_mpidr());
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_on_count);
/* ---------------------------------------------
* Jump to the main event loop to return to EL3
* and be ready for the next request on this cpu.
* ---------------------------------------------
*/
return tsp_loop(set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0));
}

115
bl32/tsp/tsp_interrupt.c Normal file
View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2014-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <inttypes.h>
#include <platform_def.h>
#include <arch_helpers.h>
#include <bl32/tsp/tsp.h>
#include <common/debug.h>
#include <plat/common/platform.h>
#include "tsp_private.h"
/*******************************************************************************
* This function updates the TSP statistics for S-EL1 interrupts handled
* synchronously i.e the ones that have been handed over by the TSPD. It also
* keeps count of the number of times control was passed back to the TSPD
* after handling the interrupt. In the future it will be possible that the
* TSPD hands over an S-EL1 interrupt to the TSP but does not expect it to
* return execution. This statistic will be useful to distinguish between these
* two models of synchronous S-EL1 interrupt handling. The 'elr_el3' parameter
* contains the address of the instruction in normal world where this S-EL1
* interrupt was generated.
******************************************************************************/
void tsp_update_sync_sel1_intr_stats(uint32_t type, uint64_t elr_el3)
{
uint32_t linear_id = plat_my_core_pos();
tsp_stats[linear_id].sync_sel1_intr_count++;
if (type == TSP_HANDLE_SEL1_INTR_AND_RETURN)
tsp_stats[linear_id].sync_sel1_intr_ret_count++;
VERBOSE("TSP: cpu 0x%lx sync s-el1 interrupt request from 0x%" PRIx64 "\n",
read_mpidr(), elr_el3);
VERBOSE("TSP: cpu 0x%lx: %d sync s-el1 interrupt requests,"
" %d sync s-el1 interrupt returns\n",
read_mpidr(),
tsp_stats[linear_id].sync_sel1_intr_count,
tsp_stats[linear_id].sync_sel1_intr_ret_count);
}
/******************************************************************************
* This function is invoked when a non S-EL1 interrupt is received and causes
* the preemption of TSP. This function returns TSP_PREEMPTED and results
* in the control being handed over to EL3 for handling the interrupt.
*****************************************************************************/
int32_t tsp_handle_preemption(void)
{
uint32_t linear_id = plat_my_core_pos();
tsp_stats[linear_id].preempt_intr_count++;
VERBOSE("TSP: cpu 0x%lx: %d preempt interrupt requests\n",
read_mpidr(), tsp_stats[linear_id].preempt_intr_count);
return TSP_PREEMPTED;
}
/*******************************************************************************
* TSP interrupt handler is called as a part of both synchronous and
* asynchronous handling of TSP interrupts. Currently the physical timer
* interrupt is the only S-EL1 interrupt that this handler expects. It returns
* 0 upon successfully handling the expected interrupt and all other
* interrupts are treated as normal world or EL3 interrupts.
******************************************************************************/
int32_t tsp_common_int_handler(void)
{
uint32_t linear_id = plat_my_core_pos(), id;
/*
* Get the highest priority pending interrupt id and see if it is the
* secure physical generic timer interrupt in which case, handle it.
* Otherwise throw this interrupt at the EL3 firmware.
*
* There is a small time window between reading the highest priority
* pending interrupt and acknowledging it during which another
* interrupt of higher priority could become the highest pending
* interrupt. This is not expected to happen currently for TSP.
*/
id = plat_ic_get_pending_interrupt_id();
/* TSP can only handle the secure physical timer interrupt */
if (id != TSP_IRQ_SEC_PHY_TIMER) {
#if SPMC_AT_EL3
/*
* With the EL3 FF-A SPMC we expect only Timer secure interrupt to fire in
* the TSP, so panic if any other interrupt does.
*/
ERROR("Unexpected interrupt id %u\n", id);
panic();
#else
return tsp_handle_preemption();
#endif
}
/*
* Acknowledge and handle the secure timer interrupt. Also sanity check
* if it has been preempted by another interrupt through an assertion.
*/
id = plat_ic_acknowledge_interrupt();
assert(id == TSP_IRQ_SEC_PHY_TIMER);
tsp_generic_timer_handler();
plat_ic_end_of_interrupt(id);
/* Update the statistics and print some messages */
tsp_stats[linear_id].sel1_intr_count++;
VERBOSE("TSP: cpu 0x%lx handled S-EL1 interrupt %d\n",
read_mpidr(), id);
VERBOSE("TSP: cpu 0x%lx: %d S-EL1 requests\n",
read_mpidr(), tsp_stats[linear_id].sel1_intr_count);
return 0;
}

288
bl32/tsp/tsp_main.c Normal file
View File

@ -0,0 +1,288 @@
/*
* Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <arch_features.h>
#include <arch_helpers.h>
#include <bl32/tsp/tsp.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <lib/spinlock.h>
#include <plat/common/platform.h>
#include <platform_tsp.h>
#include "tsp_private.h"
#include <platform_def.h>
/*******************************************************************************
* TSP main entry point where it gets the opportunity to initialize its secure
* state/applications. Once the state is initialized, it must return to the
* SPD with a pointer to the 'tsp_vector_table' jump table.
******************************************************************************/
uint64_t tsp_main(void)
{
NOTICE("TSP: %s\n", version_string);
NOTICE("TSP: %s\n", build_message);
INFO("TSP: Total memory base : 0x%lx\n", (unsigned long) BL32_BASE);
INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_SIZE);
uint32_t linear_id = plat_my_core_pos();
/* Initialize the platform */
tsp_platform_setup();
/* Initialize secure/applications state here */
tsp_generic_timer_start();
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_on_count++;
INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_on_count);
console_flush();
return (uint64_t) &tsp_vector_table;
}
/*******************************************************************************
* This function performs any remaining book keeping in the test secure payload
* after this cpu's architectural state has been setup in response to an earlier
* psci cpu_on request.
******************************************************************************/
smc_args_t *tsp_cpu_on_main(void)
{
uint32_t linear_id = plat_my_core_pos();
/* Initialize secure/applications state here */
tsp_generic_timer_start();
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_on_count++;
INFO("TSP: cpu 0x%lx turned on\n", read_mpidr());
INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_on_count);
/* Indicate to the SPD that we have completed turned ourselves on */
return set_smc_args(TSP_ON_DONE, 0, 0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
* This function performs any remaining book keeping in the test secure payload
* before this cpu is turned off in response to a psci cpu_off request.
******************************************************************************/
smc_args_t *tsp_cpu_off_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/*
* This cpu is being turned off, so disable the timer to prevent the
* secure timer interrupt from interfering with power down. A pending
* interrupt will be lost but we do not care as we are turning off.
*/
tsp_generic_timer_stop();
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_off_count++;
INFO("TSP: cpu 0x%lx off request\n", read_mpidr());
INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu off requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_off_count);
/* Indicate to the SPD that we have completed this request */
return set_smc_args(TSP_OFF_DONE, 0, 0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
* This function performs any book keeping in the test secure payload before
* this cpu's architectural state is saved in response to an earlier psci
* cpu_suspend request.
******************************************************************************/
smc_args_t *tsp_cpu_suspend_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/*
* Save the time context and disable it to prevent the secure timer
* interrupt from interfering with wakeup from the suspend state.
*/
tsp_generic_timer_save();
tsp_generic_timer_stop();
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_suspend_count++;
INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu suspend requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_suspend_count);
/* Indicate to the SPD that we have completed this request */
return set_smc_args(TSP_SUSPEND_DONE, 0, 0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
* This function performs any book keeping in the test secure payload after this
* cpu's architectural state has been restored after wakeup from an earlier psci
* cpu_suspend request.
******************************************************************************/
smc_args_t *tsp_cpu_resume_main(uint64_t max_off_pwrlvl,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint32_t linear_id = plat_my_core_pos();
/* Restore the generic timer context */
tsp_generic_timer_restore();
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_resume_count++;
INFO("TSP: cpu 0x%lx resumed. maximum off power level %" PRId64 "\n",
read_mpidr(), max_off_pwrlvl);
INFO("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu resume requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_resume_count);
/* Indicate to the SPD that we have completed this request */
return set_smc_args(TSP_RESUME_DONE, 0, 0, 0, 0, 0, 0, 0);
}
/*******************************************************************************
* TSP fast smc handler. The secure monitor jumps to this function by
* doing the ERET after populating X0-X7 registers. The arguments are received
* in the function arguments in order. Once the service is rendered, this
* function returns to Secure Monitor by raising SMC.
******************************************************************************/
smc_args_t *tsp_smc_handler(uint64_t func,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7)
{
uint128_t service_args;
uint64_t service_arg0;
uint64_t service_arg1;
uint64_t results[2];
uint32_t linear_id = plat_my_core_pos();
u_register_t dit;
/* Update this cpu's statistics */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
INFO("TSP: cpu 0x%lx received %s smc 0x%" PRIx64 "\n", read_mpidr(),
((func >> 31) & 1) == 1 ? "fast" : "yielding",
func);
INFO("TSP: cpu 0x%lx: %d smcs, %d erets\n", read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count);
/* Render secure services and obtain results here */
results[0] = arg1;
results[1] = arg2;
/*
* Request a service back from dispatcher/secure monitor.
* This call returns and thereafter resumes execution.
*/
service_args = tsp_get_magic();
service_arg0 = (uint64_t)service_args;
service_arg1 = (uint64_t)(service_args >> 64U);
#if CTX_INCLUDE_MTE_REGS
/*
* Write a dummy value to an MTE register, to simulate usage in the
* secure world
*/
write_gcr_el1(0x99);
#endif
/* Determine the function to perform based on the function ID */
switch (TSP_BARE_FID(func)) {
case TSP_ADD:
results[0] += service_arg0;
results[1] += service_arg1;
break;
case TSP_SUB:
results[0] -= service_arg0;
results[1] -= service_arg1;
break;
case TSP_MUL:
results[0] *= service_arg0;
results[1] *= service_arg1;
break;
case TSP_DIV:
results[0] /= service_arg0 ? service_arg0 : 1;
results[1] /= service_arg1 ? service_arg1 : 1;
break;
case TSP_CHECK_DIT:
if (!is_feat_dit_supported()) {
ERROR("DIT not supported\n");
results[0] = 0;
results[1] = 0xffff;
break;
}
dit = read_dit();
results[0] = dit == service_arg0;
results[1] = dit;
/* Toggle the dit bit */
write_dit(service_arg0 != 0U ? 0 : DIT_BIT);
break;
default:
break;
}
return set_smc_args(func, 0,
results[0],
results[1],
0, 0, 0, 0);
}

145
bl32/tsp/tsp_private.h Normal file
View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2014-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef TSP_PRIVATE_H
#define TSP_PRIVATE_H
/*******************************************************************************
* The TSP memory footprint starts at address BL32_BASE and ends with the
* linker symbol __BL32_END__. Use these addresses to compute the TSP image
* size.
******************************************************************************/
#define BL32_TOTAL_LIMIT BL32_END
#define BL32_TOTAL_SIZE (BL32_TOTAL_LIMIT - (unsigned long) BL32_BASE)
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <bl32/tsp/tsp.h>
#include <lib/cassert.h>
#include <lib/spinlock.h>
#include <smccc_helpers.h>
typedef struct work_statistics {
/* Number of s-el1 interrupts on this cpu */
uint32_t sel1_intr_count;
/* Number of non s-el1 interrupts on this cpu which preempted TSP */
uint32_t preempt_intr_count;
/* Number of sync s-el1 interrupts on this cpu */
uint32_t sync_sel1_intr_count;
/* Number of s-el1 interrupts returns on this cpu */
uint32_t sync_sel1_intr_ret_count;
uint32_t smc_count; /* Number of returns on this cpu */
uint32_t eret_count; /* Number of entries on this cpu */
uint32_t cpu_on_count; /* Number of cpu on requests */
uint32_t cpu_off_count; /* Number of cpu off requests */
uint32_t cpu_suspend_count; /* Number of cpu suspend requests */
uint32_t cpu_resume_count; /* Number of cpu resume requests */
} __aligned(CACHE_WRITEBACK_GRANULE) work_statistics_t;
/* Macros to access members of the above structure using their offsets */
#define read_sp_arg(args, offset) ((args)->_regs[offset >> 3])
#define write_sp_arg(args, offset, val) (((args)->_regs[offset >> 3]) \
= val)
uint128_t tsp_get_magic(void);
smc_args_t *set_smc_args(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
smc_args_t *tsp_cpu_resume_main(uint64_t max_off_pwrlvl,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
smc_args_t *tsp_cpu_suspend_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
smc_args_t *tsp_cpu_on_main(void);
smc_args_t *tsp_cpu_off_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
/* Generic Timer functions */
void tsp_generic_timer_start(void);
void tsp_generic_timer_handler(void);
void tsp_generic_timer_stop(void);
void tsp_generic_timer_save(void);
void tsp_generic_timer_restore(void);
/* S-EL1 interrupt management functions */
void tsp_update_sync_sel1_intr_stats(uint32_t type, uint64_t elr_el3);
/* Data structure to keep track of TSP statistics */
extern work_statistics_t tsp_stats[PLATFORM_CORE_COUNT];
/* Vector table of jumps */
extern tsp_vectors_t tsp_vector_table;
/* functions */
int32_t tsp_common_int_handler(void);
int32_t tsp_handle_preemption(void);
smc_args_t *tsp_abort_smc_handler(uint64_t func,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
smc_args_t *tsp_smc_handler(uint64_t func,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
smc_args_t *tsp_system_reset_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
smc_args_t *tsp_system_off_main(uint64_t arg0,
uint64_t arg1,
uint64_t arg2,
uint64_t arg3,
uint64_t arg4,
uint64_t arg5,
uint64_t arg6,
uint64_t arg7);
uint64_t tsp_main(void);
#endif /* __ASSEMBLER__ */
#endif /* TSP_PRIVATE_H */

91
bl32/tsp/tsp_timer.c Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <plat/common/platform.h>
#include "tsp_private.h"
/*******************************************************************************
* Data structure to keep track of per-cpu secure generic timer context across
* power management operations.
******************************************************************************/
typedef struct timer_context {
uint64_t cval;
uint32_t ctl;
} timer_context_t;
static timer_context_t pcpu_timer_context[PLATFORM_CORE_COUNT];
/*******************************************************************************
* This function initializes the generic timer to fire every 0.5 second
******************************************************************************/
void tsp_generic_timer_start(void)
{
uint64_t cval;
uint32_t ctl = 0;
/* The timer will fire every 0.5 second */
cval = read_cntpct_el0() + (read_cntfrq_el0() >> 1);
write_cntps_cval_el1(cval);
/* Enable the secure physical timer */
set_cntp_ctl_enable(ctl);
write_cntps_ctl_el1(ctl);
}
/*******************************************************************************
* This function deasserts the timer interrupt and sets it up again
******************************************************************************/
void tsp_generic_timer_handler(void)
{
/* Ensure that the timer did assert the interrupt */
assert(get_cntp_ctl_istatus(read_cntps_ctl_el1()));
/*
* Disable the timer and reprogram it. The barriers ensure that there is
* no reordering of instructions around the reprogramming code.
*/
isb();
write_cntps_ctl_el1(0);
tsp_generic_timer_start();
isb();
}
/*******************************************************************************
* This function deasserts the timer interrupt prior to cpu power down
******************************************************************************/
void tsp_generic_timer_stop(void)
{
/* Disable the timer */
write_cntps_ctl_el1(0);
}
/*******************************************************************************
* This function saves the timer context prior to cpu suspension
******************************************************************************/
void tsp_generic_timer_save(void)
{
uint32_t linear_id = plat_my_core_pos();
pcpu_timer_context[linear_id].cval = read_cntps_cval_el1();
pcpu_timer_context[linear_id].ctl = read_cntps_ctl_el1();
flush_dcache_range((uint64_t) &pcpu_timer_context[linear_id],
sizeof(pcpu_timer_context[linear_id]));
}
/*******************************************************************************
* This function restores the timer context post cpu resumption
******************************************************************************/
void tsp_generic_timer_restore(void)
{
uint32_t linear_id = plat_my_core_pos();
write_cntps_cval_el1(pcpu_timer_context[linear_id].cval);
write_cntps_ctl_el1(pcpu_timer_context[linear_id].ctl);
}

1378
changelog.yaml Normal file

File diff suppressed because it is too large Load Diff

239
common/aarch32/debug.S Normal file
View File

@ -0,0 +1,239 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/debug.h>
.globl asm_print_str
.globl asm_print_hex
.globl asm_print_hex_bits
.globl asm_assert
.globl el3_panic
.globl report_exception
.globl report_prefetch_abort
.globl report_data_abort
/* Since the max decimal input number is 65536 */
#define MAX_DEC_DIVISOR 10000
/* The offset to add to get ascii for numerals '0 - 9' */
#define ASCII_OFFSET_NUM '0'
#if ENABLE_ASSERTIONS
.section .rodata.assert_str, "aS"
assert_msg1:
.asciz "ASSERT: File "
assert_msg2:
#if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION)
/******************************************************************
* Virtualization comes with the UDIV/SDIV instructions. If missing
* write file line number in hexadecimal format.
******************************************************************/
.asciz " Line 0x"
#else
.asciz " Line "
/*
* This macro is intended to be used to print the
* line number in decimal. Used by asm_assert macro.
* The max number expected is 65536.
* In: r4 = the decimal to print.
* Clobber: lr, r0, r1, r2, r5, r6
*/
.macro asm_print_line_dec
mov r6, #10 /* Divide by 10 after every loop iteration */
ldr r5, =MAX_DEC_DIVISOR
dec_print_loop:
udiv r0, r4, r5 /* Get the quotient */
mls r4, r0, r5, r4 /* Find the remainder */
add r0, r0, #ASCII_OFFSET_NUM /* Convert to ascii */
bl plat_crash_console_putc
udiv r5, r5, r6 /* Reduce divisor */
cmp r5, #0
bne dec_print_loop
.endm
#endif
/* ---------------------------------------------------------------------------
* Assertion support in assembly.
* The below function helps to support assertions in assembly where we do not
* have a C runtime stack. Arguments to the function are :
* r0 - File name
* r1 - Line no
* Clobber list : lr, r0 - r6
* ---------------------------------------------------------------------------
*/
func asm_assert
#if LOG_LEVEL >= LOG_LEVEL_INFO
/*
* Only print the output if LOG_LEVEL is higher or equal to
* LOG_LEVEL_INFO, which is the default value for builds with DEBUG=1.
*/
/* Stash the parameters already in r0 and r1 */
mov r5, r0
mov r6, r1
/* Ensure the console is initialized */
bl plat_crash_console_init
/* Check if the console is initialized */
cmp r0, #0
beq _assert_loop
/* The console is initialized */
ldr r4, =assert_msg1
bl asm_print_str
mov r4, r5
bl asm_print_str
ldr r4, =assert_msg2
bl asm_print_str
/* Check if line number higher than max permitted */
ldr r4, =~0xffff
tst r6, r4
bne _assert_loop
mov r4, r6
#if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION)
/******************************************************************
* Virtualization comes with the UDIV/SDIV instructions. If missing
* write file line number in hexadecimal format.
******************************************************************/
bl asm_print_hex
#else
asm_print_line_dec
#endif
bl plat_crash_console_flush
_assert_loop:
#endif /* LOG_LEVEL >= LOG_LEVEL_INFO */
no_ret plat_panic_handler
endfunc asm_assert
#endif /* ENABLE_ASSERTIONS */
/*
* This function prints a string from address in r4
* Clobber: lr, r0 - r4
*/
func asm_print_str
mov r3, lr
1:
ldrb r0, [r4], #0x1
cmp r0, #0
beq 2f
bl plat_crash_console_putc
b 1b
2:
bx r3
endfunc asm_print_str
/*
* This function prints a hexadecimal number in r4.
* In: r4 = the hexadecimal to print.
* Clobber: lr, r0 - r3, r5
*/
func asm_print_hex
mov r5, #32 /* No of bits to convert to ascii */
/* Convert to ascii number of bits in r5 */
asm_print_hex_bits:
mov r3, lr
1:
sub r5, r5, #4
lsr r0, r4, r5
and r0, r0, #0xf
cmp r0, #0xa
blo 2f
/* Add by 0x27 in addition to ASCII_OFFSET_NUM
* to get ascii for characters 'a - f'.
*/
add r0, r0, #0x27
2:
add r0, r0, #ASCII_OFFSET_NUM
bl plat_crash_console_putc
cmp r5, #0
bne 1b
bx r3
endfunc asm_print_hex
/***********************************************************
* The common implementation of el3_panic for all BL stages
***********************************************************/
.section .rodata.panic_str, "aS"
panic_msg: .asciz "PANIC at PC : 0x"
panic_end: .asciz "\r\n"
func el3_panic
/* Have LR copy point to PC at the time of panic */
sub r6, lr, #4
/* Initialize crash console and verify success */
bl plat_crash_console_init
/* Check if the console is initialized */
cmp r0, #0
beq _panic_handler
/* The console is initialized */
ldr r4, =panic_msg
bl asm_print_str
/* Print LR in hex */
mov r4, r6
bl asm_print_hex
/* Print new line */
ldr r4, =panic_end
bl asm_print_str
bl plat_crash_console_flush
_panic_handler:
mov lr, r6
b plat_panic_handler
endfunc el3_panic
/***********************************************************
* This function is called from the vector table for
* unhandled exceptions. It reads the current mode and
* passes it to platform.
***********************************************************/
func report_exception
mrs r0, cpsr
and r0, #MODE32_MASK
bl plat_report_exception
no_ret plat_panic_handler
endfunc report_exception
/***********************************************************
* This function is called from the vector table for
* unhandled exceptions. The lr_abt is given as an
* argument to platform handler.
***********************************************************/
func report_prefetch_abort
#if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION)
b report_exception
#else
mrs r0, lr_abt
bl plat_report_prefetch_abort
no_ret plat_panic_handler
#endif
endfunc report_prefetch_abort
/***********************************************************
* This function is called from the vector table for
* unhandled exceptions. The lr_abt is given as an
* argument to platform handler.
***********************************************************/
func report_data_abort
#if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION)
b report_exception
#else
mrs r0, lr_abt
bl plat_report_data_abort
no_ret plat_panic_handler
#endif
endfunc report_data_abort

202
common/aarch64/debug.S Normal file
View File

@ -0,0 +1,202 @@
/*
* Copyright (c) 2014-2023 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/debug.h>
.globl asm_print_str
.globl asm_print_hex
.globl asm_print_hex_bits
.globl asm_print_newline
.globl asm_assert
.globl el3_panic
.globl elx_panic
/* Since the max decimal input number is 65536 */
#define MAX_DEC_DIVISOR 10000
/* The offset to add to get ascii for numerals '0 - 9' */
#define ASCII_OFFSET_NUM 0x30
#if ENABLE_ASSERTIONS
.section .rodata.assert_str, "aS"
assert_msg1:
.asciz "ASSERT: File "
assert_msg2:
.asciz " Line "
/*
* This macro is intended to be used to print the
* line number in decimal. Used by asm_assert macro.
* The max number expected is 65536.
* In: x4 = the decimal to print.
* Clobber: x30, x0, x1, x2, x5, x6
*/
.macro asm_print_line_dec
mov x6, #10 /* Divide by 10 after every loop iteration */
mov x5, #MAX_DEC_DIVISOR
dec_print_loop:
udiv x0, x4, x5 /* Get the quotient */
msub x4, x0, x5, x4 /* Find the remainder */
add x0, x0, #ASCII_OFFSET_NUM /* Convert to ascii */
bl plat_crash_console_putc
udiv x5, x5, x6 /* Reduce divisor */
cbnz x5, dec_print_loop
.endm
/* ---------------------------------------------------------------------------
* Assertion support in assembly.
* The below function helps to support assertions in assembly where we do not
* have a C runtime stack. Arguments to the function are :
* x0 - File name
* x1 - Line no
* Clobber list : x30, x0, x1, x2, x3, x4, x5, x6.
* ---------------------------------------------------------------------------
*/
func asm_assert
#if LOG_LEVEL >= LOG_LEVEL_INFO
/*
* Only print the output if LOG_LEVEL is higher or equal to
* LOG_LEVEL_INFO, which is the default value for builds with DEBUG=1.
*/
mov x5, x0
mov x6, x1
/* Ensure the console is initialized */
bl plat_crash_console_init
/* Check if the console is initialized */
cbz x0, _assert_loop
/* The console is initialized */
adr x4, assert_msg1
bl asm_print_str
mov x4, x5
bl asm_print_str
adr x4, assert_msg2
bl asm_print_str
/* Check if line number higher than max permitted */
tst x6, #~0xffff
b.ne _assert_loop
mov x4, x6
asm_print_line_dec
bl plat_crash_console_flush
_assert_loop:
#endif /* LOG_LEVEL >= LOG_LEVEL_INFO */
no_ret plat_panic_handler
endfunc asm_assert
#endif /* ENABLE_ASSERTIONS */
/*
* This function prints a string from address in x4.
* In: x4 = pointer to string.
* Clobber: x30, x0, x1, x2, x3
*/
func asm_print_str
mov x3, x30
1:
ldrb w0, [x4], #0x1
cbz x0, 2f
bl plat_crash_console_putc
b 1b
2:
ret x3
endfunc asm_print_str
/*
* This function prints a hexadecimal number in x4.
* In: x4 = the hexadecimal to print.
* Clobber: x30, x0 - x3, x5
*/
func asm_print_hex
mov x5, #64 /* No of bits to convert to ascii */
/* Convert to ascii number of bits in x5 */
asm_print_hex_bits:
mov x3, x30
1:
sub x5, x5, #4
lsrv x0, x4, x5
and x0, x0, #0xf
cmp x0, #0xA
b.lo 2f
/* Add by 0x27 in addition to ASCII_OFFSET_NUM
* to get ascii for characters 'a - f'.
*/
add x0, x0, #0x27
2:
add x0, x0, #ASCII_OFFSET_NUM
bl plat_crash_console_putc
cbnz x5, 1b
ret x3
endfunc asm_print_hex
/*
* Helper function to print newline to console
* Clobber: x0
*/
func asm_print_newline
mov x0, '\n'
b plat_crash_console_putc
endfunc asm_print_newline
/***********************************************************
* The common implementation of el3_panic for all BL stages
***********************************************************/
.section .rodata.panic_str, "aS"
panic_msg: .asciz "PANIC at PC : 0x"
func elx_panic
#if CRASH_REPORTING && defined(IMAGE_BL31)
b report_elx_panic
#endif /* CRASH_REPORTING && IMAGE_BL31 */
b panic_common
endfunc elx_panic
/* ---------------------------------------------------------------------------
* el3_panic assumes that it is invoked from a C Runtime Environment ie a
* valid stack exists. This call will not return.
* Clobber list : if CRASH_REPORTING is not enabled then x30, x0 - x6
* ---------------------------------------------------------------------------
*/
func el3_panic
#if CRASH_REPORTING && defined(IMAGE_BL31)
b report_el3_panic
#endif /* CRASH_REPORTING && IMAGE_BL31 */
panic_common:
mov x6, x30
bl plat_crash_console_init
/* Check if the console is initialized */
cbz x0, _panic_handler
/* The console is initialized */
adr x4, panic_msg
bl asm_print_str
mov x4, x6
/* The panic location is lr -4 */
sub x4, x4, #4
bl asm_print_hex
/* Print new line */
bl asm_print_newline
bl plat_crash_console_flush
_panic_handler:
/* Pass to plat_panic_handler the address from where el3_panic was
* called, not the address of the call from el3_panic. */
mov x30, x6
b plat_panic_handler
endfunc el3_panic

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
#include <common/bl_common.h>
/* -----------------------------------------------------------------------------
* Very simple stackless exception handlers used by BL2 and BL31 stages.
* BL31 uses them before stacks are setup. BL2 uses them throughout.
* -----------------------------------------------------------------------------
*/
.globl early_exceptions
vector_base early_exceptions
/* -----------------------------------------------------
* Current EL with SP0 : 0x0 - 0x200
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSP0
mov x0, #SYNC_EXCEPTION_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSP0
vector_entry IrqSP0
mov x0, #IRQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSP0
vector_entry FiqSP0
mov x0, #FIQ_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSP0
vector_entry SErrorSP0
mov x0, #SERROR_SP_EL0
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSP0
/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x400
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSPx
mov x0, #SYNC_EXCEPTION_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionSPx
vector_entry IrqSPx
mov x0, #IRQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqSPx
vector_entry FiqSPx
mov x0, #FIQ_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqSPx
vector_entry SErrorSPx
mov x0, #SERROR_SP_ELX
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorSPx
/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA64
mov x0, #SYNC_EXCEPTION_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionA64
vector_entry IrqA64
mov x0, #IRQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA64
vector_entry FiqA64
mov x0, #FIQ_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA64
vector_entry SErrorA64
mov x0, #SERROR_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA64
/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA32
mov x0, #SYNC_EXCEPTION_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SynchronousExceptionA32
vector_entry IrqA32
mov x0, #IRQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry IrqA32
vector_entry FiqA32
mov x0, #FIQ_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry FiqA32
vector_entry SErrorA32
mov x0, #SERROR_AARCH32
bl plat_report_exception
no_ret plat_panic_handler
end_vector_entry SErrorA32

View File

@ -0,0 +1,266 @@
/*
* Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/console.h>
/* Maximum number of entries in the backtrace to display */
#define UNWIND_LIMIT 20U
/*
* If -fno-omit-frame-pointer is used:
*
* - AArch64: The AAPCS defines the format of the frame records and mandates the
* usage of r29 as frame pointer.
*
* - AArch32: The format of the frame records is not defined in the AAPCS.
* However, at least GCC and Clang use the same format. When they are forced
* to only generate A32 code (with -marm), they use r11 as frame pointer and a
* similar format as in AArch64. If interworking with T32 is enabled, the
* frame pointer is r7 and the format is different. This is not supported by
* this implementation of backtrace, so it is needed to use -marm.
*/
/* Frame records form a linked list in the stack */
struct frame_record {
/* Previous frame record in the list */
struct frame_record *parent;
/* Return address of the function at this level */
uintptr_t return_addr;
};
static inline uintptr_t extract_address(uintptr_t address)
{
uintptr_t ret = address;
#if ENABLE_PAUTH
/*
* When pointer authentication is enabled, the LR value saved on the
* stack contains a PAC. It must be stripped to retrieve the return
* address.
*/
xpaci(ret);
#endif
return ret;
}
/*
* Returns true if the address points to a virtual address that can be read at
* the current EL, false otherwise.
*/
#ifdef __aarch64__
static bool is_address_readable(uintptr_t address)
{
unsigned int el = get_current_el();
uintptr_t addr = extract_address(address);
if (el == 3U) {
ats1e3r(addr);
} else if (el == 2U) {
ats1e2r(addr);
} else {
AT(ats1e1r, addr);
}
isb();
/* If PAR.F == 1 the address translation was aborted. */
if ((read_par_el1() & PAR_F_MASK) != 0U)
return false;
return true;
}
#else /* !__aarch64__ */
static bool is_address_readable(uintptr_t addr)
{
unsigned int el = get_current_el();
if (el == 3U) {
write_ats1cpr(addr);
} else if (el == 2U) {
write_ats1hr(addr);
} else {
write_ats1cpr(addr);
}
isb();
/* If PAR.F == 1 the address translation was aborted. */
if ((read64_par() & PAR_F_MASK) != 0U)
return false;
return true;
}
#endif /* __aarch64__ */
/*
* Returns true if all the bytes in a given object are in mapped memory and an
* LDR using this pointer would succeed, false otherwise.
*/
static bool is_valid_object(uintptr_t addr, size_t size)
{
assert(size > 0U);
if (addr == 0U)
return false;
/* Detect overflows */
if ((addr + size) < addr)
return false;
/* A pointer not aligned properly could trigger an alignment fault. */
if ((addr & (sizeof(uintptr_t) - 1U)) != 0U)
return false;
/* Check that all the object is readable */
for (size_t i = 0; i < size; i++) {
if (!is_address_readable(addr + i))
return false;
}
return true;
}
/*
* Returns true if the specified address is correctly aligned and points to a
* valid memory region.
*/
static bool is_valid_jump_address(uintptr_t addr)
{
if (addr == 0U)
return false;
/* Check alignment. Both A64 and A32 use 32-bit opcodes */
if ((addr & (sizeof(uint32_t) - 1U)) != 0U)
return false;
if (!is_address_readable(addr))
return false;
return true;
}
/*
* Returns true if the pointer points at a valid frame record, false otherwise.
*/
static bool is_valid_frame_record(struct frame_record *fr)
{
return is_valid_object((uintptr_t)fr, sizeof(struct frame_record));
}
/*
* Adjust the frame-pointer-register value by 4 bytes on AArch32 to have the
* same layout as AArch64.
*/
static struct frame_record *adjust_frame_record(struct frame_record *fr)
{
#ifdef __aarch64__
return fr;
#else
return (struct frame_record *)((uintptr_t)fr - 4U);
#endif
}
static void unwind_stack(struct frame_record *fr, uintptr_t current_pc,
uintptr_t link_register)
{
uintptr_t call_site;
static const char *backtrace_str = "%u: %s: 0x%lx\n";
const char *el_str = get_el_str(get_current_el());
if (!is_valid_frame_record(fr)) {
printf("ERROR: Corrupted frame pointer (frame record address = %p)\n",
fr);
return;
}
call_site = extract_address(fr->return_addr);
if (call_site != link_register) {
printf("ERROR: Corrupted stack (frame record address = %p)\n",
fr);
return;
}
/* The level 0 of the backtrace is the current backtrace function */
printf(backtrace_str, 0U, el_str, current_pc);
/*
* The last frame record pointer in the linked list at the beginning of
* the stack should be NULL unless stack is corrupted.
*/
for (unsigned int i = 1U; i < UNWIND_LIMIT; i++) {
/* If an invalid frame record is found, exit. */
if (!is_valid_frame_record(fr))
return;
/*
* A32 and A64 are fixed length so the address from where the
* call was made is the instruction before the return address,
* which is always 4 bytes before it.
*/
call_site = extract_address(fr->return_addr) - 4U;
/*
* If the address is invalid it means that the frame record is
* probably corrupted.
*/
if (!is_valid_jump_address(call_site))
return;
printf(backtrace_str, i, el_str, call_site);
fr = adjust_frame_record(fr->parent);
}
printf("ERROR: Max backtrace depth reached\n");
}
/*
* Display a backtrace. The cookie string parameter is displayed along the
* trace to help filter the log messages.
*
* Many things can prevent displaying the expected backtrace. For example,
* compiler optimizations can use a branch instead of branch with link when it
* detects a tail call. The backtrace level for this caller will not be
* displayed, as it does not appear in the call stack anymore. Also, assembly
* functions will not be displayed unless they setup AAPCS compliant frame
* records on AArch64 and compliant with GCC-specific frame record format on
* AArch32.
*
* Usage of the trace: addr2line can be used to map the addresses to function
* and source code location when given the ELF file compiled with debug
* information. The "-i" flag is highly recommended to improve display of
* inlined function. The *.dump files generated when building each image can
* also be used.
*
* WARNING: In case of corrupted stack, this function could display security
* sensitive information past the beginning of the stack so it must not be used
* in production build. This function is only compiled in when ENABLE_BACKTRACE
* is set to 1.
*/
void backtrace(const char *cookie)
{
uintptr_t return_address = (uintptr_t)__builtin_return_address(0U);
struct frame_record *fr = __builtin_frame_address(0U);
/* Printing the backtrace may crash the system, flush before starting */
console_flush();
fr = adjust_frame_record(fr);
printf("BACKTRACE: START: %s\n", cookie);
unwind_stack(fr, (uintptr_t)&backtrace, return_address);
printf("BACKTRACE: END: %s\n", cookie);
}

View File

@ -0,0 +1,31 @@
#
# Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Enable backtrace by default in DEBUG AArch64 builds
ifeq (${ARCH},aarch32)
ENABLE_BACKTRACE := 0
else
ENABLE_BACKTRACE := ${DEBUG}
endif
ifeq (${ENABLE_BACKTRACE},1)
# Force the compiler to include the frame pointer
TF_CFLAGS += -fno-omit-frame-pointer
BL_COMMON_SOURCES += common/backtrace/backtrace.c
endif
ifeq (${ARCH},aarch32)
ifeq (${ENABLE_BACKTRACE},1)
ifneq (${AARCH32_INSTRUCTION_SET},A32)
$(error Error: AARCH32_INSTRUCTION_SET=A32 is needed \
for ENABLE_BACKTRACE when compiling for AArch32.)
endif
endif
endif
$(eval $(call assert_boolean,ENABLE_BACKTRACE))
$(eval $(call add_define,ENABLE_BACKTRACE))

280
common/bl_common.c Normal file
View File

@ -0,0 +1,280 @@
/*
* Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <arch.h>
#include <arch_features.h>
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/auth/auth_mod.h>
#include <drivers/io/io_storage.h>
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_defs.h>
#include <plat/common/platform.h>
#if TRUSTED_BOARD_BOOT
# ifdef DYN_DISABLE_AUTH
static int disable_auth;
/******************************************************************************
* API to dynamically disable authentication. Only meant for development
* systems. This is only invoked if DYN_DISABLE_AUTH is defined.
*****************************************************************************/
void dyn_disable_auth(void)
{
INFO("Disabling authentication of images dynamically\n");
disable_auth = 1;
}
# endif /* DYN_DISABLE_AUTH */
/******************************************************************************
* Function to determine whether the authentication is disabled dynamically.
*****************************************************************************/
static int dyn_is_auth_disabled(void)
{
# ifdef DYN_DISABLE_AUTH
return disable_auth;
# else
return 0;
# endif
}
#endif /* TRUSTED_BOARD_BOOT */
uintptr_t page_align(uintptr_t value, unsigned dir)
{
/* Round up the limit to the next page boundary */
if ((value & PAGE_SIZE_MASK) != 0U) {
value &= ~PAGE_SIZE_MASK;
if (dir == UP)
value += PAGE_SIZE;
}
return value;
}
/*******************************************************************************
* Internal function to load an image at a specific address given
* an image ID and extents of free memory.
*
* If the load is successful then the image information is updated.
*
* Returns 0 on success, a negative error code otherwise.
******************************************************************************/
static int load_image(unsigned int image_id, image_info_t *image_data)
{
uintptr_t dev_handle;
uintptr_t image_handle;
uintptr_t image_spec;
uintptr_t image_base;
size_t image_size;
size_t bytes_read;
int io_result;
assert(image_data != NULL);
assert(image_data->h.version >= VERSION_2);
image_base = image_data->image_base;
/* Obtain a reference to the image by querying the platform layer */
io_result = plat_get_image_source(image_id, &dev_handle, &image_spec);
if (io_result != 0) {
WARN("Failed to obtain reference to image id=%u (%i)\n",
image_id, io_result);
return io_result;
}
/* Attempt to access the image */
io_result = io_open(dev_handle, image_spec, &image_handle);
if (io_result != 0) {
WARN("Failed to access image id=%u (%i)\n",
image_id, io_result);
return io_result;
}
INFO("Loading image id=%u at address 0x%lx\n", image_id, image_base);
/* Find the size of the image */
io_result = io_size(image_handle, &image_size);
if ((io_result != 0) || (image_size == 0U)) {
WARN("Failed to determine the size of the image id=%u (%i)\n",
image_id, io_result);
goto exit;
}
/* Check that the image size to load is within limit */
if (image_size > image_data->image_max_size) {
WARN("Image id=%u size out of bounds\n", image_id);
io_result = -EFBIG;
goto exit;
}
/*
* image_data->image_max_size is a uint32_t so image_size will always
* fit in image_data->image_size.
*/
image_data->image_size = (uint32_t)image_size;
/* We have enough space so load the image now */
/* TODO: Consider whether to try to recover/retry a partially successful read */
io_result = io_read(image_handle, image_base, image_size, &bytes_read);
if ((io_result != 0) || (bytes_read < image_size)) {
WARN("Failed to load image id=%u (%i)\n", image_id, io_result);
goto exit;
}
INFO("Image id=%u loaded: 0x%lx - 0x%lx\n", image_id, image_base,
(uintptr_t)(image_base + image_size));
exit:
(void)io_close(image_handle);
/* Ignore improbable/unrecoverable error in 'close' */
/* TODO: Consider maintaining open device connection from this bootloader stage */
(void)io_dev_close(dev_handle);
/* Ignore improbable/unrecoverable error in 'dev_close' */
return io_result;
}
#if TRUSTED_BOARD_BOOT
/*
* This function uses recursion to authenticate the parent images up to the root
* of trust.
*/
static int load_auth_image_recursive(unsigned int image_id,
image_info_t *image_data,
int is_parent_image)
{
int rc;
unsigned int parent_id;
/* Use recursion to authenticate parent images */
rc = auth_mod_get_parent_id(image_id, &parent_id);
if (rc == 0) {
rc = load_auth_image_recursive(parent_id, image_data, 1);
if (rc != 0) {
return rc;
}
}
/* Load the image */
rc = load_image(image_id, image_data);
if (rc != 0) {
return rc;
}
/* Authenticate it */
rc = auth_mod_verify_img(image_id,
(void *)image_data->image_base,
image_data->image_size);
if (rc != 0) {
/* Authentication error, zero memory and flush it right away. */
zero_normalmem((void *)image_data->image_base,
image_data->image_size);
flush_dcache_range(image_data->image_base,
image_data->image_size);
return -EAUTH;
}
return 0;
}
#endif /* TRUSTED_BOARD_BOOT */
static int load_auth_image_internal(unsigned int image_id,
image_info_t *image_data)
{
#if TRUSTED_BOARD_BOOT
if (dyn_is_auth_disabled() == 0) {
return load_auth_image_recursive(image_id, image_data, 0);
}
#endif
return load_image(image_id, image_data);
}
/*******************************************************************************
* Generic function to load and authenticate an image. The image is actually
* loaded by calling the 'load_image()' function. Therefore, it returns the
* same error codes if the loading operation failed, or -EAUTH if the
* authentication failed. In addition, this function uses recursion to
* authenticate the parent images up to the root of trust (if TBB is enabled).
******************************************************************************/
int load_auth_image(unsigned int image_id, image_info_t *image_data)
{
int err;
/*
* All firmware banks should be part of the same non-volatile storage as per
* PSA FWU specification, hence don't check for any alternate boot source
* when PSA FWU is enabled.
*/
#if PSA_FWU_SUPPORT
err = load_auth_image_internal(image_id, image_data);
#else
do {
err = load_auth_image_internal(image_id, image_data);
} while ((err != 0) && (plat_try_next_boot_source() != 0));
#endif /* PSA_FWU_SUPPORT */
if (err == 0) {
/*
* If loading of the image gets passed (along with its
* authentication in case of Trusted-Boot flow) then measure
* it (if MEASURED_BOOT flag is enabled).
*/
err = plat_mboot_measure_image(image_id, image_data);
if (err != 0) {
return err;
}
/*
* Flush the image to main memory so that it can be executed
* later by any CPU, regardless of cache and MMU state.
*/
flush_dcache_range(image_data->image_base,
image_data->image_size);
}
return err;
}
/*******************************************************************************
* Print the content of an entry_point_info_t structure.
******************************************************************************/
void print_entry_point_info(const entry_point_info_t *ep_info)
{
INFO("Entry point address = 0x%lx\n", ep_info->pc);
INFO("SPSR = 0x%x\n", ep_info->spsr);
#define PRINT_IMAGE_ARG(n) \
VERBOSE("Argument #" #n " = 0x%llx\n", \
(unsigned long long) ep_info->args.arg##n)
PRINT_IMAGE_ARG(0);
PRINT_IMAGE_ARG(1);
PRINT_IMAGE_ARG(2);
PRINT_IMAGE_ARG(3);
#ifdef __aarch64__
PRINT_IMAGE_ARG(4);
PRINT_IMAGE_ARG(5);
PRINT_IMAGE_ARG(6);
PRINT_IMAGE_ARG(7);
#endif
#undef PRINT_IMAGE_ARG
}
/*
* This function is for returning the TF-A version
*/
const char *get_version(void)
{
extern const char version[];
return version;
}

351
common/desc_image_load.c Normal file
View File

@ -0,0 +1,351 @@
/*
* Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/desc_image_load.h>
#include <common/tbbr/tbbr_img_def.h>
static bl_load_info_t bl_load_info;
static bl_params_t next_bl_params;
/*******************************************************************************
* This function flushes the data structures so that they are visible
* in memory for the next BL image.
******************************************************************************/
void flush_bl_params_desc(void)
{
flush_bl_params_desc_args(bl_mem_params_desc_ptr,
bl_mem_params_desc_num,
&next_bl_params);
}
/*******************************************************************************
* This function flushes the data structures specified as arguments so that they
* are visible in memory for the next BL image.
******************************************************************************/
void flush_bl_params_desc_args(bl_mem_params_node_t *mem_params_desc_ptr,
unsigned int mem_params_desc_num,
bl_params_t *next_bl_params_ptr)
{
assert(mem_params_desc_ptr != NULL);
assert(mem_params_desc_num != 0U);
assert(next_bl_params_ptr != NULL);
flush_dcache_range((uintptr_t)mem_params_desc_ptr,
sizeof(*mem_params_desc_ptr) * mem_params_desc_num);
flush_dcache_range((uintptr_t)next_bl_params_ptr,
sizeof(*next_bl_params_ptr));
}
/*******************************************************************************
* This function returns the index for given image_id, within the
* image descriptor array provided by bl_image_info_descs_ptr, if the
* image is found else it returns -1.
******************************************************************************/
int get_bl_params_node_index(unsigned int image_id)
{
unsigned int index;
assert(image_id != INVALID_IMAGE_ID);
for (index = 0U; index < bl_mem_params_desc_num; index++) {
if (bl_mem_params_desc_ptr[index].image_id == image_id)
return (int)index;
}
return -1;
}
/*******************************************************************************
* This function returns the pointer to `bl_mem_params_node_t` object for
* given image_id, within the image descriptor array provided by
* bl_mem_params_desc_ptr, if the image is found else it returns NULL.
******************************************************************************/
bl_mem_params_node_t *get_bl_mem_params_node(unsigned int image_id)
{
int index;
assert(image_id != INVALID_IMAGE_ID);
index = get_bl_params_node_index(image_id);
if (index >= 0)
return &bl_mem_params_desc_ptr[index];
else
return NULL;
}
/*******************************************************************************
* This function creates the list of loadable images, by populating and
* linking each `bl_load_info_node_t` type node, using the internal array
* of image descriptor provided by bl_mem_params_desc_ptr. It also populates
* and returns `bl_load_info_t` type structure that contains head of the list
* of loadable images.
******************************************************************************/
bl_load_info_t *get_bl_load_info_from_mem_params_desc(void)
{
unsigned int index = 0;
/* If there is no image to start with, return NULL */
if (bl_mem_params_desc_num == 0U)
return NULL;
/* Assign initial data structures */
bl_load_info_node_t *bl_node_info =
&bl_mem_params_desc_ptr[index].load_node_mem;
bl_load_info.head = bl_node_info;
SET_PARAM_HEAD(&bl_load_info, PARAM_BL_LOAD_INFO, VERSION_2, 0U);
/* Go through the image descriptor array and create the list */
for (; index < bl_mem_params_desc_num; index++) {
/* Populate the image information */
bl_node_info->image_id = bl_mem_params_desc_ptr[index].image_id;
bl_node_info->image_info = &bl_mem_params_desc_ptr[index].image_info;
/* Link next image if present */
if ((index + 1U) < bl_mem_params_desc_num) {
/* Get the memory and link the next node */
bl_node_info->next_load_info =
&bl_mem_params_desc_ptr[index + 1U].load_node_mem;
bl_node_info = bl_node_info->next_load_info;
}
}
return &bl_load_info;
}
/*******************************************************************************
* This function creates the list of executable images, by populating and
* linking each `bl_params_node_t` type node, using the internal array of
* image descriptor provided by bl_mem_params_desc_ptr. It also populates
* and returns `bl_params_t` type structure that contains head of the list
* of executable images.
******************************************************************************/
bl_params_t *get_next_bl_params_from_mem_params_desc(void)
{
unsigned int count;
unsigned int img_id = 0U;
unsigned int link_index = 0U;
bl_params_node_t *bl_current_exec_node = NULL;
bl_params_node_t *bl_last_exec_node = NULL;
bl_mem_params_node_t *desc_ptr;
/* If there is no image to start with, return NULL */
if (bl_mem_params_desc_num == 0U)
return NULL;
/* Get the list HEAD */
for (count = 0U; count < bl_mem_params_desc_num; count++) {
desc_ptr = &bl_mem_params_desc_ptr[count];
if ((EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE) &&
(EP_GET_FIRST_EXE(desc_ptr->ep_info.h.attr) == EP_FIRST_EXE)) {
next_bl_params.head = &desc_ptr->params_node_mem;
link_index = count;
break;
}
}
/* Make sure we have a HEAD node */
assert(next_bl_params.head != NULL);
/* Populate the HEAD information */
SET_PARAM_HEAD(&next_bl_params, PARAM_BL_PARAMS, VERSION_2, 0U);
/*
* Go through the image descriptor array and create the list.
* This bounded loop is to make sure that we are not looping forever.
*/
for (count = 0U; count < bl_mem_params_desc_num; count++) {
desc_ptr = &bl_mem_params_desc_ptr[link_index];
/* Make sure the image is executable */
assert(EP_GET_EXE(desc_ptr->ep_info.h.attr) == EXECUTABLE);
/* Get the memory for current node */
bl_current_exec_node = &desc_ptr->params_node_mem;
/* Populate the image information */
bl_current_exec_node->image_id = desc_ptr->image_id;
bl_current_exec_node->image_info = &desc_ptr->image_info;
bl_current_exec_node->ep_info = &desc_ptr->ep_info;
if (bl_last_exec_node != NULL) {
/* Assert if loop detected */
assert(bl_last_exec_node->next_params_info == NULL);
/* Link the previous node to the current one */
bl_last_exec_node->next_params_info = bl_current_exec_node;
}
/* Update the last node */
bl_last_exec_node = bl_current_exec_node;
/* If no next hand-off image then break out */
img_id = desc_ptr->next_handoff_image_id;
if (img_id == INVALID_IMAGE_ID)
break;
/* Get the index for the next hand-off image */
link_index = get_bl_params_node_index(img_id);
assert((link_index > 0U) &&
(link_index < bl_mem_params_desc_num));
}
/* Invalid image is expected to terminate the loop */
assert(img_id == INVALID_IMAGE_ID);
return &next_bl_params;
}
/*******************************************************************************
* This function populates the entry point information with the corresponding
* config file for all executable BL images described in bl_params.
******************************************************************************/
void populate_next_bl_params_config(bl_params_t *bl2_to_next_bl_params)
{
bl_params_node_t *params_node;
unsigned int fw_config_id;
uintptr_t fw_config_base;
bl_mem_params_node_t *mem_params;
uintptr_t hw_config_base = 0;
assert(bl2_to_next_bl_params != NULL);
/*
* Get the `bl_mem_params_node_t` corresponding to HW_CONFIG
* if available.
*/
mem_params = get_bl_mem_params_node(HW_CONFIG_ID);
if (mem_params != NULL)
hw_config_base = mem_params->image_info.image_base;
for (params_node = bl2_to_next_bl_params->head; params_node != NULL;
params_node = params_node->next_params_info) {
fw_config_base = 0;
switch (params_node->image_id) {
case BL31_IMAGE_ID:
fw_config_id = SOC_FW_CONFIG_ID;
break;
case BL32_IMAGE_ID:
/*
* At the moment, OPTEE cannot accept a DTB in secure memory,
* so fall back and use NT_FW_CONFIG instead.
* This MUST be fixed as soon as OPTEE has support to
* receive DTBs in secure memory.
*/
#ifndef SPD_opteed
fw_config_id = TOS_FW_CONFIG_ID;
break;
#endif
case BL33_IMAGE_ID:
fw_config_id = NT_FW_CONFIG_ID;
break;
default:
fw_config_id = INVALID_IMAGE_ID;
break;
}
if (fw_config_id != INVALID_IMAGE_ID) {
mem_params = get_bl_mem_params_node(fw_config_id);
if (mem_params != NULL) {
fw_config_base = mem_params->image_info.image_base;
}
}
#ifdef SPD_opteed
/*
* If SPD_opteed is enabled, arg[0,2] are populated by
* parse_optee_header(), which is called by
* arm_bl2_handle_post_image_load(). The meaning of the
* arguments are:
* arg0 <-- MODE_RW
* arg1 <-- Paged image base
* arg2 <-- Paged image size
*/
if (params_node->image_id == BL32_IMAGE_ID) {
params_node->ep_info->args.arg3 = fw_config_base;
} else {
#endif
/*
* Pass hw and tb_fw config addresses to next images.
* NOTE - for EL3 runtime images (BL31 for AArch64
* and BL32 for AArch32), arg0 is already used by
* generic code. Take care of not overwriting the
* previous initialisations.
*/
if (params_node == bl2_to_next_bl_params->head) {
if (params_node->ep_info->args.arg1 == 0U)
params_node->ep_info->args.arg1 =
fw_config_base;
if (params_node->ep_info->args.arg2 == 0U)
params_node->ep_info->args.arg2 =
hw_config_base;
} else {
if (params_node->ep_info->args.arg0 == 0U)
params_node->ep_info->args.arg0 =
fw_config_base;
if (params_node->ep_info->args.arg1 == 0U)
params_node->ep_info->args.arg1 =
hw_config_base;
}
#ifdef SPD_opteed
}
#endif
}
}
/*******************************************************************************
* Helper to extract BL32/BL33 entry point info from arg0 passed to BL31, for
* platforms that are only interested in those. Platforms that need to extract
* more information can parse the structures themselves.
******************************************************************************/
void bl31_params_parse_helper(u_register_t param,
entry_point_info_t *bl32_ep_info_out,
entry_point_info_t *bl33_ep_info_out)
{
bl_params_node_t *node;
bl_params_t *v2 = (void *)(uintptr_t)param;
#if !ERROR_DEPRECATED
if (v2->h.version == PARAM_VERSION_1) {
struct { /* Deprecated version 1 parameter structure. */
param_header_t h;
image_info_t *bl31_image_info;
entry_point_info_t *bl32_ep_info;
image_info_t *bl32_image_info;
entry_point_info_t *bl33_ep_info;
image_info_t *bl33_image_info;
} *v1 = (void *)(uintptr_t)param;
assert(v1->h.type == PARAM_BL31);
if (bl32_ep_info_out != NULL)
*bl32_ep_info_out = *v1->bl32_ep_info;
if (bl33_ep_info_out != NULL)
*bl33_ep_info_out = *v1->bl33_ep_info;
return;
}
#endif /* !ERROR_DEPRECATED */
assert(v2->h.version == PARAM_VERSION_2);
assert(v2->h.type == PARAM_BL_PARAMS);
for (node = v2->head; node != NULL; node = node->next_params_info) {
if (node->image_id == BL32_IMAGE_ID)
if (bl32_ep_info_out != NULL)
*bl32_ep_info_out = *node->ep_info;
if (node->image_id == BL33_IMAGE_ID)
if (bl33_ep_info_out != NULL)
*bl33_ep_info_out = *node->ep_info;
}
}

626
common/fdt_fixup.c Normal file
View File

@ -0,0 +1,626 @@
/*
* Copyright (c) 2016-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Contains generic routines to fix up the device tree blob passed on to
* payloads like BL32 and BL33 (and further down the boot chain).
* This allows to easily add PSCI nodes, when the original DT does not have
* it or advertises another method.
* Also it supports to add reserved memory nodes to describe memory that
* is used by the secure world, so that non-secure software avoids using
* that.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <libfdt.h>
#include <arch.h>
#include <common/debug.h>
#include <common/fdt_fixup.h>
#include <common/fdt_wrappers.h>
#include <drivers/console.h>
#include <lib/psci/psci.h>
#include <plat/common/platform.h>
static int append_psci_compatible(void *fdt, int offs, const char *str)
{
return fdt_appendprop(fdt, offs, "compatible", str, strlen(str) + 1);
}
/*
* Those defines are for PSCI v0.1 legacy clients, which we expect to use
* the same execution state (AArch32/AArch64) as TF-A.
* Kernels running in AArch32 on an AArch64 TF-A should use PSCI v0.2.
*/
#ifdef __aarch64__
#define PSCI_CPU_SUSPEND_FNID PSCI_CPU_SUSPEND_AARCH64
#define PSCI_CPU_ON_FNID PSCI_CPU_ON_AARCH64
#else
#define PSCI_CPU_SUSPEND_FNID PSCI_CPU_SUSPEND_AARCH32
#define PSCI_CPU_ON_FNID PSCI_CPU_ON_AARCH32
#endif
/*******************************************************************************
* dt_add_psci_node() - Add a PSCI node into an existing device tree
* @fdt: pointer to the device tree blob in memory
*
* Add a device tree node describing PSCI into the root level of an existing
* device tree blob in memory.
* This will add v0.1, v0.2 and v1.0 compatible strings and the standard
* function IDs for v0.1 compatibility.
* An existing PSCI node will not be touched, the function will return success
* in this case. This function will not touch the /cpus enable methods, use
* dt_add_psci_cpu_enable_methods() for that.
*
* Return: 0 on success, -1 otherwise.
******************************************************************************/
int dt_add_psci_node(void *fdt)
{
int offs;
if (fdt_path_offset(fdt, "/psci") >= 0) {
WARN("PSCI Device Tree node already exists!\n");
return 0;
}
offs = fdt_path_offset(fdt, "/");
if (offs < 0)
return -1;
offs = fdt_add_subnode(fdt, offs, "psci");
if (offs < 0)
return -1;
if (append_psci_compatible(fdt, offs, "arm,psci-1.0"))
return -1;
if (append_psci_compatible(fdt, offs, "arm,psci-0.2"))
return -1;
if (append_psci_compatible(fdt, offs, "arm,psci"))
return -1;
if (fdt_setprop_string(fdt, offs, "method", "smc"))
return -1;
if (fdt_setprop_u32(fdt, offs, "cpu_suspend", PSCI_CPU_SUSPEND_FNID))
return -1;
if (fdt_setprop_u32(fdt, offs, "cpu_off", PSCI_CPU_OFF))
return -1;
if (fdt_setprop_u32(fdt, offs, "cpu_on", PSCI_CPU_ON_FNID))
return -1;
return 0;
}
/*
* Find the first subnode that has a "device_type" property with the value
* "cpu" and which's enable-method is not "psci" (yet).
* Returns 0 if no such subnode is found, so all have already been patched
* or none have to be patched in the first place.
* Returns 1 if *one* such subnode has been found and successfully changed
* to "psci".
* Returns negative values on error.
*
* Call in a loop until it returns 0. Recalculate the node offset after
* it has returned 1.
*/
static int dt_update_one_cpu_node(void *fdt, int offset)
{
int offs;
/* Iterate over all subnodes to find those with device_type = "cpu". */
for (offs = fdt_first_subnode(fdt, offset); offs >= 0;
offs = fdt_next_subnode(fdt, offs)) {
const char *prop;
int len;
int ret;
prop = fdt_getprop(fdt, offs, "device_type", &len);
if (prop == NULL)
continue;
if ((strcmp(prop, "cpu") != 0) || (len != 4))
continue;
/* Ignore any nodes which already use "psci". */
prop = fdt_getprop(fdt, offs, "enable-method", &len);
if ((prop != NULL) &&
(strcmp(prop, "psci") == 0) && (len == 5))
continue;
ret = fdt_setprop_string(fdt, offs, "enable-method", "psci");
if (ret < 0)
return ret;
/*
* Subnode found and patched.
* Restart to accommodate potentially changed offsets.
*/
return 1;
}
if (offs == -FDT_ERR_NOTFOUND)
return 0;
return offs;
}
/*******************************************************************************
* dt_add_psci_cpu_enable_methods() - switch CPU nodes in DT to use PSCI
* @fdt: pointer to the device tree blob in memory
*
* Iterate over all CPU device tree nodes (/cpus/cpu@x) in memory to change
* the enable-method to PSCI. This will add the enable-method properties, if
* required, or will change existing properties to read "psci".
*
* Return: 0 on success, or a negative error value otherwise.
******************************************************************************/
int dt_add_psci_cpu_enable_methods(void *fdt)
{
int offs, ret;
do {
offs = fdt_path_offset(fdt, "/cpus");
if (offs < 0)
return offs;
ret = dt_update_one_cpu_node(fdt, offs);
} while (ret > 0);
return ret;
}
#define HIGH_BITS(x) ((sizeof(x) > 4) ? ((x) >> 32) : (typeof(x))0)
/*******************************************************************************
* fdt_add_reserved_memory() - reserve (secure) memory regions in DT
* @dtb: pointer to the device tree blob in memory
* @node_name: name of the subnode to be used
* @base: physical base address of the reserved region
* @size: size of the reserved region
*
* Add a region of memory to the /reserved-memory node in a device tree in
* memory, creating that node if required. Each region goes into a subnode
* of that node and has a @node_name, a @base address and a @size.
* This will prevent any device tree consumer from using that memory. It
* can be used to announce secure memory regions, as it adds the "no-map"
* property to prevent mapping and speculative operations on that region.
*
* See reserved-memory/reserved-memory.txt in the (Linux kernel) DT binding
* documentation for details.
* According to this binding, the address-cells and size-cells must match
* those of the root node.
*
* Return: 0 on success, a negative error value otherwise.
******************************************************************************/
int fdt_add_reserved_memory(void *dtb, const char *node_name,
uintptr_t base, size_t size)
{
int offs = fdt_path_offset(dtb, "/reserved-memory");
uint32_t addresses[4];
int ac, sc;
unsigned int idx = 0;
ac = fdt_address_cells(dtb, 0);
sc = fdt_size_cells(dtb, 0);
if (offs < 0) { /* create if not existing yet */
offs = fdt_add_subnode(dtb, 0, "reserved-memory");
if (offs < 0) {
return offs;
}
fdt_setprop_u32(dtb, offs, "#address-cells", ac);
fdt_setprop_u32(dtb, offs, "#size-cells", sc);
fdt_setprop(dtb, offs, "ranges", NULL, 0);
}
if (ac > 1) {
addresses[idx] = cpu_to_fdt32(HIGH_BITS(base));
idx++;
}
addresses[idx] = cpu_to_fdt32(base & 0xffffffff);
idx++;
if (sc > 1) {
addresses[idx] = cpu_to_fdt32(HIGH_BITS(size));
idx++;
}
addresses[idx] = cpu_to_fdt32(size & 0xffffffff);
idx++;
offs = fdt_add_subnode(dtb, offs, node_name);
fdt_setprop(dtb, offs, "no-map", NULL, 0);
fdt_setprop(dtb, offs, "reg", addresses, idx * sizeof(uint32_t));
return 0;
}
/*******************************************************************************
* fdt_add_cpu() Add a new CPU node to the DT
* @dtb: Pointer to the device tree blob in memory
* @parent: Offset of the parent node
* @mpidr: MPIDR for the current CPU
*
* Create and add a new cpu node to a DTB.
*
* Return the offset of the new node or a negative value in case of error
******************************************************************************/
static int fdt_add_cpu(void *dtb, int parent, u_register_t mpidr)
{
int cpu_offs;
int err;
char snode_name[15];
uint64_t reg_prop;
reg_prop = mpidr & MPID_MASK & ~MPIDR_MT_MASK;
snprintf(snode_name, sizeof(snode_name), "cpu@%x",
(unsigned int)reg_prop);
cpu_offs = fdt_add_subnode(dtb, parent, snode_name);
if (cpu_offs < 0) {
ERROR ("FDT: add subnode \"%s\" failed: %i\n",
snode_name, cpu_offs);
return cpu_offs;
}
err = fdt_setprop_string(dtb, cpu_offs, "compatible", "arm,armv8");
if (err < 0) {
ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
"compatible", cpu_offs);
return err;
}
err = fdt_setprop_u64(dtb, cpu_offs, "reg", reg_prop);
if (err < 0) {
ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
"reg", cpu_offs);
return err;
}
err = fdt_setprop_string(dtb, cpu_offs, "device_type", "cpu");
if (err < 0) {
ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
"device_type", cpu_offs);
return err;
}
err = fdt_setprop_string(dtb, cpu_offs, "enable-method", "psci");
if (err < 0) {
ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
"enable-method", cpu_offs);
return err;
}
return cpu_offs;
}
/******************************************************************************
* fdt_add_cpus_node() - Add the cpus node to the DTB
* @dtb: pointer to the device tree blob in memory
* @afflv0: Maximum number of threads per core (affinity level 0).
* @afflv1: Maximum number of CPUs per cluster (affinity level 1).
* @afflv2: Maximum number of clusters (affinity level 2).
*
* Iterate over all the possible MPIDs given the maximum affinity levels and
* add a cpus node to the DTB with all the valid CPUs on the system.
* If there is already a /cpus node, exit gracefully
*
* A system with two CPUs would generate a node equivalent or similar to:
*
* cpus {
* #address-cells = <2>;
* #size-cells = <0>;
*
* cpu0: cpu@0 {
* compatible = "arm,armv8";
* reg = <0x0 0x0>;
* device_type = "cpu";
* enable-method = "psci";
* };
* cpu1: cpu@10000 {
* compatible = "arm,armv8";
* reg = <0x0 0x100>;
* device_type = "cpu";
* enable-method = "psci";
* };
* };
*
* Full documentation about the CPU bindings can be found at:
* https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/cpus.txt
*
* Return the offset of the node or a negative value on error.
******************************************************************************/
int fdt_add_cpus_node(void *dtb, unsigned int afflv0,
unsigned int afflv1, unsigned int afflv2)
{
int offs;
int err;
unsigned int i, j, k;
u_register_t mpidr;
int cpuid;
if (fdt_path_offset(dtb, "/cpus") >= 0) {
return -EEXIST;
}
offs = fdt_add_subnode(dtb, 0, "cpus");
if (offs < 0) {
ERROR ("FDT: add subnode \"cpus\" node to parent node failed");
return offs;
}
err = fdt_setprop_u32(dtb, offs, "#address-cells", 2);
if (err < 0) {
ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
"#address-cells", offs);
return err;
}
err = fdt_setprop_u32(dtb, offs, "#size-cells", 0);
if (err < 0) {
ERROR ("FDT: write to \"%s\" property of node at offset %i failed\n",
"#size-cells", offs);
return err;
}
/*
* Populate the node with the CPUs.
* As libfdt prepends subnodes within a node, reverse the index count
* so the CPU nodes would be better ordered.
*/
for (i = afflv2; i > 0U; i--) {
for (j = afflv1; j > 0U; j--) {
for (k = afflv0; k > 0U; k--) {
mpidr = ((i - 1) << MPIDR_AFF2_SHIFT) |
((j - 1) << MPIDR_AFF1_SHIFT) |
((k - 1) << MPIDR_AFF0_SHIFT) |
(read_mpidr_el1() & MPIDR_MT_MASK);
cpuid = plat_core_pos_by_mpidr(mpidr);
if (cpuid >= 0) {
/* Valid MPID found */
err = fdt_add_cpu(dtb, offs, mpidr);
if (err < 0) {
ERROR ("FDT: %s 0x%08x\n",
"error adding CPU",
(uint32_t)mpidr);
return err;
}
}
}
}
}
return offs;
}
/*******************************************************************************
* fdt_add_cpu_idle_states() - add PSCI CPU idle states to cpu nodes in the DT
* @dtb: pointer to the device tree blob in memory
* @states: array of idle state descriptions, ending with empty element
*
* Add information about CPU idle states to the devicetree. This function
* assumes that CPU idle states are not already present in the devicetree, and
* that all CPU states are equally applicable to all CPUs.
*
* See arm/idle-states.yaml and arm/psci.yaml in the (Linux kernel) DT binding
* documentation for more details.
*
* Return: 0 on success, a negative error value otherwise.
******************************************************************************/
int fdt_add_cpu_idle_states(void *dtb, const struct psci_cpu_idle_state *state)
{
int cpu_node, cpus_node, idle_states_node, ret;
uint32_t count, phandle;
ret = fdt_find_max_phandle(dtb, &phandle);
phandle++;
if (ret < 0) {
return ret;
}
cpus_node = fdt_path_offset(dtb, "/cpus");
if (cpus_node < 0) {
return cpus_node;
}
/* Create the idle-states node and its child nodes. */
idle_states_node = fdt_add_subnode(dtb, cpus_node, "idle-states");
if (idle_states_node < 0) {
return idle_states_node;
}
ret = fdt_setprop_string(dtb, idle_states_node, "entry-method", "psci");
if (ret < 0) {
return ret;
}
for (count = 0U; state->name != NULL; count++, phandle++, state++) {
int idle_state_node;
idle_state_node = fdt_add_subnode(dtb, idle_states_node,
state->name);
if (idle_state_node < 0) {
return idle_state_node;
}
fdt_setprop_string(dtb, idle_state_node, "compatible",
"arm,idle-state");
fdt_setprop_u32(dtb, idle_state_node, "arm,psci-suspend-param",
state->power_state);
if (state->local_timer_stop) {
fdt_setprop_empty(dtb, idle_state_node,
"local-timer-stop");
}
fdt_setprop_u32(dtb, idle_state_node, "entry-latency-us",
state->entry_latency_us);
fdt_setprop_u32(dtb, idle_state_node, "exit-latency-us",
state->exit_latency_us);
fdt_setprop_u32(dtb, idle_state_node, "min-residency-us",
state->min_residency_us);
if (state->wakeup_latency_us) {
fdt_setprop_u32(dtb, idle_state_node,
"wakeup-latency-us",
state->wakeup_latency_us);
}
fdt_setprop_u32(dtb, idle_state_node, "phandle", phandle);
}
if (count == 0U) {
return 0;
}
/* Link each cpu node to the idle state nodes. */
fdt_for_each_subnode(cpu_node, dtb, cpus_node) {
const char *device_type;
fdt32_t *value;
/* Only process child nodes with device_type = "cpu". */
device_type = fdt_getprop(dtb, cpu_node, "device_type", NULL);
if (device_type == NULL || strcmp(device_type, "cpu") != 0) {
continue;
}
/* Allocate space for the list of phandles. */
ret = fdt_setprop_placeholder(dtb, cpu_node, "cpu-idle-states",
count * sizeof(phandle),
(void **)&value);
if (ret < 0) {
return ret;
}
/* Fill in the phandles of the idle state nodes. */
for (uint32_t i = 0U; i < count; ++i) {
value[i] = cpu_to_fdt32(phandle - count + i);
}
}
return 0;
}
/**
* fdt_adjust_gic_redist() - Adjust GICv3 redistributor size
* @dtb: Pointer to the DT blob in memory
* @nr_cores: Number of CPU cores on this system.
* @gicr_base: Base address of the first GICR frame, or ~0 if unchanged
* @gicr_frame_size: Size of the GICR frame per core
*
* On a GICv3 compatible interrupt controller, the redistributor provides
* a number of 64k pages per each supported core. So with a dynamic topology,
* this size cannot be known upfront and thus can't be hardcoded into the DTB.
*
* Find the DT node describing the GICv3 interrupt controller, and adjust
* the size of the redistributor to match the number of actual cores on
* this system.
* A GICv4 compatible redistributor uses four 64K pages per core, whereas GICs
* without support for direct injection of virtual interrupts use two 64K pages.
* The @gicr_frame_size parameter should be 262144 and 131072, respectively.
* Also optionally allow adjusting the GICR frame base address, when this is
* different due to ITS frames between distributor and redistributor.
*
* Return: 0 on success, negative error value otherwise.
*/
int fdt_adjust_gic_redist(void *dtb, unsigned int nr_cores,
uintptr_t gicr_base, unsigned int gicr_frame_size)
{
int offset = fdt_node_offset_by_compatible(dtb, 0, "arm,gic-v3");
uint64_t reg_64;
uint32_t reg_32;
void *val;
int parent, ret;
int ac, sc;
if (offset < 0) {
return offset;
}
parent = fdt_parent_offset(dtb, offset);
if (parent < 0) {
return parent;
}
ac = fdt_address_cells(dtb, parent);
sc = fdt_size_cells(dtb, parent);
if (ac < 0 || sc < 0) {
return -EINVAL;
}
if (gicr_base != INVALID_BASE_ADDR) {
if (ac == 1) {
reg_32 = cpu_to_fdt32(gicr_base);
val = &reg_32;
} else {
reg_64 = cpu_to_fdt64(gicr_base);
val = &reg_64;
}
/*
* The redistributor base address is the second address in
* the "reg" entry, so we have to skip one address and one
* size cell.
*/
ret = fdt_setprop_inplace_namelen_partial(dtb, offset,
"reg", 3,
(ac + sc) * 4,
val, ac * 4);
if (ret < 0) {
return ret;
}
}
if (sc == 1) {
reg_32 = cpu_to_fdt32(nr_cores * gicr_frame_size);
val = &reg_32;
} else {
reg_64 = cpu_to_fdt64(nr_cores * (uint64_t)gicr_frame_size);
val = &reg_64;
}
/*
* The redistributor is described in the second "reg" entry.
* So we have to skip one address and one size cell, then another
* address cell to get to the second size cell.
*/
return fdt_setprop_inplace_namelen_partial(dtb, offset, "reg", 3,
(ac + sc + ac) * 4,
val, sc * 4);
}
/**
* fdt_set_mac_address () - store MAC address in device tree
* @dtb: pointer to the device tree blob in memory
* @eth_idx: number of Ethernet interface in /aliases node
* @mac_addr: pointer to 6 byte MAC address to store
*
* Use the generic local-mac-address property in a network device DT node
* to define the MAC address this device should be using. Many platform
* network devices lack device-specific non-volatile storage to hold this
* address, and leave it up to firmware to find and store a unique MAC
* address in the DT.
* The MAC address could be read from some board or firmware defined storage,
* or could be derived from some other unique property like a serial number.
*
* Return: 0 on success, a negative libfdt error value otherwise.
*/
int fdt_set_mac_address(void *dtb, unsigned int ethernet_idx,
const uint8_t *mac_addr)
{
char eth_alias[12];
const char *path;
int node;
if (ethernet_idx > 9U) {
return -FDT_ERR_BADVALUE;
}
snprintf(eth_alias, sizeof(eth_alias), "ethernet%d", ethernet_idx);
path = fdt_get_alias(dtb, eth_alias);
if (path == NULL) {
return -FDT_ERR_NOTFOUND;
}
node = fdt_path_offset(dtb, path);
if (node < 0) {
ERROR("Path \"%s\" not found in DT: %d\n", path, node);
return node;
}
return fdt_setprop(dtb, node, "local-mac-address", mac_addr, 6);
}

641
common/fdt_wrappers.c Normal file
View File

@ -0,0 +1,641 @@
/*
* Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* Helper functions to offer easier navigation of Device Tree Blob */
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <libfdt.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <common/uuid.h>
/*
* Read cells from a given property of the given node. Any number of 32-bit
* cells of the property can be read. Returns 0 on success, or a negative
* FDT error value otherwise.
*/
int fdt_read_uint32_array(const void *dtb, int node, const char *prop_name,
unsigned int cells, uint32_t *value)
{
const fdt32_t *prop;
int value_len;
assert(dtb != NULL);
assert(prop_name != NULL);
assert(value != NULL);
assert(node >= 0);
/* Access property and obtain its length (in bytes) */
prop = fdt_getprop(dtb, node, prop_name, &value_len);
if (prop == NULL) {
VERBOSE("Couldn't find property %s in dtb\n", prop_name);
return -FDT_ERR_NOTFOUND;
}
/* Verify that property length can fill the entire array. */
if (NCELLS((unsigned int)value_len) < cells) {
WARN("Property length mismatch\n");
return -FDT_ERR_BADVALUE;
}
for (unsigned int i = 0U; i < cells; i++) {
value[i] = fdt32_to_cpu(prop[i]);
}
return 0;
}
int fdt_read_uint32(const void *dtb, int node, const char *prop_name,
uint32_t *value)
{
return fdt_read_uint32_array(dtb, node, prop_name, 1, value);
}
uint32_t fdt_read_uint32_default(const void *dtb, int node,
const char *prop_name, uint32_t dflt_value)
{
uint32_t ret = dflt_value;
int err = fdt_read_uint32(dtb, node, prop_name, &ret);
if (err < 0) {
return dflt_value;
}
return ret;
}
int fdt_read_uint64(const void *dtb, int node, const char *prop_name,
uint64_t *value)
{
uint32_t array[2] = {0, 0};
int ret;
ret = fdt_read_uint32_array(dtb, node, prop_name, 2, array);
if (ret < 0) {
return ret;
}
*value = ((uint64_t)array[0] << 32) | array[1];
return 0;
}
/*
* Read bytes from a given property of the given node. Any number of
* bytes of the property can be read. The fdt pointer is updated.
* Returns 0 on success, and -1 on error.
*/
int fdtw_read_bytes(const void *dtb, int node, const char *prop,
unsigned int length, void *value)
{
const void *ptr;
int value_len;
assert(dtb != NULL);
assert(prop != NULL);
assert(value != NULL);
assert(node >= 0);
/* Access property and obtain its length (in bytes) */
ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop),
&value_len);
if (ptr == NULL) {
WARN("Couldn't find property %s in dtb\n", prop);
return -1;
}
/* Verify that property length is not less than number of bytes */
if ((unsigned int)value_len < length) {
WARN("Property length mismatch\n");
return -1;
}
(void)memcpy(value, ptr, length);
return 0;
}
/*
* Read string from a given property of the given node. Up to 'size - 1'
* characters are read, and a NUL terminator is added. Returns 0 on success,
* and -1 upon error.
*/
int fdtw_read_string(const void *dtb, int node, const char *prop,
char *str, size_t size)
{
const char *ptr;
size_t len;
assert(dtb != NULL);
assert(node >= 0);
assert(prop != NULL);
assert(str != NULL);
assert(size > 0U);
ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop), NULL);
if (ptr == NULL) {
WARN("Couldn't find property %s in dtb\n", prop);
return -1;
}
len = strlcpy(str, ptr, size);
if (len >= size) {
WARN("String of property %s in dtb has been truncated\n", prop);
return -1;
}
return 0;
}
/*
* Read UUID from a given property of the given node. Returns 0 on success,
* and a negative value upon error.
*/
int fdtw_read_uuid(const void *dtb, int node, const char *prop,
unsigned int length, uint8_t *uuid)
{
/* Buffer for UUID string (plus NUL terminator) */
char uuid_string[UUID_STRING_LENGTH + 1U];
int err;
assert(dtb != NULL);
assert(prop != NULL);
assert(uuid != NULL);
assert(node >= 0);
if (length < UUID_BYTES_LENGTH) {
return -EINVAL;
}
err = fdtw_read_string(dtb, node, prop, uuid_string,
UUID_STRING_LENGTH + 1U);
if (err != 0) {
return err;
}
if (read_uuid(uuid, uuid_string) != 0) {
return -FDT_ERR_BADVALUE;
}
return 0;
}
/*
* Write cells in place to a given property of the given node. At most 2 cells
* of the property are written. Returns 0 on success, and -1 upon error.
*/
int fdtw_write_inplace_cells(void *dtb, int node, const char *prop,
unsigned int cells, void *value)
{
int err, len;
assert(dtb != NULL);
assert(prop != NULL);
assert(value != NULL);
assert(node >= 0);
/* We expect either 1 or 2 cell property */
assert(cells <= 2U);
if (cells == 2U)
*(fdt64_t *)value = cpu_to_fdt64(*(uint64_t *)value);
else
*(fdt32_t *)value = cpu_to_fdt32(*(uint32_t *)value);
len = (int)cells * 4;
/* Set property value in place */
err = fdt_setprop_inplace(dtb, node, prop, value, len);
if (err != 0) {
WARN("Modify property %s failed with error %d\n", prop, err);
return -1;
}
return 0;
}
/*
* Write bytes in place to a given property of the given node.
* Any number of bytes of the property can be written.
* Returns 0 on success, and < 0 on error.
*/
int fdtw_write_inplace_bytes(void *dtb, int node, const char *prop,
unsigned int length, const void *data)
{
const void *ptr;
int namelen, value_len, err;
assert(dtb != NULL);
assert(prop != NULL);
assert(data != NULL);
assert(node >= 0);
namelen = (int)strlen(prop);
/* Access property and obtain its length in bytes */
ptr = fdt_getprop_namelen(dtb, node, prop, namelen, &value_len);
if (ptr == NULL) {
WARN("Couldn't find property %s in dtb\n", prop);
return -1;
}
/* Verify that property length is not less than number of bytes */
if ((unsigned int)value_len < length) {
WARN("Property length mismatch\n");
return -1;
}
/* Set property value in place */
err = fdt_setprop_inplace_namelen_partial(dtb, node, prop,
namelen, 0,
data, (int)length);
if (err != 0) {
WARN("Set property %s failed with error %d\n", prop, err);
}
return err;
}
static uint64_t fdt_read_prop_cells(const fdt32_t *prop, int nr_cells)
{
uint64_t reg = fdt32_to_cpu(prop[0]);
if (nr_cells > 1) {
reg = (reg << 32) | fdt32_to_cpu(prop[1]);
}
return reg;
}
int fdt_get_reg_props_by_index(const void *dtb, int node, int index,
uintptr_t *base, size_t *size)
{
const fdt32_t *prop;
int parent, len;
int ac, sc;
int cell;
parent = fdt_parent_offset(dtb, node);
if (parent < 0) {
return -FDT_ERR_BADOFFSET;
}
ac = fdt_address_cells(dtb, parent);
sc = fdt_size_cells(dtb, parent);
cell = index * (ac + sc);
prop = fdt_getprop(dtb, node, "reg", &len);
if (prop == NULL) {
WARN("Couldn't find \"reg\" property in dtb\n");
return -FDT_ERR_NOTFOUND;
}
if (((cell + ac + sc) * (int)sizeof(uint32_t)) > len) {
return -FDT_ERR_BADVALUE;
}
if (base != NULL) {
*base = (uintptr_t)fdt_read_prop_cells(&prop[cell], ac);
}
if (size != NULL) {
*size = (size_t)fdt_read_prop_cells(&prop[cell + ac], sc);
}
return 0;
}
/*******************************************************************************
* This function fills reg node info (base & size) with an index found by
* checking the reg-names node.
* Returns 0 on success and a negative FDT error code on failure.
******************************************************************************/
int fdt_get_reg_props_by_name(const void *dtb, int node, const char *name,
uintptr_t *base, size_t *size)
{
int index;
index = fdt_stringlist_search(dtb, node, "reg-names", name);
if (index < 0) {
return index;
}
return fdt_get_reg_props_by_index(dtb, node, index, base, size);
}
/*******************************************************************************
* This function gets the stdout path node.
* It reads the value indicated inside the device tree.
* Returns node offset on success and a negative FDT error code on failure.
******************************************************************************/
int fdt_get_stdout_node_offset(const void *dtb)
{
int node;
const char *prop, *path;
int len;
/* The /secure-chosen node takes precedence over the standard one. */
node = fdt_path_offset(dtb, "/secure-chosen");
if (node < 0) {
node = fdt_path_offset(dtb, "/chosen");
if (node < 0) {
return -FDT_ERR_NOTFOUND;
}
}
prop = fdt_getprop(dtb, node, "stdout-path", NULL);
if (prop == NULL) {
return -FDT_ERR_NOTFOUND;
}
/* Determine the actual path length, as a colon terminates the path. */
path = strchr(prop, ':');
if (path == NULL) {
len = strlen(prop);
} else {
len = path - prop;
}
/* Aliases cannot start with a '/', so it must be the actual path. */
if (prop[0] == '/') {
return fdt_path_offset_namelen(dtb, prop, len);
}
/* Lookup the alias, as this contains the actual path. */
path = fdt_get_alias_namelen(dtb, prop, len);
if (path == NULL) {
return -FDT_ERR_NOTFOUND;
}
return fdt_path_offset(dtb, path);
}
/*******************************************************************************
* Only devices which are direct children of root node use CPU address domain.
* All other devices use addresses that are local to the device node and cannot
* directly used by CPU. Device tree provides an address translation mechanism
* through "ranges" property which provides mappings from local address space to
* parent address space. Since a device could be a child of a child node to the
* root node, there can be more than one level of address translation needed to
* map the device local address space to CPU address space.
* fdtw_translate_address() API performs address translation of a local address
* to a global address with help of various helper functions.
******************************************************************************/
static bool fdtw_xlat_hit(const fdt32_t *value, int child_addr_size,
int parent_addr_size, int range_size, uint64_t base_address,
uint64_t *translated_addr)
{
uint64_t local_address, parent_address, addr_range;
local_address = fdt_read_prop_cells(value, child_addr_size);
parent_address = fdt_read_prop_cells(value + child_addr_size,
parent_addr_size);
addr_range = fdt_read_prop_cells(value + child_addr_size +
parent_addr_size,
range_size);
VERBOSE("DT: Address %" PRIx64 " mapped to %" PRIx64 " with range %" PRIx64 "\n",
local_address, parent_address, addr_range);
/* Perform range check */
if ((base_address < local_address) ||
(base_address >= local_address + addr_range)) {
return false;
}
/* Found hit for the addr range that needs to be translated */
*translated_addr = parent_address + (base_address - local_address);
VERBOSE("DT: child address %" PRIx64 "mapped to %" PRIx64 " in parent bus\n",
local_address, parent_address);
return true;
}
#define ILLEGAL_ADDR ULL(~0)
static uint64_t fdtw_search_all_xlat_entries(const void *dtb,
const struct fdt_property *ranges_prop,
int local_bus, uint64_t base_address)
{
uint64_t translated_addr;
const fdt32_t *next_entry;
int parent_bus_node, nxlat_entries, length;
int self_addr_cells, parent_addr_cells, self_size_cells, ncells_xlat;
/*
* The number of cells in one translation entry in ranges is the sum of
* the following values:
* self#address-cells + parent#address-cells + self#size-cells
* Ex: the iofpga ranges property has one translation entry with 4 cells
* They represent iofpga#addr-cells + motherboard#addr-cells + iofpga#size-cells
* = 1 + 2 + 1
*/
parent_bus_node = fdt_parent_offset(dtb, local_bus);
self_addr_cells = fdt_address_cells(dtb, local_bus);
self_size_cells = fdt_size_cells(dtb, local_bus);
parent_addr_cells = fdt_address_cells(dtb, parent_bus_node);
/* Number of cells per translation entry i.e., mapping */
ncells_xlat = self_addr_cells + parent_addr_cells + self_size_cells;
assert(ncells_xlat > 0);
/*
* Find the number of translations(mappings) specified in the current
* `ranges` property. Note that length represents number of bytes and
* is stored in big endian mode.
*/
length = fdt32_to_cpu(ranges_prop->len);
nxlat_entries = (length/sizeof(uint32_t))/ncells_xlat;
assert(nxlat_entries > 0);
next_entry = (const fdt32_t *)ranges_prop->data;
/* Iterate over the entries in the "ranges" */
for (int i = 0; i < nxlat_entries; i++) {
if (fdtw_xlat_hit(next_entry, self_addr_cells,
parent_addr_cells, self_size_cells, base_address,
&translated_addr)){
return translated_addr;
}
next_entry = next_entry + ncells_xlat;
}
INFO("DT: No translation found for address %" PRIx64 " in node %s\n",
base_address, fdt_get_name(dtb, local_bus, NULL));
return ILLEGAL_ADDR;
}
/*******************************************************************************
* address mapping needs to be done recursively starting from current node to
* root node through all intermediate parent nodes.
* Sample device tree is shown here:
smb@0,0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0 0x08000000 0x04000000>,
<1 0 0 0x14000000 0x04000000>,
<2 0 0 0x18000000 0x04000000>,
<3 0 0 0x1c000000 0x04000000>,
<4 0 0 0x0c000000 0x04000000>,
<5 0 0 0x10000000 0x04000000>;
motherboard {
arm,v2m-memory-map = "rs1";
compatible = "arm,vexpress,v2m-p1", "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
ranges;
iofpga@3,00000000 {
compatible = "arm,amba-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 3 0 0x200000>;
v2m_serial1: uart@a0000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x0a0000 0x1000>;
interrupts = <0 6 4>;
clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
clock-names = "uartclk", "apb_pclk";
};
};
};
* As seen above, there are 3 levels of address translations needed. An empty
* `ranges` property denotes identity mapping (as seen in `motherboard` node).
* Each ranges property can map a set of child addresses to parent bus. Hence
* there can be more than 1 (translation) entry in the ranges property as seen
* in the `smb` node which has 6 translation entries.
******************************************************************************/
/* Recursive implementation */
uint64_t fdtw_translate_address(const void *dtb, int node,
uint64_t base_address)
{
int length, local_bus_node;
const char *node_name;
uint64_t global_address;
local_bus_node = fdt_parent_offset(dtb, node);
node_name = fdt_get_name(dtb, local_bus_node, NULL);
/*
* In the example given above, starting from the leaf node:
* uart@a000 represents the current node
* iofpga@3,00000000 represents the local bus
* motherboard represents the parent bus
*/
/* Read the ranges property */
const struct fdt_property *property = fdt_get_property(dtb,
local_bus_node, "ranges", &length);
if (property == NULL) {
if (local_bus_node == 0) {
/*
* root node doesn't have range property as addresses
* are in CPU address space.
*/
return base_address;
}
INFO("DT: Couldn't find ranges property in node %s\n",
node_name);
return ILLEGAL_ADDR;
} else if (length == 0) {
/* empty ranges indicates identity map to parent bus */
return fdtw_translate_address(dtb, local_bus_node, base_address);
}
VERBOSE("DT: Translation lookup in node %s at offset %d\n", node_name,
local_bus_node);
global_address = fdtw_search_all_xlat_entries(dtb, property,
local_bus_node, base_address);
if (global_address == ILLEGAL_ADDR) {
return ILLEGAL_ADDR;
}
/* Translate the local device address recursively */
return fdtw_translate_address(dtb, local_bus_node, global_address);
}
/*
* For every CPU node (`/cpus/cpu@n`) in an FDT, execute a callback passing a
* pointer to the FDT and the offset of the CPU node. If the return value of the
* callback is negative, it is treated as an error and the loop is aborted. In
* this situation, the value of the callback is returned from the function.
*
* Returns `0` on success, or a negative integer representing an error code.
*/
int fdtw_for_each_cpu(const void *dtb,
int (*callback)(const void *dtb, int node, uintptr_t mpidr))
{
int ret = 0;
int parent, node = 0;
parent = fdt_path_offset(dtb, "/cpus");
if (parent < 0) {
return parent;
}
fdt_for_each_subnode(node, dtb, parent) {
const char *name;
int len;
uintptr_t mpidr = 0U;
name = fdt_get_name(dtb, node, &len);
if (strncmp(name, "cpu@", 4) != 0) {
continue;
}
ret = fdt_get_reg_props_by_index(dtb, node, 0, &mpidr, NULL);
if (ret < 0) {
break;
}
ret = callback(dtb, node, mpidr);
if (ret < 0) {
break;
}
}
return ret;
}
/*
* Find a given node in device tree. If not present, add it.
* Returns offset of node found/added on success, and < 0 on error.
*/
int fdtw_find_or_add_subnode(void *fdt, int parentoffset, const char *name)
{
int offset;
offset = fdt_subnode_offset(fdt, parentoffset, name);
if (offset == -FDT_ERR_NOTFOUND) {
offset = fdt_add_subnode(fdt, parentoffset, name);
}
if (offset < 0) {
ERROR("%s: %s: %s\n", __func__, name, fdt_strerror(offset));
}
return offset;
}

7
common/fdt_wrappers.mk Normal file
View File

@ -0,0 +1,7 @@
#
# Copyright (c) 2021, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
FDT_WRAPPERS_SOURCES := common/fdt_wrappers.c

239
common/feat_detect.c Normal file
View File

@ -0,0 +1,239 @@
/*
* Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_features.h>
#include <common/debug.h>
#include <common/feat_detect.h>
static bool tainted;
/*******************************************************************************
* This section lists the wrapper modules for each feature to evaluate the
* feature states (FEAT_STATE_ALWAYS and FEAT_STATE_CHECK) and perform
* necessary action as below:
*
* It verifies whether the FEAT_XXX (eg: FEAT_SB) is supported by the PE or not.
* Without this check an exception would occur during context save/restore
* routines, if the feature is enabled but not supported by PE.
******************************************************************************/
#define feat_detect_panic(a, b) ((a) ? (void)0 : feature_panic(b))
/*******************************************************************************
* Function : feature_panic
* Customised panic function with error logging mechanism to list the feature
* not supported by the PE.
******************************************************************************/
static inline void feature_panic(char *feat_name)
{
ERROR("FEAT_%s not supported by the PE\n", feat_name);
panic();
}
/*******************************************************************************
* Function : check_feature
* Check for a valid combination of build time flags (ENABLE_FEAT_xxx) and
* feature availability on the hardware. <min> is the smallest feature
* ID field value that is required for that feature.
* Triggers a panic later if a feature is forcefully enabled, but not
* available on the PE. Also will panic if the hardware feature ID field
* is larger than the maximum known and supported number, specified by <max>.
*
* We force inlining here to let the compiler optimise away the whole check
* if the feature is disabled at build time (FEAT_STATE_DISABLED).
******************************************************************************/
static inline void __attribute((__always_inline__))
check_feature(int state, unsigned long field, const char *feat_name,
unsigned int min, unsigned int max)
{
if (state == FEAT_STATE_ALWAYS && field < min) {
ERROR("FEAT_%s not supported by the PE\n", feat_name);
tainted = true;
}
if (state >= FEAT_STATE_ALWAYS && field > max) {
ERROR("FEAT_%s is version %ld, but is only known up to version %d\n",
feat_name, field, max);
tainted = true;
}
}
/************************************************
* Feature : FEAT_PAUTH (Pointer Authentication)
***********************************************/
static void read_feat_pauth(void)
{
#if (ENABLE_PAUTH == FEAT_STATE_ALWAYS) || (CTX_INCLUDE_PAUTH_REGS == FEAT_STATE_ALWAYS)
feat_detect_panic(is_armv8_3_pauth_present(), "PAUTH");
#endif
}
/************************************************
* Feature : FEAT_MTE (Memory Tagging Extension)
***********************************************/
static void read_feat_mte(void)
{
#if (CTX_INCLUDE_MTE_REGS == FEAT_STATE_ALWAYS)
unsigned int mte = get_armv8_5_mte_support();
feat_detect_panic((mte != MTE_UNIMPLEMENTED), "MTE");
#endif
}
/****************************************************
* Feature : FEAT_BTI (Branch Target Identification)
***************************************************/
static void read_feat_bti(void)
{
#if (ENABLE_BTI == FEAT_STATE_ALWAYS)
feat_detect_panic(is_armv8_5_bti_present(), "BTI");
#endif
}
/**************************************************
* Feature : FEAT_RME (Realm Management Extension)
*************************************************/
static void read_feat_rme(void)
{
#if (ENABLE_RME == FEAT_STATE_ALWAYS)
feat_detect_panic((get_armv9_2_feat_rme_support() !=
ID_AA64PFR0_FEAT_RME_NOT_SUPPORTED), "RME");
#endif
}
/******************************************************************
* Feature : FEAT_RNG_TRAP (Trapping support for RNDR/RNDRRS)
*****************************************************************/
static void read_feat_rng_trap(void)
{
#if (ENABLE_FEAT_RNG_TRAP == FEAT_STATE_ALWAYS)
feat_detect_panic(is_feat_rng_trap_present(), "RNG_TRAP");
#endif
}
/***********************************************************************************
* TF-A supports many Arm architectural features starting from arch version
* (8.0 till 8.7+). These features are mostly enabled through build flags. This
* mechanism helps in validating these build flags in the early boot phase
* either in BL1 or BL31 depending on the platform and assists in identifying
* and notifying the features which are enabled but not supported by the PE.
*
* It reads all the enabled features ID-registers and ensures the features
* are supported by the PE.
* In case if they aren't it stops booting at an early phase and logs the error
* messages, notifying the platforms about the features that are not supported.
*
* Further the procedure is implemented with a tri-state approach for each feature:
* ENABLE_FEAT_xxx = 0 : The feature is disabled statically at compile time
* ENABLE_FEAT_xxx = 1 : The feature is enabled and must be present in hardware.
* There will be panic if feature is not present at cold boot.
* ENABLE_FEAT_xxx = 2 : The feature is enabled but dynamically enabled at runtime
* depending on hardware capability.
*
* For better readability, state values are defined with macros, namely:
* { FEAT_STATE_DISABLED, FEAT_STATE_ALWAYS, FEAT_STATE_CHECK }, taking values
* { 0, 1, 2 }, respectively, as their naming.
**********************************************************************************/
void detect_arch_features(void)
{
tainted = false;
/* v8.0 features */
check_feature(ENABLE_FEAT_SB, read_feat_sb_id_field(), "SB", 1, 1);
check_feature(ENABLE_FEAT_CSV2_2, read_feat_csv2_id_field(),
"CSV2_2", 2, 3);
/*
* Even though the PMUv3 is an OPTIONAL feature, it is always
* implemented and Arm prescribes so. So assume it will be there and do
* away with a flag for it. This is used to check minor PMUv3px
* revisions so that we catch them as they come along
*/
check_feature(FEAT_STATE_ALWAYS, read_feat_pmuv3_id_field(),
"PMUv3", 1, ID_AA64DFR0_PMUVER_PMUV3P7);
/* v8.1 features */
check_feature(ENABLE_FEAT_PAN, read_feat_pan_id_field(), "PAN", 1, 3);
check_feature(ENABLE_FEAT_VHE, read_feat_vhe_id_field(), "VHE", 1, 1);
/* v8.2 features */
check_feature(ENABLE_SVE_FOR_NS, read_feat_sve_id_field(),
"SVE", 1, 1);
check_feature(ENABLE_FEAT_RAS, read_feat_ras_id_field(), "RAS", 1, 2);
/* v8.3 features */
read_feat_pauth();
/* v8.4 features */
check_feature(ENABLE_FEAT_DIT, read_feat_dit_id_field(), "DIT", 1, 1);
check_feature(ENABLE_FEAT_AMU, read_feat_amu_id_field(),
"AMUv1", 1, 2);
check_feature(ENABLE_MPAM_FOR_LOWER_ELS, read_feat_mpam_version(),
"MPAM", 1, 17);
check_feature(CTX_INCLUDE_NEVE_REGS, read_feat_nv_id_field(),
"NV2", 2, 2);
check_feature(ENABLE_FEAT_SEL2, read_feat_sel2_id_field(),
"SEL2", 1, 1);
check_feature(ENABLE_TRF_FOR_NS, read_feat_trf_id_field(),
"TRF", 1, 1);
/* v8.5 features */
read_feat_mte();
check_feature(ENABLE_FEAT_RNG, read_feat_rng_id_field(), "RNG", 1, 1);
read_feat_bti();
read_feat_rng_trap();
/* v8.6 features */
check_feature(ENABLE_FEAT_AMUv1p1, read_feat_amu_id_field(),
"AMUv1p1", 2, 2);
check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(), "FGT", 1, 1);
check_feature(ENABLE_FEAT_ECV, read_feat_ecv_id_field(), "ECV", 1, 2);
check_feature(ENABLE_FEAT_TWED, read_feat_twed_id_field(),
"TWED", 1, 1);
/*
* even though this is a "DISABLE" it does confusingly perform feature
* enablement duties like all other flags here. Check it against the HW
* feature when we intend to diverge from the default behaviour
*/
check_feature(DISABLE_MTPMU, read_feat_mtpmu_id_field(), "MTPMU", 1, 1);
/* v8.7 features */
check_feature(ENABLE_FEAT_HCX, read_feat_hcx_id_field(), "HCX", 1, 1);
/* v8.9 features */
check_feature(ENABLE_FEAT_TCR2, read_feat_tcr2_id_field(),
"TCR2", 1, 1);
check_feature(ENABLE_FEAT_S2PIE, read_feat_s2pie_id_field(),
"S2PIE", 1, 1);
check_feature(ENABLE_FEAT_S1PIE, read_feat_s1pie_id_field(),
"S1PIE", 1, 1);
check_feature(ENABLE_FEAT_S2POE, read_feat_s2poe_id_field(),
"S2POE", 1, 1);
check_feature(ENABLE_FEAT_S1POE, read_feat_s1poe_id_field(),
"S1POE", 1, 1);
check_feature(ENABLE_FEAT_MTE_PERM, read_feat_mte_perm_id_field(),
"MTE_PERM", 1, 1);
/* v9.0 features */
check_feature(ENABLE_BRBE_FOR_NS, read_feat_brbe_id_field(),
"BRBE", 1, 2);
check_feature(ENABLE_TRBE_FOR_NS, read_feat_trbe_id_field(),
"TRBE", 1, 1);
/* v9.2 features */
check_feature(ENABLE_SME_FOR_NS, read_feat_sme_id_field(),
"SME", 1, 2);
check_feature(ENABLE_SME2_FOR_NS, read_feat_sme_id_field(),
"SME2", 2, 2);
/* v9.4 features */
check_feature(ENABLE_FEAT_GCS, read_feat_gcs_id_field(), "GCS", 1, 1);
read_feat_rme();
if (tainted) {
panic();
}
}

80
common/image_decompress.c Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdint.h>
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/image_decompress.h>
static uintptr_t decompressor_buf_base;
static uint32_t decompressor_buf_size;
static decompressor_t *decompressor;
static struct image_info saved_image_info;
void image_decompress_init(uintptr_t buf_base, uint32_t buf_size,
decompressor_t *_decompressor)
{
decompressor_buf_base = buf_base;
decompressor_buf_size = buf_size;
decompressor = _decompressor;
}
void image_decompress_prepare(struct image_info *info)
{
/*
* If the image is compressed, it should be loaded into the temporary
* buffer instead of its final destination. We save image_info, then
* override ->image_base and ->image_max_size so that load_image() will
* transfer the compressed data to the temporary buffer.
*/
saved_image_info = *info;
info->image_base = decompressor_buf_base;
info->image_max_size = decompressor_buf_size;
}
int image_decompress(struct image_info *info)
{
uintptr_t compressed_image_base, image_base, work_base;
uint32_t compressed_image_size, work_size;
int ret;
/*
* The size of compressed data has been filled by load_image().
* Read it out before restoring image_info.
*/
compressed_image_size = info->image_size;
compressed_image_base = info->image_base;
*info = saved_image_info;
assert(compressed_image_size <= decompressor_buf_size);
image_base = info->image_base;
/*
* Use the rest of the temporary buffer as workspace of the
* decompressor since the decompressor may need additional memory.
*/
work_base = compressed_image_base + compressed_image_size;
work_size = decompressor_buf_size - compressed_image_size;
ret = decompressor(&compressed_image_base, compressed_image_size,
&image_base, info->image_max_size,
work_base, work_size);
if (ret) {
ERROR("Failed to decompress image (err=%d)\n", ret);
return ret;
}
/* image_base is updated to the final pos when decompressor() exits. */
info->image_size = image_base - info->image_base;
flush_dcache_range(info->image_base, info->image_size);
return 0;
}

154
common/runtime_svc.c Normal file
View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
/*******************************************************************************
* The 'rt_svc_descs' array holds the runtime service descriptors exported by
* services by placing them in the 'rt_svc_descs' linker section.
* The 'rt_svc_descs_indices' array holds the index of a descriptor in the
* 'rt_svc_descs' array. When an SMC arrives, the OEN[29:24] bits and the call
* type[31] bit in the function id are combined to get an index into the
* 'rt_svc_descs_indices' array. This gives the index of the descriptor in the
* 'rt_svc_descs' array which contains the SMC handler.
******************************************************************************/
uint8_t rt_svc_descs_indices[MAX_RT_SVCS];
#define RT_SVC_DECS_NUM ((RT_SVC_DESCS_END - RT_SVC_DESCS_START)\
/ sizeof(rt_svc_desc_t))
/*******************************************************************************
* Function to invoke the registered `handle` corresponding to the smc_fid in
* AArch32 mode.
******************************************************************************/
uintptr_t handle_runtime_svc(uint32_t smc_fid,
void *cookie,
void *handle,
unsigned int flags)
{
u_register_t x1, x2, x3, x4;
unsigned int index;
unsigned int idx;
const rt_svc_desc_t *rt_svc_descs;
assert(handle != NULL);
idx = get_unique_oen_from_smc_fid(smc_fid);
assert(idx < MAX_RT_SVCS);
index = rt_svc_descs_indices[idx];
if (index >= RT_SVC_DECS_NUM)
SMC_RET1(handle, SMC_UNK);
rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;
get_smc_params_from_ctx(handle, x1, x2, x3, x4);
return rt_svc_descs[index].handle(smc_fid, x1, x2, x3, x4, cookie,
handle, flags);
}
/*******************************************************************************
* Simple routine to sanity check a runtime service descriptor before using it
******************************************************************************/
static int32_t validate_rt_svc_desc(const rt_svc_desc_t *desc)
{
if (desc == NULL)
return -EINVAL;
if (desc->start_oen > desc->end_oen)
return -EINVAL;
if (desc->end_oen >= OEN_LIMIT)
return -EINVAL;
if ((desc->call_type != SMC_TYPE_FAST) &&
(desc->call_type != SMC_TYPE_YIELD))
return -EINVAL;
/* A runtime service having no init or handle function doesn't make sense */
if ((desc->init == NULL) && (desc->handle == NULL))
return -EINVAL;
return 0;
}
/*******************************************************************************
* This function calls the initialisation routine in the descriptor exported by
* a runtime service. Once a descriptor has been validated, its start & end
* owning entity numbers and the call type are combined to form a unique oen.
* The unique oen is used as an index into the 'rt_svc_descs_indices' array.
* The index of the runtime service descriptor is stored at this index.
******************************************************************************/
void __init runtime_svc_init(void)
{
int rc = 0;
uint8_t index, start_idx, end_idx;
rt_svc_desc_t *rt_svc_descs;
/* Assert the number of descriptors detected are less than maximum indices */
assert((RT_SVC_DESCS_END >= RT_SVC_DESCS_START) &&
(RT_SVC_DECS_NUM < MAX_RT_SVCS));
/* If no runtime services are implemented then simply bail out */
if (RT_SVC_DECS_NUM == 0U)
return;
/* Initialise internal variables to invalid state */
(void)memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));
rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;
for (index = 0U; index < RT_SVC_DECS_NUM; index++) {
rt_svc_desc_t *service = &rt_svc_descs[index];
/*
* An invalid descriptor is an error condition since it is
* difficult to predict the system behaviour in the absence
* of this service.
*/
rc = validate_rt_svc_desc(service);
if (rc != 0) {
ERROR("Invalid runtime service descriptor %p\n",
(void *) service);
panic();
}
/*
* The runtime service may have separate rt_svc_desc_t
* for its fast smc and yielding smc. Since the service itself
* need to be initialized only once, only one of them will have
* an initialisation routine defined. Call the initialisation
* routine for this runtime service, if it is defined.
*/
if (service->init != NULL) {
rc = service->init();
if (rc != 0) {
ERROR("Error initializing runtime service %s\n",
service->name);
continue;
}
}
/*
* Fill the indices corresponding to the start and end
* owning entity numbers with the index of the
* descriptor which will handle the SMCs for this owning
* entity range.
*/
start_idx = (uint8_t)get_unique_oen(service->start_oen,
service->call_type);
end_idx = (uint8_t)get_unique_oen(service->end_oen,
service->call_type);
assert(start_idx <= end_idx);
assert(end_idx < MAX_RT_SVCS);
for (; start_idx <= end_idx; start_idx++)
rt_svc_descs_indices[start_idx] = index;
}
}

45
common/tf_crc32.c Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdarg.h>
#include <assert.h>
#include <arm_acle.h>
#include <common/debug.h>
#include <common/tf_crc32.h>
/* compute CRC using Arm intrinsic function
*
* This function is useful for the platforms with the CPU ARMv8.0
* (with CRC instructions supported), and onwards.
* Platforms with CPU ARMv8.0 should make sure to add a compile switch
* '-march=armv8-a+crc" for successful compilation of this file.
*
* @crc: previous accumulated CRC
* @buf: buffer base address
* @size: the size of the buffer
*
* Return calculated CRC value
*/
uint32_t tf_crc32(uint32_t crc, const unsigned char *buf, size_t size)
{
assert(buf != NULL);
uint32_t calc_crc = ~crc;
const unsigned char *local_buf = buf;
size_t local_size = size;
/*
* calculate CRC over byte data
*/
while (local_size != 0UL) {
calc_crc = __crc32b(calc_crc, *local_buf);
local_buf++;
local_size--;
}
return ~calc_crc;
}

79
common/tf_log.c Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdarg.h>
#include <assert.h>
#include <stdio.h>
#include <common/debug.h>
#include <plat/common/platform.h>
/* Set the default maximum log level to the `LOG_LEVEL` build flag */
static unsigned int max_log_level = LOG_LEVEL;
/*
* The common log function which is invoked by TF-A code.
* This function should not be directly invoked and is meant to be
* only used by the log macros defined in debug.h. The function
* expects the first character in the format string to be one of the
* LOG_MARKER_* macros defined in debug.h.
*/
void tf_log(const char *fmt, ...)
{
unsigned int log_level;
va_list args;
const char *prefix_str;
/* We expect the LOG_MARKER_* macro as the first character */
log_level = fmt[0];
/* Verify that log_level is one of LOG_MARKER_* macro defined in debug.h */
assert((log_level > 0U) && (log_level <= LOG_LEVEL_VERBOSE));
assert((log_level % 10U) == 0U);
if (log_level > max_log_level)
return;
prefix_str = plat_log_get_prefix(log_level);
while (*prefix_str != '\0') {
(void)putchar(*prefix_str);
prefix_str++;
}
va_start(args, fmt);
(void)vprintf(fmt + 1, args);
va_end(args);
}
void tf_log_newline(const char log_fmt[2])
{
unsigned int log_level = log_fmt[0];
/* Verify that log_level is one of LOG_MARKER_* macro defined in debug.h */
assert((log_level > 0U) && (log_level <= LOG_LEVEL_VERBOSE));
assert((log_level % 10U) == 0U);
if (log_level > max_log_level)
return;
putchar('\n');
}
/*
* The helper function to set the log level dynamically by platform. The
* maximum log level is determined by `LOG_LEVEL` build flag at compile time
* and this helper can set a lower (or equal) log level than the one at compile.
*/
void tf_log_set_max_level(unsigned int log_level)
{
assert(log_level <= LOG_LEVEL_VERBOSE);
assert((log_level % 10U) == 0U);
/* Cap log_level to the compile time maximum. */
if (log_level <= (unsigned int)LOG_LEVEL)
max_log_level = log_level;
}

Some files were not shown because too many files have changed in this diff Show More