New build
This commit is contained in:
commit
d867bcfd20
91
.checkpatch.conf
Normal file
91
.checkpatch.conf
Normal 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
73
.commitlintrc.js
Normal 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 */
|
||||
},
|
||||
};
|
||||
72
.editorconfig
Normal file
72
.editorconfig
Normal 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
1
.husky/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_
|
||||
7
.husky/commit-msg
Executable file
7
.husky/commit-msg
Executable 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
3
.husky/commit-msg.commitlint
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
||||
194
.husky/commit-msg.gerrit
Executable file
194
.husky/commit-msg.gerrit
Executable 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
6
.husky/pre-commit
Executable 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
63
.husky/pre-commit.copyright
Executable 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
8
.husky/prepare-commit-msg
Executable 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
28
.husky/prepare-commit-msg.cz
Executable 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
|
||||
32
.readthedocs.yaml
Normal file
32
.readthedocs.yaml
Normal 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
151
.versionrc.js
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
15
bl1/aarch32/bl1_arch_setup.c
Normal file
15
bl1/aarch32/bl1_arch_setup.c
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
172
bl1/aarch32/bl1_context_mgmt.c
Normal file
172
bl1/aarch32/bl1_context_mgmt.c
Normal 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);
|
||||
}
|
||||
99
bl1/aarch32/bl1_entrypoint.S
Normal file
99
bl1/aarch32/bl1_entrypoint.S
Normal 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
|
||||
165
bl1/aarch32/bl1_exceptions.S
Normal file
165
bl1/aarch32/bl1_exceptions.S
Normal 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
|
||||
19
bl1/aarch64/bl1_arch_setup.c
Normal file
19
bl1/aarch64/bl1_arch_setup.c
Normal 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);
|
||||
}
|
||||
131
bl1/aarch64/bl1_context_mgmt.c
Normal file
131
bl1/aarch64/bl1_context_mgmt.c
Normal 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 */
|
||||
108
bl1/aarch64/bl1_entrypoint.S
Normal file
108
bl1/aarch64/bl1_entrypoint.S
Normal 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
|
||||
287
bl1/aarch64/bl1_exceptions.S
Normal file
287
bl1/aarch64/bl1_exceptions.S
Normal 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
169
bl1/bl1.ld.S
Normal 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
38
bl1/bl1.mk
Normal 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
745
bl1/bl1_fwu.c
Normal 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
302
bl1/bl1_main.c
Normal 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
33
bl1/bl1_private.h
Normal 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
65
bl1/tbbr/tbbr_img_desc.c
Normal 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,
|
||||
}
|
||||
};
|
||||
16
bl2/aarch32/bl2_arch_setup.c
Normal file
16
bl2/aarch32/bl2_arch_setup.c
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
57
bl2/aarch32/bl2_el3_entrypoint.S
Normal file
57
bl2/aarch32/bl2_el3_entrypoint.S
Normal 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
|
||||
21
bl2/aarch32/bl2_el3_exceptions.S
Normal file
21
bl2/aarch32/bl2_el3_exceptions.S
Normal 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 */
|
||||
136
bl2/aarch32/bl2_entrypoint.S
Normal file
136
bl2/aarch32/bl2_entrypoint.S
Normal 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
|
||||
46
bl2/aarch32/bl2_run_next_image.S
Normal file
46
bl2/aarch32/bl2_run_next_image.S
Normal 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
|
||||
19
bl2/aarch64/bl2_arch_setup.c
Normal file
19
bl2/aarch64/bl2_arch_setup.c
Normal 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));
|
||||
}
|
||||
72
bl2/aarch64/bl2_el3_entrypoint.S
Normal file
72
bl2/aarch64/bl2_el3_entrypoint.S
Normal 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
|
||||
131
bl2/aarch64/bl2_el3_exceptions.S
Normal file
131
bl2/aarch64/bl2_el3_exceptions.S
Normal 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
|
||||
141
bl2/aarch64/bl2_entrypoint.S
Normal file
141
bl2/aarch64/bl2_entrypoint.S
Normal 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
|
||||
67
bl2/aarch64/bl2_rme_entrypoint.S
Normal file
67
bl2/aarch64/bl2_rme_entrypoint.S
Normal 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
|
||||
36
bl2/aarch64/bl2_run_next_image.S
Normal file
36
bl2/aarch64/bl2_run_next_image.S
Normal 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
133
bl2/bl2.ld.S
Normal 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
55
bl2/bl2.mk
Normal 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
221
bl2/bl2_el3.ld.S
Normal 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
110
bl2/bl2_image_load_v2.c
Normal 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
169
bl2/bl2_main.c
Normal 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
24
bl2/bl2_private.h
Normal 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 */
|
||||
127
bl2u/aarch32/bl2u_entrypoint.S
Normal file
127
bl2u/aarch32/bl2u_entrypoint.S
Normal 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
|
||||
129
bl2u/aarch64/bl2u_entrypoint.S
Normal file
129
bl2u/aarch64/bl2u_entrypoint.S
Normal 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
123
bl2u/bl2u.ld.S
Normal 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
21
bl2u/bl2u.mk
Normal 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
65
bl2u/bl2u_main.c
Normal 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();
|
||||
}
|
||||
229
bl31/aarch64/bl31_entrypoint.S
Normal file
229
bl31/aarch64/bl31_entrypoint.S
Normal 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
|
||||
469
bl31/aarch64/crash_reporting.S
Normal file
469
bl31/aarch64/crash_reporting.S
Normal 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
305
bl31/aarch64/ea_delegate.S
Normal 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
|
||||
645
bl31/aarch64/runtime_exceptions.S
Normal file
645
bl31/aarch64/runtime_exceptions.S
Normal 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
210
bl31/bl31.ld.S
Normal 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
184
bl31/bl31.mk
Normal 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
66
bl31/bl31_context_mgmt.c
Normal 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
303
bl31/bl31_main.c
Normal 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
30
bl31/bl31_traps.c
Normal 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
533
bl31/ehf.c
Normal 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
227
bl31/interrupt_mgmt.c
Normal 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
15
bl32/optee/optee.mk
Normal 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)
|
||||
367
bl32/sp_min/aarch32/entrypoint.S
Normal file
367
bl32/sp_min/aarch32/entrypoint.S
Normal 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
157
bl32/sp_min/sp_min.ld.S
Normal 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
84
bl32/sp_min/sp_min.mk
Normal 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
250
bl32/sp_min/sp_min_main.c
Normal 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 */
|
||||
14
bl32/sp_min/sp_min_private.h
Normal file
14
bl32/sp_min/sp_min_private.h
Normal 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 */
|
||||
74
bl32/sp_min/wa_cve_2017_5715_bpiall.S
Normal file
74
bl32/sp_min/wa_cve_2017_5715_bpiall.S
Normal 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 */
|
||||
75
bl32/sp_min/wa_cve_2017_5715_icache_inv.S
Normal file
75
bl32/sp_min/wa_cve_2017_5715_icache_inv.S
Normal 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 */
|
||||
489
bl32/tsp/aarch64/tsp_entrypoint.S
Normal file
489
bl32/tsp/aarch64/tsp_entrypoint.S
Normal 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
|
||||
162
bl32/tsp/aarch64/tsp_exceptions.S
Normal file
162
bl32/tsp/aarch64/tsp_exceptions.S
Normal 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
|
||||
30
bl32/tsp/aarch64/tsp_request.S
Normal file
30
bl32/tsp/aarch64/tsp_request.S
Normal 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
252
bl32/tsp/ffa_helpers.c
Normal 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
116
bl32/tsp/ffa_helpers.h
Normal 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
129
bl32/tsp/tsp.ld.S
Normal 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
49
bl32/tsp/tsp.mk
Normal 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
156
bl32/tsp/tsp_common.c
Normal 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
656
bl32/tsp/tsp_ffa_main.c
Normal 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
115
bl32/tsp/tsp_interrupt.c
Normal 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
288
bl32/tsp/tsp_main.c
Normal 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
145
bl32/tsp/tsp_private.h
Normal 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
91
bl32/tsp/tsp_timer.c
Normal 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
1378
changelog.yaml
Normal file
File diff suppressed because it is too large
Load Diff
239
common/aarch32/debug.S
Normal file
239
common/aarch32/debug.S
Normal 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
202
common/aarch64/debug.S
Normal 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
|
||||
129
common/aarch64/early_exceptions.S
Normal file
129
common/aarch64/early_exceptions.S
Normal 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
|
||||
266
common/backtrace/backtrace.c
Normal file
266
common/backtrace/backtrace.c
Normal 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);
|
||||
}
|
||||
31
common/backtrace/backtrace.mk
Normal file
31
common/backtrace/backtrace.mk
Normal 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
280
common/bl_common.c
Normal 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
351
common/desc_image_load.c
Normal 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
626
common/fdt_fixup.c
Normal 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 = ®_32;
|
||||
} else {
|
||||
reg_64 = cpu_to_fdt64(gicr_base);
|
||||
val = ®_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 = ®_32;
|
||||
} else {
|
||||
reg_64 = cpu_to_fdt64(nr_cores * (uint64_t)gicr_frame_size);
|
||||
val = ®_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
641
common/fdt_wrappers.c
Normal 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
7
common/fdt_wrappers.mk
Normal 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
239
common/feat_detect.c
Normal 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
80
common/image_decompress.c
Normal 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
154
common/runtime_svc.c
Normal 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
45
common/tf_crc32.c
Normal 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
79
common/tf_log.c
Normal 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
Loading…
x
Reference in New Issue
Block a user