Compare commits

...

No commits in common. "main" and "master" have entirely different histories.
main ... master

5542 changed files with 1081588 additions and 0 deletions

91
.checkpatch.conf Normal file
View File

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

199
.clang-format Normal file
View File

@ -0,0 +1,199 @@
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: false
ForEachMacros:
- fdt_for_each_compatible_node
- fdt_for_each_property_offset
- fdt_for_each_subnode
- for_each_err_record_info
- for_each_subscriber
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|iso646|limits|locale|math|setjmp|signal|stdalign|stdarg|stdatomic|stdbool|stdckdint|stddef|stdint|stdio|stdlib|stdnoreturn|string|tgmath|threads|time|uchar|wchar|wctype)\.h>$'
Priority: 0
SortPriority: 0
CaseSensitive: false
- Regex: '^<lib/(compiler-rt|libfdt|mbedtls|zlib)/.+>$'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<(platform(_def)?\.h)|(plat[_/].+)>$'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.+>$'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '^".+"$'
Priority: 4
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 8
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 8
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 10
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseInsensitive
SortJavaStaticImport: Before
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptControlMacros
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: false
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: false
AfterOverloadedOperator: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: c++03
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Always
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

72
.commitlintrc.js Normal file
View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2021-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* eslint-env es6 */
"use strict";
import fs from "fs";
import rules from "@commitlint/rules";
import yaml from "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;
}
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 */
export default {
extends: ["@commitlint/config-conventional"],
plugins: [
{
rules: {
"signed-off-by-exists": rules["trailer-exists"],
"change-id-exists": rules["trailer-exists"],
},
},
],
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": [2, "always", scopes] /* Error */
},
};

4
.ctags Normal file
View File

@ -0,0 +1,4 @@
--regex-Asm=/^func[ \t]+([a-zA-Z_0-9]+)$/\1/l,function/
--regex-Asm=/^.*\.macro[ \t]+([a-zA-Z_0-9]+)$/\1/m,macro/
--regex-Asm=/^vector_entry[ \t]+([a-zA-Z_0-9]+)$/\1/l,function/
--regex-Asm=/^.equ[ \t]+([a-zA-Z_0-9]+),/\1/l,name/

15
.cz-adapter.cjs Normal file
View File

@ -0,0 +1,15 @@
/*
* Copyright (c) 2024, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* A workaround for:
*
* https://github.com/conventional-changelog/commitlint/issues/3949
*/
exports.prompter = async (inquirerIns, commit) => {
; (await import('@commitlint/cz-commitlint')).prompter(inquirerIns, commit)
}

3
.cz.json Normal file
View File

@ -0,0 +1,3 @@
{
"path": "./.cz-adapter.cjs"
}

75
.editorconfig Normal file
View File

@ -0,0 +1,75 @@
#
# Copyright (c) 2017-2024, 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
[.git/COMMIT_EDITMSG]
max_line_length = 72

5
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,5 @@
package.json @CJKay
package-lock.json @CJKay
pyproject.toml @CJKay
poetry.lock @CJKay

89
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,89 @@
version: 2
updates:
- target-branch: "main"
package-ecosystem: "npm"
versioning-strategy: "lockfile-only"
directories: ["/", "/tools/conventional-changelog-tf-a"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["minor", "patch"]
- target-branch: "lts-v2.12"
package-ecosystem: "npm"
versioning-strategy: "lockfile-only"
directories: ["/", "/tools/conventional-changelog-tf-a"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["patch"]
- target-branch: "lts-v2.10"
package-ecosystem: "npm"
versioning-strategy: "lockfile-only"
directories: ["/", "/tools/conventional-changelog-tf-a"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["patch"]
- target-branch: "lts-v2.8"
package-ecosystem: "npm"
versioning-strategy: "lockfile-only"
directories: ["/", "/tools/conventional-changelog-tf-a"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["patch"]
- target-branch: "main"
package-ecosystem: "pip"
versioning-strategy: "lockfile-only"
directories: ["/", "/tools/cot_dt2c", "/tools/memory", "/tools/tlc"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["minor", "patch"]
- target-branch: "lts-v2.12"
package-ecosystem: "pip"
versioning-strategy: "lockfile-only"
directories: ["/", "/tools/cot_dt2c", "/tools/memory", "/tools/tlc"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["patch"]
- target-branch: "lts-v2.10"
package-ecosystem: "pip"
versioning-strategy: "lockfile-only"
directories: ["/"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["patch"]
- target-branch: "lts-v2.8"
package-ecosystem: "pip"
versioning-strategy: "lockfile-only"
directories: ["/"]
schedule:
interval: "daily"
groups:
dev-deps:
patterns: ["*"]
update-types: ["patch"]

55
.gitignore vendored Normal file
View File

@ -0,0 +1,55 @@
# Ignore miscellaneous files
cscope.*
*.swp
*.patch
*~
.project
.cproject
# Ignore build directory
build/
# Ignore build products from tools
tools/**/*.o
tools/**/*.d
tools/renesas/rcar_layout_create/*.bin
tools/renesas/rcar_layout_create/*.srec
tools/renesas/rcar_layout_create/*.map
tools/renesas/rcar_layout_create/*.elf
tools/renesas/rzg_layout_create/*.bin
tools/renesas/rzg_layout_create/*.srec
tools/renesas/rzg_layout_create/*.map
tools/renesas/rzg_layout_create/*.elf
tools/fiptool/fiptool
tools/fiptool/fiptool.exe
tools/memory/src/memory/__pycache__/
tools/cert_create/src/*.o
tools/cert_create/src/**/*.o
tools/cert_create/cert_create
tools/cert_create/cert_create.exe
tools/marvell/doimage/doimage
tools/amlogic/doimage
tools/stm32image/*.o
tools/stm32image/stm32image
tools/stm32image/stm32image.exe
tools/sptool/__pycache__/
tools/encrypt_fw/encrypt_fw
tools/encrypt_fw/encrypt_fw.exe
# GNU GLOBAL files
GPATH
GRTAGS
GSYMS
GTAGS
# Ctags
tags
# Node.js
node_modules/
# common python virtual environment directories
.env/
env/
.venv/
venv/

16
.gitmodules vendored Normal file
View File

@ -0,0 +1,16 @@
[submodule "libtl"]
path = contrib/libtl
url = https://review.trustedfirmware.org/shared/transfer-list-library
shallow = true
[submodule "libeventlog"]
path = contrib/libeventlog
url = https://review.trustedfirmware.org/shared/libEventLog
shallow = true
[submodule "contrib/libtpm"]
path = contrib/libtpm
url = https://review.trustedfirmware.org/shared/libTPM
shallow = true
[submodule "mbed-tls"]
path = contrib/mbed-tls
url = https://github.com/Mbed-TLS/mbedtls.git
shallow = true

5
.gitreview Normal file
View File

@ -0,0 +1,5 @@
[gerrit]
host=review.trustedfirmware.org
port=29418
project=TF-A/trusted-firmware-a
defaultbranch=integration

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

4
.husky/commit-msg Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
"$(dirname "$0")/commit-msg.gerrit" "$@"
"$(dirname "$0")/commit-msg.commitlint" "$@"

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

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

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

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

3
.husky/pre-commit Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
"$(dirname "$0")/pre-commit.copyright" "$@"

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

@ -0,0 +1,91 @@
#!/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
PLATPROV=
ORG=`echo "$GIT_AUTHOR_EMAIL" | awk -F '[@]' '{ print $2;}'`
case $ORG in
amd.com)
PLATPROV="Advanced Micro Devices, Inc. All rights reserved."
;;
*arm.com)
PLATPROV="$ARM_RGX"
;;
*)
;;
esac
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.*$PLATPROV" "$FILE"
echo
}
while read -r FILE; do
if [ -z "$FILE" ]
then
break
fi
# Check if copyright header exists for the org
if ! grep "opyright.*$YEAR_RGX.*$PLATPROV" "$FILE">/dev/null 2>&1 && [[ $ORG != *arm* ]]
then
echo -e "Copyright header ""$RED""$PLATPROV""$BLANK"" is missing in ""$YELLOW""$FILE""$BLANK"
fi
# Check if the copyright year is updated for the org and update it
if [ ! -z "$PLATPROV" ]
then
if ! grep "opyright.*$YEAR_NOW.*$PLATPROV" "$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.*$PLATPROV" "$FILE" >/dev/null 2>&1
then
exit_code=1
sed -i "s/\(opyright.*\)$YEAR_RGX\(.*$PLATPROV\)/\1$(date +"%Y")\2/" $FILE
user_warning
# If it is single "date" type of entry - add the copyright extension to current year.
elif grep "opyright.*$YEAR_RGX.*$PLATPROV" "$FILE" >/dev/null 2>&1
then
exit_code=1
sed -i "s/\(opyright.*$YEAR_RGX\)\(.*$PLATPROV\)/\1-$(date +"%Y")\2/" $FILE
user_warning
fi
# Even if the year is correct - verify that Arm copyright is formatted correctly.
if [[ $ORG == *arm* ]]
then
if grep "opyright.*\(ARM\|arm\)" "$FILE">/dev/null 2>&1
then
exit_code=1
sed -i "s/\(opyright.*\)\(ARM\|arm\)/\1Arm/" $FILE
user_warning
fi
fi
fi
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

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

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

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

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

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v20.11.1

34
.readthedocs.yaml Normal file
View File

@ -0,0 +1,34 @@
# Copyright (c) 2023-2025, 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
submodules:
include: all
recursive: true
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"
post_install:
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --no-root --with docs
sphinx:
configuration: docs/conf.py
# Auxiliary formats to export to (in addition to the default HTML output).
formats:
- pdf

151
.versionrc.cjs Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2021-2024, 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[3]}`;
},
"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[3]}`;
},
"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;
}
}
}
]
};

26
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"files.exclude": {
"**/.mypy_cache/**": true,
"**/.pytest_cache/**": true,
"**/.ruff_cache/**": true,
"**/.venv/**": true,
"**/node_modules/**": true,
"build/**": true,
},
"files.watcherExclude": {
"**/.mypy_cache/**": true,
"**/.pytest_cache/**": true,
"**/.ruff_cache/**": true,
"**/.venv/**": true,
"**/node_modules/**": true,
"build/**": true,
},
"search.exclude": {
"**/.mypy_cache/**": true,
"**/.pytest_cache/**": true,
"**/.ruff_cache/**": true,
"**/.venv/**": true,
"**/node_modules/**": true,
"build/**": true,
},
}

1322
Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016-2025, 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
/* -----------------------------------------------------
* Jump to main function.
* -----------------------------------------------------
*/
bl bl1_main
/* -----------------------------------------------------
* Jump to next image.
* -----------------------------------------------------
*/
/*
* Get the smc_context for next BL image,
* program the gp/system registers and save it in `r4`.
*/
bl smc_get_next_ctx
mov r4, r0
/* Only turn-off MMU if going to secure world */
ldr r5, [r4, #SMC_CTX_SCR]
tst r5, #SCR_NS_BIT
bne skip_mmu_off
/*
* MMU needs to be disabled because both BL1 and BL2/BL2U execute
* in PL1, and therefore share the same address space.
* BL2/BL2U will initialize the address space according to its
* own requirement.
*/
bl disable_mmu_icache_secure
stcopr r0, TLBIALL
dsb sy
isb
skip_mmu_off:
/* Restore smc_context from `r4` and exit secure monitor mode. */
mov r0, r4
monitor_exit
endfunc bl1_entrypoint

View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2016-2025, 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_aarch32
/* Get the smc_context for next BL image */
bl smc_get_next_ctx
mov r4, r0
/* Only turn-off MMU if going to secure world */
ldr r5, [r4, #SMC_CTX_SCR]
tst r5, #SCR_NS_BIT
bne skip_mmu_off
/* Disable the MMU */
bl disable_mmu_icache_secure
stcopr r0, TLBIALL
dsb sy
isb
skip_mmu_off:
/* -----------------------------------------------------
* Do the transition to next BL image.
* -----------------------------------------------------
*/
mov r0, r4
monitor_exit
endfunc smc_handler

View File

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

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2015-2025, 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"
entry_point_info_t *bl2_ep_info;
/*
* 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];
void *cm_get_context(size_t security_state)
{
assert(sec_state_is_valid(security_state));
return &bl1_cpu_context[security_state];
}
#if BL2_RUNS_AT_EL3
/*******************************************************************************
* This function prepares the entry point information to run BL2 in EL3. BL2 is
* set to run at EL3 in case of RESET_TO_BL2=1 (not applicable for BL1) or 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)
{
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);
/* 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 /* BL2_RUNS_AT_EL3 */

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2013-2025, 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
/* -----------------------------------------------------
* 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
/* --------------------------------------------------------------------
* Initialize platform and jump to our c-entry point
* for this type of reset.
* --------------------------------------------------------------------
*/
bl bl1_main
/* --------------------------------------------------
* Do the transition to next boot image. BL2 is always compiled to run at
* EL3 when BL2_RUNS_AT_EL3 is set.
* --------------------------------------------------
*/
#if BL2_RUNS_AT_EL3
b bl1_run_bl2_in_el3
#else
b el3_exit
#endif
endfunc bl1_entrypoint
/* -----------------------------------------------------
* void bl1_run_bl2_in_el3();
* This function runs BL2 in EL3.
* -----------------------------------------------------
*/
#if BL2_RUNS_AT_EL3
func bl1_run_bl2_in_el3
/* 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_el3
#endif /* BL2_RUNS_AT_EL3 */

View File

@ -0,0 +1,226 @@
/*
* Copyright (c) 2013-2025, 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 unexpected_sync_exception
mov x0, #SYNC_EXCEPTION_AARCH64
bl plat_report_exception
no_ret plat_panic_handler
endfunc unexpected_sync_exception
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
smc_handler:
bl prepare_el3_entry
/* -----------------------------------------------------
* Go to BL1 SMC handler.
* -----------------------------------------------------
*/
bl bl1_smc_wrapper_aarch64
/* -----------------------------------------------------
* Do the transition to next BL image.
* -----------------------------------------------------
*/
b el3_exit
endfunc smc_handler64

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

@ -0,0 +1,179 @@
/*
* Copyright (c) 2013-2024, 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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text address is not aligned on a page boundary.");
__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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro address is not aligned on a page boundary.");
__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.")
__RW_START__ = .;
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 */
__RW_END__ = .;
__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 = .;
}

53
bl1/bl1.mk Normal file
View File

@ -0,0 +1,53 @@
#
# Copyright (c) 2013-2026, 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/el3_runtime/aarch64/context.S \
lib/cpus/errata_common.c
ifeq (${WORKAROUND_CVE_2025_0647},1)
BL1_SOURCES += lib/cpus/aarch64/wa_cve_2025_0647_cpprctx.S
endif
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
BL1_DEFAULT_LINKER_SCRIPT_SOURCE := bl1/bl1.ld.S
# CRYPTO_SUPPORT
NEED_AUTH := $(if $(filter 1,$(TRUSTED_BOARD_BOOT)),1,)
NEED_HASH := $(if $(filter 1,$(MEASURED_BOOT) $(DRTM_SUPPORT)),1,)
$(eval $(call set_crypto_support,NEED_AUTH,NEED_HASH))
# BL1_CPPFLAGS
$(eval BL1_CPPFLAGS += $(call make_defines, \
$(sort \
CRYPTO_SUPPORT \
)))
# Numeric_Flags
$(eval $(call assert_numerics,\
$(sort \
CRYPTO_SUPPORT \
)))

749
bl1/bl1_fwu.c Normal file
View File

@ -0,0 +1,749 @@
/*
* Copyright (c) 2015-2026, 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);
assert(checked_desc != NULL);
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)) {
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);
(void)bl1_fwu_remove_loaded_id(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)) {
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;
}

322
bl1/bl1_main.c Normal file
View File

@ -0,0 +1,322 @@
/*
* Copyright (c) 2013-2026, 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/build_message.h>
#include <common/debug.h>
#include <context.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/el3_runtime/context_mgmt.h>
#include <lib/extensions/pauth.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
/*******************************************************************************
* Setup function for BL1.
* Also 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 __no_pauth bl1_main(void)
{
unsigned int image_id;
/* Enable early console if EARLY_CONSOLE flag is enabled */
plat_setup_early_console();
/* Perform early platform-specific setup */
bl1_early_platform_setup();
/* Perform late platform-specific setup */
bl1_plat_arch_setup();
/* Init registers that don't get contexted */
cm_manage_extensions_el3(plat_my_core_pos());
/* When BL2 runs in Secure world, it needs a coherent context. */
#if !BL2_RUNS_AT_EL3
/* Init per-world context registers. */
cm_manage_extensions_per_world();
#endif
#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", build_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();
if (is_feat_crypto_supported()) {
disable_fpregs_traps_el3();
}
/* Perform platform setup in BL1. */
bl1_platform_setup();
/* 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) {
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_AUTH_START, PMF_CACHE_MAINT);
#endif
bl1_load_bl2();
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_AUTH_END, PMF_CACHE_MAINT);
#endif
} else {
NOTICE("BL1-FWU: *******FWU Process Started*******\n");
}
if (is_feat_crypto_supported()) {
enable_fpregs_traps_el3();
}
/* Teardown the measured boot driver */
bl1_plat_mboot_finish();
crypto_mod_finish();
bl1_prepare_next_image(image_id);
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL1_EXIT, PMF_CACHE_MAINT);
#endif
console_flush();
/* Disable pointer authentication before jumping to next boot image. */
if (is_feat_pauth_supported()) {
pauth_disable_el3();
}
}
/*******************************************************************************
* 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);
}
}
#if __aarch64__
u_register_t bl1_smc_wrapper_aarch64(cpu_context_t *ctx)
{
u_register_t x1, x2, x3, x4;
unsigned int smc_fid, flags;
gp_regs_t *gpregs = get_gpregs_ctx(ctx);
smc_fid = read_ctx_reg(gpregs, CTX_GPREG_X0);
x1 = read_ctx_reg(gpregs, CTX_GPREG_X1);
x2 = read_ctx_reg(gpregs, CTX_GPREG_X2);
x3 = read_ctx_reg(gpregs, CTX_GPREG_X3);
x4 = read_ctx_reg(gpregs, CTX_GPREG_X4);
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
flags = read_scr_el3() & SCR_NS_BIT;
return bl1_smc_handler(smc_fid, x1, x2, x3, x4, NULL, ctx, flags);
}
#else
/*******************************************************************************
* 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_aarch32(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);
}
#endif

32
bl1/bl1_private.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2013-2025, 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);
u_register_t bl1_fwu_smc_handler(unsigned int smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
unsigned int flags);
#endif /* BL1_PRIVATE_H */

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

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

View File

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

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2017-2025, 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
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

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

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2016-2025, 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 r8, r0
mov r9, r1
mov r10, r2
mov r11, 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, r8
mov r1, r9
mov r2, r10
mov r3, r11
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021-2025, 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_plat_prepare_exit
/*
* Extract PC and SPSR based on struct `entry_point_info_t`
* and load it in LR and SPSR registers respectively.
*/
ldr lr, [r8, #ENTRY_POINT_INFO_PC_OFFSET]
ldr r1, [r8, #(ENTRY_POINT_INFO_PC_OFFSET + 4)]
msr spsr_xc, r1
/* Some BL32 stages expect lr_svc to provide the BL33 entry address */
cps #MODE32_svc
ldr lr, [r8, #ENTRY_POINT_INFO_LR_SVC_OFFSET]
cps #MODE32_mon
add r8, r8, #ENTRY_POINT_INFO_ARGS_OFFSET
ldm r8, {r0, r1, r2, r3}
exception_return
endfunc bl2_run_next_image

View File

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

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2017-2026, 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
/*
* PIE for BL2 image is supported only in case of RESET_TO_BL2=1 and
* BL2_IN_XIP_MEM=0
*/
#if ENABLE_PIE && RESET_TO_BL2 && !BL2_IN_XIP_MEM
#define FIXUP_SIZE ((BL2_LIMIT) - (BL2_BASE))
#else
#define FIXUP_SIZE 0
#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=RESET_TO_BL2 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS && RESET_TO_BL2 \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU && RESET_TO_BL2 \
_init_memory=RESET_TO_BL2 \
_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
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

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

View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2013-2025, 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
#if ENABLE_BTI
/* Enable PAC branch type compatibility */
bic x0, x0, #(SCTLR_BT0_BIT | SCTLR_BT1_BIT)
#endif
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
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl bl2_main
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/
no_ret plat_panic_handler
endfunc bl2_entrypoint

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021-2025, 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_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

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

@ -0,0 +1,135 @@
/*
* Copyright (c) 2013-2025, 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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text address is not aligned on a page boundary.");
__TEXT_START__ = .;
*bl2_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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro address is not aligned on a page boundary.");
__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.")
}

59
bl2/bl2.mk Normal file
View File

@ -0,0 +1,59 @@
#
# Copyright (c) 2013-2025, 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 \
plat/common/${ARCH}/platform_up_stack.S \
${MBEDTLS_SOURCES}
ifeq (${ARCH},aarch64)
BL2_SOURCES += common/aarch64/early_exceptions.S
endif
ifneq (${ENABLE_FEAT_RME},0)
include lib/gpt_rme/gpt_rme.mk
BL2_SOURCES += ${GPT_LIB_SRCS}
endif
ifeq (${BL2_RUNS_AT_EL3},1)
BL2_SOURCES += bl2/${ARCH}/bl2_el3_entrypoint.S \
bl2/${ARCH}/bl2_el3_exceptions.S \
bl2/${ARCH}/bl2_run_next_image.S
BL2_DEFAULT_LINKER_SCRIPT_SOURCE := bl2/bl2_el3.ld.S
else
# Normal operation, no RME, no BL2 at EL3
BL2_SOURCES += bl2/${ARCH}/bl2_entrypoint.S
BL2_DEFAULT_LINKER_SCRIPT_SOURCE := bl2/bl2.ld.S
endif
ifeq (${RESET_TO_BL2},1)
# BL2 at EL3, no RME
BL2_SOURCES += lib/cpus/${ARCH}/cpu_helpers.S
endif
ifeq (${ENABLE_PMF},1)
BL2_SOURCES += lib/pmf/pmf_main.c
endif
# CRYPTO_SUPPORT
NEED_AUTH := $(if $(filter 1,$(TRUSTED_BOARD_BOOT)),1,)
NEED_HASH := $(if $(filter 1,$(MEASURED_BOOT) $(DRTM_SUPPORT)),1,)
$(eval $(call set_crypto_support,NEED_AUTH,NEED_HASH))
# BL2_CPPFLAGS
$(eval BL2_CPPFLAGS += $(call make_defines, \
$(sort \
CRYPTO_SUPPORT \
)))
# Numeric_Flags
$(eval $(call assert_numerics,\
$(sort \
CRYPTO_SUPPORT \
)))

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

@ -0,0 +1,237 @@
/*
* Copyright (c) 2017-2025, 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
/* If platform didn't override BL2_TEXT_RESIDENT_LIMIT then set to 4K */
#ifndef BL2_TEXT_RESIDENT_LIMIT
#define BL2_TEXT_RESIDENT_LIMIT U(0x1000)
#endif
.text . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text address is not aligned on a page boundary.");
__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__ <=
BL2_TEXT_RESIDENT_LIMIT,
"Resident part of BL2 has exceeded its limit.")
#else /* SEPARATE_CODE_AND_RODATA */
.ro . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro address is not aligned on a page boundary.");
__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 */
/* BL1 will have done this if it's built */
#if RESET_TO_BL2
ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__,
"cpu_ops not defined for this platform.")
#endif
#if BL2_IN_XIP_MEM
ROM_REGION_END = .;
. = BL2_RW_BASE;
ASSERT(BL2_RW_BASE == ALIGN(PAGE_SIZE),
"BL2_RW_BASE address is not aligned on a page boundary.")
#endif /* BL2_IN_XIP_MEM */
__RW_START__ = .;
DATA_SECTION >RAM AT>ROM
__DATA_RAM_START__ = __DATA_START__;
__DATA_RAM_END__ = __DATA_END__;
RELA_SECTION >RAM
#if SEPARATE_BL2_NOLOAD_REGION
SAVED_ADDR = .;
. = BL2_NOLOAD_START;
__BL2_NOLOAD_START__ = .;
#endif /* SEPARATE_BL2_NOLOAD_REGION */
STACK_SECTION >RAM_NOLOAD
BSS_SECTION >RAM_NOLOAD
XLAT_TABLE_SECTION >RAM_NOLOAD
#if SEPARATE_BL2_NOLOAD_REGION
__BL2_NOLOAD_END__ = .;
RAM_NOLOAD_REGION_END = .;
. = SAVED_ADDR;
#endif /* SEPARATE_BL2_NOLOAD_REGION */
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure the rest of
* the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL2_END__ = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
#if BL2_IN_XIP_MEM
__BL2_RAM_START__ = ADDR(.data);
__BL2_RAM_END__ = .;
__DATA_ROM_START__ = LOADADDR(.data);
__DATA_SIZE__ = SIZEOF(.data);
/*
* The .data section is the last PROGBITS section so its end marks the end
* of BL2's RO content in XIP memory.
*/
__BL2_ROM_END__ = __DATA_ROM_START__ + __DATA_SIZE__;
ASSERT(__BL2_ROM_END__ <= BL2_RO_LIMIT,
"BL2's RO content has exceeded its limit.")
#endif /* BL2_IN_XIP_MEM */
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
RAM_REGION_END = .;
#if BL2_IN_XIP_MEM
ASSERT(. <= BL2_RW_LIMIT, "BL2's RW content has exceeded its limit.")
#else /* BL2_IN_XIP_MEM */
ASSERT(. <= BL2_LIMIT, "BL2 image has exceeded its limit.")
#endif /* BL2_IN_XIP_MEM */
}

110
bl2/bl2_image_load_v2.c Normal file
View File

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

166
bl2/bl2_main.c Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2013-2026, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch.h>
#include <arch_helpers.h>
#include <arch_features.h>
#include <bl1/bl1.h>
#include <bl2/bl2.h>
#include <common/bl_common.h>
#include <common/build_message.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
/*******************************************************************************
* 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.
******************************************************************************/
void __no_pauth bl2_main(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
entry_point_info_t *next_bl_ep_info;
/* Enable early console if EARLY_CONSOLE flag is enabled */
plat_setup_early_console();
/* Perform early platform-specific setup */
bl2_early_platform_setup2(arg0, arg1, arg2, arg3);
/* Perform remaining generic architectural setup */
bl2_arch_setup();
/* Perform late platform-specific setup */
bl2_plat_arch_setup();
if (is_feat_pauth_supported()) {
#if BL2_RUNS_AT_EL3
pauth_init_enable_el3();
#else
pauth_init_enable_el1();
#endif
}
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_ENTRY, PMF_CACHE_MAINT);
#endif
NOTICE("BL2: %s\n", build_version_string);
NOTICE("BL2: %s\n", build_message);
#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();
if (is_feat_crypto_supported()) {
#if BL2_RUNS_AT_EL3
disable_fpregs_traps_el3();
#endif
}
/* Initialize boot source */
bl2_plat_preload_setup();
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_AUTH_START, PMF_CACHE_MAINT);
#endif
/* Load the subsequent bootloader images. */
next_bl_ep_info = bl2_load_images();
if (is_feat_crypto_supported()) {
#if BL2_RUNS_AT_EL3
enable_fpregs_traps_el3();
#endif
}
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_AUTH_END, PMF_CACHE_MAINT);
#endif
/* Teardown the Measured Boot backend */
bl2_plat_mboot_finish();
crypto_mod_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__ */
/*
* Disable pointer authentication before running next boot image
*/
if (is_feat_pauth_supported()) {
pauth_disable_el1();
}
#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();
/*
* Disable pointer authentication before running next boot image
*/
if (is_feat_pauth_supported()) {
pauth_disable_el3();
}
bl2_run_next_image(next_bl_ep_info);
#endif /* BL2_RUNS_AT_EL3 */
}

24
bl2/bl2_private.h Normal file
View File

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

View File

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

View File

@ -0,0 +1,133 @@
/*
* 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
#if ENABLE_BTI
/* Enable PAC branch type compatibility */
bic x0, x0, #(SCTLR_BT0_BIT | SCTLR_BT1_BIT)
#endif
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

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

@ -0,0 +1,129 @@
/*
* 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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text address is not aligned on a page boundary.");
__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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro address is not aligned on a page boundary.");
__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 = .;
}

32
bl2u/bl2u.mk Normal file
View File

@ -0,0 +1,32 @@
#
# Copyright (c) 2015-2025, 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
# CRYPTO_SUPPORT
NEED_AUTH := $(if $(filter 1,$(TRUSTED_BOARD_BOOT)),1,)
NEED_HASH := $(if $(filter 1,$(MEASURED_BOOT) $(DRTM_SUPPORT)),1,)
$(eval $(call set_crypto_support,NEED_AUTH,NEED_HASH))
# BL2U_CPPFLAGS
$(eval BL2U_CPPFLAGS += $(call make_defines, \
$(sort \
CRYPTO_SUPPORT \
)))
# Numeric_Flags
$(eval $(call assert_numerics,\
$(sort \
CRYPTO_SUPPORT \
)))

66
bl2u/bl2u_main.c Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2015-2024, 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/build_message.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", build_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();
}

BIN
bl31.bin

Binary file not shown.

View File

@ -0,0 +1,222 @@
/*
* Copyright (c) 2013-2025, 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
/* --------------------------------------------------------------------
* 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
adrp x0, __PER_CPU_START__
add x0, x0, :lo12:__PER_CPU_START__
adrp x1, __PER_CPU_END__
add x1, x1, :lo12:__PER_CPU_END__
sub x1, x1, x0
bl clean_dcache_range
#if (PLATFORM_NODE_COUNT > 1)
/*
* dcache clean per-cpu sections defined by the platform.
* Care must be taken to preserve and retain the clobbered
* registers. A standard around the container for per-cpu nodes
* is not yet defined.
*/
bl plat_per_cpu_dcache_clean
#endif /* (PLATFORM_NODE_COUNT > 1) */
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.
*
* IMPORTANT: after invoking bl31_plat_enable_mmu(), the stack may end up
* corrupted. Thus, when using this function, we must operate under the
* assumption that we've no stack to use. Therefore, DO NOT place this
* in another C function call or, generally, any place that would break
* the aforementioned assumption.
*/
#if HW_ASSISTED_COHERENCY || WARMBOOT_ENABLE_DCACHE_EARLY
mov x0, xzr
#else
mov x0, #DISABLE_DCACHE
#endif
bl bl31_plat_enable_mmu
bl bl31_warmboot
#if ENABLE_RUNTIME_INSTRUMENTATION
pmf_calc_timestamp_addr rt_instr_svc, RT_INSTR_EXIT_PSCI
mov x19, x0
/*
* Invalidate before updating timestamp to ensure previous timestamp
* updates on the same cache line with caches disabled are properly
* seen by the same core. Without the cache invalidate, the core might
* write into a stale cache line.
*/
mov x1, #PMF_TS_SIZE
mov x20, x30
bl inv_dcache_range
mov x30, x20
mrs x0, cntpct_el0
str x0, [x19]
#endif
b el3_exit
endfunc bl31_warm_entrypoint

View File

@ -0,0 +1,428 @@
/*
* Copyright (c) 2014-2026, 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/utils_def.h>
.globl report_unhandled_exception
.globl report_unhandled_interrupt
.globl report_el3_panic
#if CRASH_REPORTING
/* need enough space in crash buffer to save 8 registers */
#define CRASH_BUF_SIZE (8 * CPU_WORD_SIZE)
#define CRASH_BUF_SPACE (CRASH_BUF_SIZE * PLATFORM_CORE_COUNT)
.section .data.crash_buf
crash_buf:
.align 3 /* log2 of CPU_WORD_SIZE */
.space CRASH_BUF_SPACE
/* ------------------------------------------------------
* 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 "mpidr_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"
/*
* 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, x20, sp
*/
func size_controlled_print_helper
#if ENABLE_FEAT_D128
size_controlled_print_128:
/* Set flag to print 128-bit registers */
mov x20, #1
b 1f
size_controlled_print:
/* Set flag to print 64-bit registers */
mov x20, #0
1:
#else
size_controlled_print:
#endif
/* 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, #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
/* Print the high 64 bits (or whole 64-bit register) */
ldr x4, [x7], #REGSZ
bl asm_print_hex
#if ENABLE_FEAT_D128
cbz x20, 2f
/* Print the low 64 bits in case of a 128-bit register */
ldr x4, [x7], #REGSZ
bl asm_print_hex
2:
#endif
bl asm_print_newline
b test_size_list
exit_size_print:
mov x30, sp
ret
endfunc size_controlled_print_helper
/* -----------------------------------------------------
* 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, x20, 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
/*
* An equivalent helper function for storing x8 - x15
* registers in a different order inside the crash buf.
* In the end the function size_controlled_print_128 is
* called to print the registers to the console.
* Clobbers : x0 - x7, x20, sp
*/
func str_in_crash_buf_print_128
/* 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_128
endfunc str_in_crash_buf_print_128
/* ---------------------------------------------------------------------
* This macro calculates the offset to crash buf and stores it in
* tpidr_el3. It also saves x0 to x6 and x30 in the crash buf by using
* system registers as temporary registers. These are selected to be
* present in a minimal ARMv8.0 implementation and not overwrite context
* we want to report.
* ---------------------------------------------------------------------
*/
.macro prepare_crash_buf_and_save_regs
msr tpidr_el3, x0
/* PMUv3 is presumed to be always present. Disable counting so
* pmccntr_el0 can be used */
mrs x0, pmcr_el0
bic x0, x0, #PMCR_EL0_E_BIT
msr pmcr_el0, x0
msr far_el1, x1
msr elr_el1, x2
msr tpidr_el1, x3
msr mair_el1, x4
msr sp_el1, x5
msr vbar_el1, x6
msr tpidr_el0, x7
msr tpidrro_el0, x8
msr pmccntr_el0, x30
/* calculate crash_buf[core_pos] */
bl plat_my_core_pos
mov_imm x1, CRASH_BUF_SIZE
mul x0, x0, x1
adr_l x1, crash_buf
add x0, x0, x1
/* put x0 in the crash buffer */
mrs x1, tpidr_el3
str x1, [x0]
/* Store crash buffer address in tpidr_el3 */
msr tpidr_el3, x0
/* put x1 - x7 in the crash buffer */
mrs x1, far_el1
mrs x2, elr_el1
mrs x3, tpidr_el1
mrs x4, mair_el1
mrs x5, sp_el1
mrs x6, vbar_el1
mrs x30, pmccntr_el0
str x1, [x0, #REGSZ]
stp x2, x3, [x0, #REGSZ * 2]
stp x4, x5, [x0, #REGSZ * 4]
stp x6, x30, [x0, #REGSZ * 6]
/* put these back as there's no space in the buffer */
mrs x7, tpidr_el0
mrs x8, tpidrro_el0
.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
/* Switch to SP_ELx */
msr spsel, #MODE_SP_ELX
prepare_crash_buf_and_save_regs
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_and_save_regs
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 (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_and_save_regs
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. A group may be
* interrupted in case a potential group of 128-bit
* sys regs needs to be printed.
* ------------------------------------------------------------
*/
do_crash_reporting:
/* 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
#if ENABLE_PAUTH == 2
/* Skip if not present in hardware */
is_feat_pauth_present_asm x0, x1
beq 1f
#endif
/*
* The assembler must see support for xpaci. So turn the compiler
* extension on. GCC prior to 10 doesn't understand the PAuth extension
* but it does understand armv8.3-a in general. Avoid using 8.3 if
* the compiler understands "pauth" so we don't downgrade a higher
* -march that was specified on the commandline.
*/
#if __GNUC__ < 10
.arch armv8.3-a
#else
.arch_extension pauth
#endif
/* Demangle address */
xpaci x4
1:
#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, mpidr_el1
mrs x9, sp_el0
mrs x10, 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

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018-2025, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2022, NVIDIA Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
#include <cpu_macros.S>
#include <context.h>
.globl handle_pending_async_ea
/*
* Handler for async EA from lower EL synchronized at EL3 entry in FFH mode.
*
* This scenario may arise when there is an error (EA) in the system which is not
* yet signaled to PE while executing in lower EL. During entry into EL3, the errors
* are synchronized either implicitly or explicitly causing async EA to pend at EL3.
*
* On detecting the pending EA (via ISR_EL1.A), if the EA routing model is Firmware
* First handling (FFH, SCR_EL3.EA = 1) this handler first handles the pending EA
* and then handles the original exception.
*
* This function assumes x30 has been saved.
*/
func handle_pending_async_ea
/*
* Prepare for nested handling of EA. Stash sysregs clobbered by nested
* exception and handler
*/
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_GPREG_LR]
mrs x30, esr_el3
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ESR_EL3]
mrs x30, spsr_el3
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_SPSR_EL3]
mrs x30, elr_el3
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
mov x30, #1
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
/*
* Restore the original x30 saved as part of entering EL3. This is not
* required for the current function but for EL3 SError vector entry
* once PSTATE.A bit is unmasked. We restore x30 and then the same
* value is stored in EL3 SError vector entry.
*/
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
/*
* After clearing PSTATE.A bit pending SError will trigger at current EL.
* Put explicit synchronization event to ensure newly unmasked interrupt
* is taken immediately.
*/
unmask_async_ea
/* Restore the original exception information along with zeroing the storage */
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
msr elr_el3, x30
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_SPSR_EL3]
msr spsr_el3, x30
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_SPSR_EL3]
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ESR_EL3]
msr esr_el3, x30
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ESR_EL3]
/*
* If the original exception corresponds to SError from lower El, eret back
* to lower EL, otherwise return to vector table for original exception handling.
*/
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x30, #EC_SERROR
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_GPREG_LR]
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_GPREG_LR]
b.eq 1f
ret
1:
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
str xzr, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
exception_return
endfunc handle_pending_async_ea

View File

@ -0,0 +1,468 @@
/*
* Copyright (c) 2013-2026, 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 <common/runtime_svc.h>
#include <context.h>
#include <cpu_macros.S>
#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 restore_x30
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
.endm
/*
* Macro that synchronizes errors (EA) and checks for pending SError.
* On detecting a pending SError it either reflects it back to lower
* EL (KFH) or handles it in EL3 (FFH) based on EA routing model.
*/
.macro sync_and_handle_pending_serror
synchronize_errors
mrs x30, ISR_EL1
tbz x30, #ISR_A_SHIFT, 2f
#if FFH_SUPPORT
mrs x30, scr_el3
tst x30, #SCR_EA_BIT
b.eq 1f
bl handle_pending_async_ea
b 2f
#endif
1:
/* This function never returns, but need LR for decision making */
bl reflect_pending_async_ea_to_lower_el
2:
.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.
*/
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
per_cpu_cur percpu_data, x29, x30
mrs x30, cntpct_el0
str x30, [x29, #CPU_DATA_CPU_DATA_PMF_TS]
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
mrs x30, esr_el3
/* fast paths that have a minimal environment */
cmp x30, #EC_IMP_DEF_EL3
b.eq imp_def_el3_handler
/* setup the full environment */
bl prepare_el3_entry
bl handler_sync_exception
no_ret el3_exit
.endm
.macro handle_lower_el_async_ea
bl prepare_el3_entry
bl handler_lower_el_async_ea
no_ret el3_exit
.endm
/* ---------------------------------------------------------------------
* This function handles FIQ or IRQ interrupts i.e. EL3, S-EL1 and NS
* interrupts.
* ---------------------------------------------------------------------
*/
.macro handle_interrupt_exception
bl prepare_el3_entry
bl handler_interrupt_exception
/* Return from exception, possibly in a different security state */
no_ret el3_exit
.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 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 implicit/explicit synchronization in EL3 (SCR_EL3.EA=1)
* during EL3 entry. For the former case we continue with "plat_handle_el3_ea".
* The later case will occur when PSTATE.A bit is cleared in
* "handle_pending_async_ea". This means we are doing a nested
* exception in EL3. Call the handler for async EA which will eret back to
* original el3 handler if it is nested exception. Also, unmask EA so that we
* catch any further EA arise when handling this nested exception at EL3.
*/
save_x30
ldr x30, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
cbz x30, 1f
/*
* This is nested exception handling, clear the flag to avoid taking this
* path for further exceptions caused by EA handling
*/
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
unmask_async_ea
handle_lower_el_async_ea
1:
restore_x30
#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
sync_and_handle_pending_serror
handle_sync_exception
end_vector_entry sync_exception_aarch64
vector_entry irq_aarch64
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
handle_interrupt_exception
end_vector_entry irq_aarch64
vector_entry fiq_aarch64
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
handle_interrupt_exception
end_vector_entry fiq_aarch64
/*
* Need to synchronize any outstanding SError since we can get a burst of errors.
* So reuse the sync mechanism to catch any further errors which are pending.
*/
vector_entry serror_aarch64
#if FFH_SUPPORT
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
handle_lower_el_async_ea
#else
b report_unhandled_exception
#endif
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
sync_and_handle_pending_serror
handle_sync_exception
end_vector_entry sync_exception_aarch32
vector_entry irq_aarch32
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
handle_interrupt_exception
end_vector_entry irq_aarch32
vector_entry fiq_aarch32
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
handle_interrupt_exception
end_vector_entry fiq_aarch32
/*
* Need to synchronize any outstanding SError since we can get a burst of errors.
* So reuse the sync mechanism to catch any further errors which are pending.
*/
vector_entry serror_aarch32
#if FFH_SUPPORT
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
handle_lower_el_async_ea
#else
b report_unhandled_exception
#endif
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 */
func imp_def_el3_handler
/* 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, el3_handler_exit
mrs x1, esr_el3
ubfx x1, x1, #ESR_EC_SHIFT, #ESR_EC_LENGTH
blr x0
el3_handler_exit:
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]
restore_x30
no_ret report_unhandled_exception
endfunc imp_def_el3_handler
/*
* Handler for async EA from lower EL synchronized at EL3 entry in KFH mode.
*
* This scenario may arise when there is an error (EA) in the system which is not
* yet signaled to PE while executing in lower EL. During entry into EL3, the errors
* are synchronized either implicitly or explicitly causing async EA to pend at EL3.
*
* On detecting the pending EA (via ISR_EL1.A) and if the EA routing model is
* KFH (SCR_EL3.EA = 1) this handler reflects ther error back to lower EL.
*
* This function assumes x30 has been saved.
*/
func reflect_pending_async_ea_to_lower_el
/*
* As the original exception was not handled we need to ensure that we return
* back to the instruction which caused the exception. To acheive that, eret
* to "elr-4" (Label "subtract_elr_el3") for SMC or simply eret otherwise
* (Label "skip_smc_check").
*
* LIMITATION: It could be that async EA is masked at the target exception level
* or the priority of async EA wrt to the EL3/secure interrupt is lower, which
* causes back and forth between lower EL and EL3. In case of back and forth between
* lower EL and EL3, we can track the loop count in "CTX_NESTED_EA_FLAG" and leverage
* previous ELR in "CTX_SAVED_ELR_EL3" to detect this cycle and further panic
* to indicate a problem here (Label "check_loop_ctr"). If we are in this cycle, loop
* counter retains its value but if we do a normal el3_exit this flag gets cleared.
* However, setting SCR_EL3.IESB = 1, should give priority to SError handling
* as per AArch64.TakeException pseudo code in Arm ARM.
*
* TODO: In future if EL3 gets a capability to inject a virtual SError to lower
* ELs, we can remove the el3_panic and handle the original exception first and
* inject SError to lower EL before ereting back.
*/
stp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
ldr x29, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
mrs x28, elr_el3
cmp x29, x28
b.eq check_loop_ctr
str x28, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
/* Zero the loop counter */
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
b skip_loop_ctr
check_loop_ctr:
ldr x29, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
add x29, x29, #1
str x29, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
cmp x29, #ASYNC_EA_REPLAY_COUNTER
b.ge el3_panic
skip_loop_ctr:
/*
* Logic to distinguish if we came from SMC or any other exception.
* Use offsets in vector entry to get which exception we are handling.
* In each vector entry of size 0x200, address "0x0-0x80" is for sync
* exception and "0x80-0x200" is for async exceptions.
* Use vector base address (vbar_el3) and exception offset (LR) to
* calculate whether the address we came from is any of the following
* "0x0-0x80", "0x200-0x280", "0x400-0x480" or "0x600-0x680"
*/
mrs x29, vbar_el3
sub x30, x30, x29
and x30, x30, #0x1ff
cmp x30, #0x80
b.ge skip_smc_check
/* Its a synchronous exception, Now check if it is SMC or not? */
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x30, #EC_AARCH32_SMC
b.eq subtract_elr_el3
cmp x30, #EC_AARCH64_SMC
b.eq subtract_elr_el3
b skip_smc_check
subtract_elr_el3:
sub x28, x28, #4
skip_smc_check:
msr elr_el3, x28
ldp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
exception_return
endfunc reflect_pending_async_ea_to_lower_el
/* ---------------------------------------------------------------------
* 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_WIDTH
/* 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 */

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

@ -0,0 +1,258 @@
/*
* Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/per_cpu/per_cpu_defs.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 */
#if SEPARATE_RWDATA_REGION
RAM_RW (rw): ORIGIN = BL31_RWDATA_BASE, LENGTH = BL31_RWDATA_LIMIT - BL31_RWDATA_BASE
#else /* SEPARATE_RWDATA_REGION */
#define RAM_RW RAM
#endif /* SEPARATE_RWDATA_REGION */
}
#if 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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text is not aligned on a page boundary.");
__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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro is not aligned on a page boundary.");
__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) */
#if SEPARATE_RWDATA_REGION
. = BL31_RWDATA_BASE;
ASSERT(BL31_RWDATA_BASE == ALIGN(PAGE_SIZE),
"BL31_RWDATA_BASE address is not aligned on a page boundary.")
/*
* Define a linker symbol to mark the start of the RW memory area for this
* image.
*/
__RW_START__ = . ;
DATA_SECTION >RAM_RW AT>RAM
__DATA_RAM_START__ = __DATA_START__;
__DATA_RAM_END__ = __DATA_END__;
__DATA_ROM_START__ = LOADADDR(.data);
. = ALIGN(PAGE_SIZE);
__RW_END__ = .;
RELA_SECTION >RAM
#else /* SEPARATE_RWDATA_REGION */
/*
* Define a linker symbol to mark the start of the RW memory area for this
* image.
*/
__RW_START__ = . ;
DATA_SECTION >RAM
RELA_SECTION >RAM
#endif /* SEPARATE_RWDATA_REGION */
#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);
#if !SEPARATE_RWDATA_REGION
__RW_END__ = .;
#endif /* SEPARATE_RWDATA_REGION */
__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
PER_CPU >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 */
/*
* Define a linker symbol to mark the end of the RW memory area for this
* image.
*/
#if !SEPARATE_RWDATA_REGION
__RW_END__ = .;
#endif /* SEPARATE_RWDATA_REGION */
__BL31_END__ = .;
ASSERT(. <= BL31_LIMIT, "BL31 image has exceeded its limit.")
#endif /* SEPARATE_NOBITS_REGION */
RAM_REGION_END = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
}

263
bl31/bl31.mk Normal file
View File

@ -0,0 +1,263 @@
#
# Copyright (c) 2013-2026, 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
ifeq (${SPMC_AT_EL3},1)
$(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/runtime_exceptions.S \
bl31/bl31_context_mgmt.c \
bl31/bl31_traps.c \
common/runtime_svc.c \
common/smc_validation.c \
lib/cpus/errata_common.c \
lib/per_cpu/aarch64/per_cpu_asm.S \
lib/per_cpu/per_cpu.c \
plat/common/aarch64/platform_mp_stack.S \
services/arm_arch_svc/arm_arch_svc_setup.c \
services/std_svc/std_svc_setup.c \
lib/el3_runtime/simd_ctx.c \
${PSCI_LIB_SOURCES} \
${SPMD_SOURCES} \
${SPM_MM_SOURCES} \
${SPMC_SOURCES} \
${SPM_SOURCES}
VENDOR_EL3_SRCS += services/el3/ven_el3_svc.c
CPU_SVC_SRCS += services/cpu/cpu_svc.c
ifeq (${ENABLE_PMF}, 1)
BL31_SOURCES += lib/pmf/pmf_main.c \
${VENDOR_EL3_SRCS}
endif
include lib/debugfs/debugfs.mk
ifeq (${USE_DEBUGFS},1)
BL31_SOURCES += ${DEBUGFS_SRCS} \
${VENDOR_EL3_SRCS}
endif
ifeq (${PLATFORM_REPORT_CTX_MEM_USE},1)
BL31_SOURCES += lib/el3_runtime/aarch64/context_debug.c
endif
ifeq (${EL3_EXCEPTION_HANDLING},1)
BL31_SOURCES += bl31/ehf.c
endif
ifeq (${FFH_SUPPORT},1)
BL31_SOURCES += bl31/aarch64/ea_delegate.S
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
ifneq (${ENABLE_FEAT_FGT2},0)
BL31_SOURCES += lib/extensions/fgt/fgt2.c
endif
ifneq (${ENABLE_FEAT_IDTE3},0)
BL31_SOURCES += lib/extensions/idte/idte3.c
endif
ifneq (${ENABLE_FEAT_TCR2},0)
BL31_SOURCES += lib/extensions/tcr/tcr2.c
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_FEAT_MPAM},0)
BL31_SOURCES += lib/extensions/mpam/mpam.c
endif
ifneq (${ENABLE_FEAT_DEBUGV8P9},0)
BL31_SOURCES += lib/extensions/debug/debugv8p9.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
ifneq (${ENABLE_FEAT_CPA2}, 0)
BL31_SOURCES += lib/extensions/cpa2/cpa2.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 (${WORKAROUND_CVE_2025_0647},1)
BL31_SOURCES += lib/cpus/aarch64/wa_cve_2025_0647_cpprctx.S
endif
ifeq ($(SMC_PCI_SUPPORT),1)
BL31_SOURCES += services/std_svc/pci_svc.c
endif
ifneq (${ENABLE_FEAT_RME},0)
include lib/gpt_rme/gpt_rme.mk
BL31_SOURCES += ${GPT_LIB_SRCS}
endif
ifeq (${ENABLE_RMM},1)
BL31_SOURCES += ${RMMD_SOURCES}
endif
ifeq (${USE_DSU_DRIVER},1)
BL31_SOURCES += drivers/arm/dsu/dsu.c
endif
# RAS sources
ifneq (${ENABLE_FEAT_RAS},0)
ifeq (${HANDLE_EA_EL3_FIRST_NS},1)
BL31_SOURCES += lib/extensions/ras/std_err_record.c \
lib/extensions/ras/ras_common.c
endif
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
ifeq (${LFA_SUPPORT},1)
include services/std_svc/lfa/lfa.mk
BL31_SOURCES += ${LFA_SOURCES}
endif
ifeq ($(CROS_WIDEVINE_SMC),1)
BL31_SOURCES += services/oem/chromeos/widevine_smc_handlers.c
endif
ifeq (${FIRME_SUPPORT},1)
BL31_SOURCES += services/std_svc/firme/firme_main.c \
services/std_svc/firme/firme_base_service.c \
services/std_svc/firme/firme_granule_management_service.c
endif
BL31_DEFAULT_LINKER_SCRIPT_SOURCE := bl31/bl31.ld.S
# CRYPTO_SUPPORT
NEED_AUTH := 0
NEED_HASH := $(if $(filter 1,$(MEASURED_BOOT) $(DRTM_SUPPORT)),1,)
$(eval $(call set_crypto_support,NEED_AUTH,NEED_HASH))
# 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
# BL31_CPPFLAGS
$(eval BL31_CPPFLAGS += $(call make_defines, \
$(sort \
CRYPTO_SUPPORT \
)))
$(eval $(call assert_booleans,\
$(sort \
CRASH_REPORTING \
EL3_EXCEPTION_HANDLING \
FIRME_SUPPORT \
SDEI_SUPPORT \
USE_DSU_DRIVER \
)))
# Numeric_Flags
$(eval $(call assert_numerics,\
$(sort \
CRYPTO_SUPPORT \
)))
$(eval $(call add_defines,\
$(sort \
CRASH_REPORTING \
EL3_EXCEPTION_HANDLING \
SDEI_SUPPORT \
USE_DSU_DRIVER \
FIRME_SUPPORT \
)))

66
bl31/bl31_context_mgmt.c Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <bl31/bl31.h>
#include <common/bl_common.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/el3_runtime/cpu_data.h>
/*******************************************************************************
* This function returns a pointer to the most recent 'cpu_context' structure
* for the calling CPU that was set as the context for the specified security
* state. NULL is returned if no such structure has been specified.
******************************************************************************/
void *cm_get_context(size_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,
size_t 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);
}

357
bl31/bl31_main.c Normal file
View File

@ -0,0 +1,357 @@
/*
* Copyright (c) 2013-2026, 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/build_message.h>
#include <common/debug.h>
#include <common/feat_detect.h>
#include <common/runtime_svc.h>
#include <drivers/arm/dsu.h>
#include <drivers/arm/gic.h>
#include <drivers/console.h>
#include <lib/bootmarker_capture.h>
#include <lib/el3_runtime/context_debug.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/extensions/pauth.h>
#include <lib/gpt_rme/gpt_rme.h>
#include <lib/pmf/pmf.h>
#include <lib/runtime_instr.h>
#include <lib/xlat_tables/xlat_mmu_helpers.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_RMM
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 = (uint32_t)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.
******************************************************************************/
static void __init bl31_lib_init(void)
{
cm_init();
}
/*******************************************************************************
* 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 __no_pauth bl31_main(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
unsigned int core_pos = plat_my_core_pos();
/* Enable early console if EARLY_CONSOLE flag is enabled */
plat_setup_early_console();
/* Perform early platform-specific setup */
bl31_early_platform_setup2(arg0, arg1, arg2, arg3);
/* Perform late platform-specific setup */
bl31_plat_arch_setup();
#if FEATURE_DETECTION
/* Detect if features enabled during compilation are supported by PE. */
detect_arch_features(core_pos);
#endif /* FEATURE_DETECTION */
/* Prints context_memory allocated for all the security states */
report_ctx_memory_usage();
/* Init registers that never change for the lifetime of the core. */
cm_manage_extensions_el3(core_pos);
/* Init per-world context registers */
cm_manage_extensions_per_world();
NOTICE("BL31: %s\n", build_version_string);
NOTICE("BL31: %s\n", build_message);
#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
#if USE_GIC_DRIVER
/*
* Initialize the GIC driver and this core's GIC interface before fully
* setting up the platform. This allows early platform setup to
* configure interrupts.
*/
gic_init(core_pos);
gic_pcpu_init(core_pos);
gic_cpuif_enable(core_pos);
#endif /* USE_GIC_DRIVER */
/* Perform platform setup in BL31 */
bl31_platform_setup();
#if USE_DSU_DRIVER
dsu_driver_init(&plat_dsu_data);
#endif
/* 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_RMM
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();
/*
* Perform any platform specific runtime setup prior to cold boot exit
* from BL31
*/
bl31_plat_runtime_setup();
#if ENABLE_RUNTIME_INSTRUMENTATION
console_flush();
PMF_CAPTURE_TIMESTAMP(bl_svc, BL31_EXIT, PMF_CACHE_MAINT);
#endif
console_flush();
console_switch_state(CONSOLE_FLAG_RUNTIME);
}
void __no_pauth bl31_warmboot(void)
{
unsigned int core_pos = plat_my_core_pos();
#if FEATURE_DETECTION
/* Detect if features enabled during compilation are supported by PE. */
detect_arch_features(core_pos);
#endif /* FEATURE_DETECTION */
/* Init registers that never change for the lifetime of the core. */
cm_manage_extensions_el3(core_pos);
/*
* 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.
*/
#if ENABLE_FEAT_RME
if (is_feat_rme_supported()) {
if (gpt_enable() != 0) {
panic();
}
}
#endif
/* Enable DSU driver for each booting core */
#if USE_DSU_DRIVER
dsu_driver_init(&plat_dsu_data);
#endif
psci_warmboot_entrypoint(core_pos);
}
/*******************************************************************************
* 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;
}
static 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)
{
const 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_RMM
/*******************************************************************************
* 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

336
bl31/bl31_traps.c Normal file
View File

@ -0,0 +1,336 @@
/*
* Copyright (c) 2022-2026, 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 <arch_features.h>
#include <arch_helpers.h>
#include <bl31/sync_handle.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/extensions/idte3.h>
static int access_raz_wi(bool is_read, uint8_t rt, cpu_context_t *ctx)
{
if (is_read && rt != XZR_REG_NUM) {
ctx->gpregs_ctx.ctx_regs[rt] = 0UL;
}
return TRAP_RET_CONTINUE;
}
int handle_sysreg_trap(uint64_t esr_el3, cpu_context_t *ctx, u_register_t flags)
{
uint64_t opcode = EXTRACT(ESR_ISS, esr_el3) & ~(MASK(ISS_SYS64_DIR) | MASK(ISS_SYS64_RT));
uint8_t rt = EXTRACT(ISS_SYS64_RT, esr_el3);
bool is_read = EXTRACT(ISS_SYS64_DIR, opcode);
if (is_feat_idte3_supported() &&
((opcode >= ISS_SYSREG_OPCODE_IDREG_MIN &&
opcode <= ISS_SYSREG_OPCODE_IDREG_MAX) ||
opcode == ISS_SYSREG_OPCODE_GMID_EL1)) {
return handle_idreg_trap(rt, opcode, ctx, flags);
}
if (is_feat_rng_trap_supported() &&
(opcode == ISS_SYSREG_OPCODE_RNDR ||
opcode == ISS_SYSREG_OPCODE_RNDRRS)) {
el3_state_t *state = get_el3state_ctx(ctx);
u_register_t *data;
u_register_t new_spsr;
int ret = TRAP_RET_CONTINUE;
/* Only expect reads, write shouldn't be possible. */
assert(EXTRACT(ISS_SYS64_DIR, esr_el3) != 0);
/* Successful reads should have NZCV == 0 */
new_spsr = read_ctx_reg(state, CTX_SPSR_EL3);
new_spsr &= ~SPSR_NZCV;
/*
* Don't generate entropy for XZR accesses as it will be unused.
* Report success despite the zero read. Reporting failure would
* also be logical but C6.1.4.1 doesn't make this clear and
* success is simpler for now.
*/
if (rt != ISS_SYSREG_RT_XZR) {
data = &(ctx->gpregs_ctx.ctx_regs[rt]);
ret = plat_handle_rng_trap(data, opcode == ISS_SYSREG_OPCODE_RNDRRS);
if (ret == TRAP_RET_UNHANDLED) {
/* Failure is signaled with 0 and NZCV = 0b0100. */
new_spsr |= SPSR_Z_BIT;
*data = 0;
ret = TRAP_RET_CONTINUE;
}
}
write_ctx_reg(state, CTX_SPSR_EL3, new_spsr);
assert(ret == TRAP_RET_CONTINUE);
return ret;
}
if (is_feat_ras_supported() && !FAULT_INJECTION_SUPPORT &&
(opcode == ISS_SYSREG_OPCODE_ERXPFGCDN_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXPFGCTL_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXPFGF_EL1)) {
return access_raz_wi(is_read, rt, ctx);
}
if (is_feat_ras_supported() && RAS_TRAP_NS_ERR_REC_ACCESS &&
(opcode == ISS_SYSREG_OPCODE_ERRSELR_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXADDR_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXCTLR_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXMISC0_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXMISC1_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXSTATUS_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERRIDR_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXFR_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXMISC2_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXMISC3_EL1 ||
opcode == ISS_SYSREG_OPCODE_ERXGSR_EL1)) {
return access_raz_wi(is_read, rt, ctx);
}
#if IMPDEF_SYSREG_TRAP
/* isolate selected bits and check they are all set */
if (opcode & ISS_SYSREG_OPCODE_IMPDEF_MASK == ISS_SYSREG_OPCODE_IMPDEF_MASK) {
return plat_handle_impdef_trap(esr_el3, ctx);
}
#endif
return TRAP_RET_UNHANDLED;
}
static bool is_tge_enabled(void)
{
u_register_t hcr_el2 = read_hcr_el2();
return ((is_feat_vhe_present()) && ((hcr_el2 & HCR_TGE_BIT) != 0U));
}
/*
* This function is to ensure that undef injection does not happen into
* non-existent S-EL2. This could happen when trap happens from S-EL{1,0}
* and non-secure world is running with TGE bit set, considering EL3 does
* not save/restore EL2 registers if only one world has EL2 enabled.
* So reading hcr_el2.TGE would give NS world value.
*/
static bool is_secure_trap_without_sel2(u_register_t scr)
{
return ((scr & (SCR_NS_BIT | SCR_EEL2_BIT)) == 0);
}
static unsigned int target_el(unsigned int from_el, u_register_t scr)
{
if (from_el > MODE_EL1) {
return from_el;
} else if (is_tge_enabled() && !is_secure_trap_without_sel2(scr)) {
return MODE_EL2;
} else {
return MODE_EL1;
}
}
static u_register_t get_elr_el3(u_register_t spsr_el3, u_register_t vbar, unsigned int target_el)
{
unsigned int outgoing_el = GET_EL(spsr_el3);
u_register_t elr_el3 = 0;
if (outgoing_el == target_el) {
/*
* Target EL is either EL1 or EL2, lsb can tell us the SPsel
* Thread mode : 0
* Handler mode : 1
*/
if ((spsr_el3 & (MODE_SP_MASK << MODE_SP_SHIFT)) == MODE_SP_ELX) {
elr_el3 = vbar + CURRENT_EL_SPX;
} else {
elr_el3 = vbar + CURRENT_EL_SP0;
}
} else {
/* Vector address for Lower EL using Aarch64 */
elr_el3 = vbar + LOWER_EL_AARCH64;
}
return elr_el3;
}
/*
* Explicitly create all bits of SPSR to get PSTATE at exception return.
*
* The code is based on "Aarch64.exceptions.takeexception" described in
* DDI0602 revision 2026-03.
* "https://developer.arm.com/documentation/ddi0597/2026-03/Shared-Pseudocode/
* aarch64-exceptions-takeexception"
*
* NOTE: This piece of code must be reviewed every release against the latest
* takeexception sequence to ensure that we keep up with new arch features that
* affect the PSTATE.
*
* Next review: TF-A 2.16 release
*
* FEAT_NV3 has an impact but is not implemented in EL3 yet.
* TODO: this should create a BRBE exception record
*/
u_register_t create_spsr(u_register_t old_spsr, unsigned int target_el)
{
u_register_t new_spsr = 0;
u_register_t sctlr;
/* Set M bits for target EL in AArch64 mode, also get sctlr */
if (target_el == MODE_EL2) {
sctlr = read_sctlr_el2();
new_spsr |= (SPSR_M_AARCH64 << SPSR_M_SHIFT) | SPSR_M_EL2H;
} else {
sctlr = read_sctlr_el1();
new_spsr |= (SPSR_M_AARCH64 << SPSR_M_SHIFT) | SPSR_M_EL1H;
}
/* Mask all exceptions, update DAIF bits */
new_spsr |= SPSR_DAIF_MASK << SPSR_DAIF_SHIFT;
/* If FEAT_BTI is present, clear BTYPE bits */
new_spsr |= old_spsr & (SPSR_BTYPE_MASK_AARCH64 << SPSR_BTYPE_SHIFT_AARCH64);
if (is_feat_bti_present()) {
new_spsr &= ~(SPSR_BTYPE_MASK_AARCH64 << SPSR_BTYPE_SHIFT_AARCH64);
}
/* If SSBS is implemented, take the value from SCTLR.DSSBS */
new_spsr |= old_spsr & SPSR_SSBS_BIT_AARCH64;
if (is_feat_ssbs_present()) {
if ((sctlr & SCTLR_DSSBS_BIT) != 0U) {
new_spsr |= SPSR_SSBS_BIT_AARCH64;
} else {
new_spsr &= ~SPSR_SSBS_BIT_AARCH64;
}
}
/* If FEAT_NMI is implemented, ALLINT = !(SCTLR.SPINTMASK) */
new_spsr |= old_spsr & SPSR_ALLINT_BIT_AARCH64;
if (is_feat_nmi_present()) {
if ((sctlr & SCTLR_SPINTMASK_BIT) != 0U) {
new_spsr &= ~SPSR_ALLINT_BIT_AARCH64;
} else {
new_spsr |= SPSR_ALLINT_BIT_AARCH64;
}
}
/* Clear PSTATE.IL bit explicitly */
new_spsr &= ~SPSR_IL_BIT;
/* Clear PSTATE.SS bit explicitly */
new_spsr &= ~SPSR_SS_BIT;
/* Update PSTATE.PAN bit */
new_spsr |= old_spsr & SPSR_PAN_BIT;
if (is_feat_pan_present() &&
((target_el == MODE_EL1) || ((target_el == MODE_EL2) && is_tge_enabled())) &&
((sctlr & SCTLR_SPAN_BIT) == 0U)) {
new_spsr |= SPSR_PAN_BIT;
}
/* Clear UAO bit if FEAT_UAO is present */
new_spsr |= old_spsr & SPSR_UAO_BIT_AARCH64;
if (is_feat_uao_present()) {
new_spsr &= ~SPSR_UAO_BIT_AARCH64;
}
/* DIT bits are unchanged */
new_spsr |= old_spsr & SPSR_DIT_BIT;
/* If FEAT_MTE2 is implemented mask tag faults by setting TCO bit */
new_spsr |= old_spsr & SPSR_TCO_BIT_AARCH64;
if (is_feat_mte2_present()) {
new_spsr |= SPSR_TCO_BIT_AARCH64;
}
/* NZCV bits are unchanged */
new_spsr |= old_spsr & SPSR_NZCV;
/* UINJ bit is unchanged */
new_spsr |= old_spsr & SPSR_UINJ_BIT;
/* If FEAT_EBEP is present set PM bit */
new_spsr |= old_spsr & SPSR_PM_BIT_AARCH64;
if (is_feat_ebep_present()) {
new_spsr |= SPSR_PM_BIT_AARCH64;
}
/* If FEAT_SEBEP is present clear PPEND bit */
new_spsr |= old_spsr & SPSR_PPEND_BIT;
if (is_feat_sebep_present()) {
new_spsr &= ~SPSR_PPEND_BIT;
}
/* If FEAT_GCS is present, update EXLOCK bit */
new_spsr |= old_spsr & SPSR_EXLOCK_BIT_AARCH64;
if (is_feat_gcs_present()) {
u_register_t gcscr;
if (target_el == MODE_EL2) {
gcscr = read_gcscr_el2();
} else {
gcscr = read_gcscr_el1();
}
new_spsr |= (gcscr & GCSCR_EXLOCK_EN_BIT) ? SPSR_EXLOCK_BIT_AARCH64 : 0;
}
/* If FEAT_PAUTH_LR present then zero the PACM bit. */
new_spsr |= old_spsr & SPSR_PACM_BIT_AARCH64;
if (is_feat_pauth_lr_present()) {
new_spsr &= ~SPSR_PACM_BIT_AARCH64;
}
return new_spsr;
}
/*
* Handler for injecting Undefined exception to lower EL which is caused by
* lower EL accessing system registers of which (old)EL3 firmware is unaware.
*
* This is a safety net to avoid EL3 panics caused by system register access
* that triggers an exception syndrome EC=0x18.
*/
void inject_undef64(cpu_context_t *ctx)
{
el3_state_t *state = get_el3state_ctx(ctx);
u_register_t old_spsr = read_ctx_reg(state, CTX_SPSR_EL3);
u_register_t scr_el3 = 0U;
unsigned int to_el = 0U;
u_register_t esr = 0U;
u_register_t elr_el3 = 0U;
u_register_t new_spsr = 0U;
if (is_feat_uinj_supported()) {
new_spsr = old_spsr | SPSR_UINJ_BIT;
write_ctx_reg(state, CTX_SPSR_EL3, new_spsr);
return;
}
scr_el3 = read_ctx_reg(state, CTX_SCR_EL3);
to_el = target_el(GET_EL(old_spsr), scr_el3);
esr = (EC_UNKNOWN << ESR_EC_SHIFT) | ESR_IL_BIT;
elr_el3 = read_ctx_reg(state, CTX_ELR_EL3);
if (to_el == MODE_EL2) {
write_elr_el2(elr_el3);
elr_el3 = get_elr_el3(old_spsr, read_vbar_el2(), to_el);
write_esr_el2(esr);
write_spsr_el2(old_spsr);
} else {
write_elr_el1(elr_el3);
elr_el3 = get_elr_el3(old_spsr, read_vbar_el1(), to_el);
write_esr_el1(esr);
write_spsr_el1(old_spsr);
}
new_spsr = create_spsr(old_spsr, to_el);
write_ctx_reg(state, CTX_SPSR_EL3, new_spsr);
write_ctx_reg(state, CTX_ELR_EL3, elr_el3);
}

540
bl31/ehf.c Normal file
View File

@ -0,0 +1,540 @@
/*
* Copyright (c) 2017-2024, 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 GIC600_ERRATA_WA_2384374
if (cur_pri_idx == EHF_INVALID_IDX) {
old_mask = plat_ic_deactivate_priority(pe_data->init_pri_mask);
} else {
old_mask = plat_ic_deactivate_priority(priority);
}
#else
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);
}
#endif
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 only when SPM_MM present in secure. */
#if SPM_MM
set_interrupt_rm_flag(flags, SECURE);
#endif
/* 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);

235
bl31/interrupt_mgmt.c Normal file
View File

@ -0,0 +1,235 @@
/*
* 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 {
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(size_t security_state)
{
u_register_t scr_el3;
assert(sec_state_is_valid(security_state));
scr_el3 = intr_type_descs[INTR_TYPE_NS].scr_el3[security_state];
scr_el3 |= intr_type_descs[INTR_TYPE_S_EL1].scr_el3[security_state];
scr_el3 |= intr_type_descs[INTR_TYPE_EL3].scr_el3[security_state];
return scr_el3;
}
/*******************************************************************************
* This function uses the 'interrupt_type_flags' parameter to obtain the value
* of the trap bit (IRQ/FIQ) in the SCR_EL3 for a security state for this
* interrupt type. It uses it to update the SCR_EL3 in the cpu context and the
* 'intr_type_desc' for that security state.
******************************************************************************/
static void set_scr_el3_from_rm(uint32_t type,
uint32_t interrupt_type_flags,
uint32_t security_state)
{
uint32_t flag, bit_pos;
flag = get_interrupt_rm_flag(interrupt_type_flags, security_state);
bit_pos = plat_interrupt_type_to_line(type, security_state);
intr_type_descs[type].scr_el3[security_state] = (u_register_t)flag << bit_pos;
/*
* Update scr_el3 only if there is a context available. If not, it
* will be updated later during context initialization which will obtain
* the scr_el3 value to be used via get_scr_el3_from_routing_model()
*/
if (cm_get_context(security_state) != NULL) {
cm_write_scr_el3_bit(security_state, bit_pos, flag);
}
}
/*******************************************************************************
* This function validates the routing model specified in the 'flags' and
* updates internal data structures to reflect the new routing model. It also
* updates the copy of SCR_EL3 for each security state with the new routing
* model in the 'cpu_context' structure for this cpu.
******************************************************************************/
int32_t set_routing_model(uint32_t type, uint32_t flags)
{
int32_t rc;
rc = validate_interrupt_type(type);
if (rc != 0) {
return rc;
}
rc = validate_routing_model(type, flags);
if (rc != 0) {
return rc;
}
/* Update the routing model in internal data structures */
intr_type_descs[type].flags = flags;
set_scr_el3_from_rm(type, flags, SECURE);
set_scr_el3_from_rm(type, flags, NON_SECURE);
return 0;
}
/******************************************************************************
* This function disables the routing model of interrupt 'type' from the
* specified 'security_state' on the local core. The disable is in effect
* till the core powers down or till the next enable for that interrupt
* type.
*****************************************************************************/
int disable_intr_rm_local(uint32_t type, uint32_t security_state)
{
uint32_t bit_pos, flag;
assert(intr_type_descs[type].handler != NULL);
flag = get_interrupt_rm_flag(INTR_DEFAULT_RM, security_state);
bit_pos = plat_interrupt_type_to_line(type, security_state);
cm_write_scr_el3_bit(security_state, bit_pos, flag);
return 0;
}
/******************************************************************************
* This function enables the routing model of interrupt 'type' from the
* specified 'security_state' on the local core.
*****************************************************************************/
int enable_intr_rm_local(uint32_t type, uint32_t security_state)
{
uint32_t bit_pos, flag;
assert(intr_type_descs[type].handler != NULL);
flag = get_interrupt_rm_flag(intr_type_descs[type].flags,
security_state);
bit_pos = plat_interrupt_type_to_line(type, security_state);
cm_write_scr_el3_bit(security_state, bit_pos, flag);
return 0;
}
/*******************************************************************************
* This function registers a handler for the 'type' of interrupt specified. It
* also validates the routing model specified in the 'flags' for this type of
* interrupt.
******************************************************************************/
int32_t register_interrupt_type_handler(uint32_t type,
interrupt_type_handler_t handler,
uint32_t flags)
{
int32_t rc;
/* Validate the 'handler' parameter */
if (handler == NULL) {
return -EINVAL;
}
/* Validate the 'flags' parameter */
if ((flags & INTR_TYPE_FLAGS_MASK) != 0U) {
return -EINVAL;
}
/* Check if a handler has already been registered */
if (intr_type_descs[type].handler != NULL) {
return -EALREADY;
}
rc = set_routing_model(type, flags);
if (rc != 0) {
return rc;
}
/* Save the handler */
intr_type_descs[type].handler = handler;
return 0;
}
/*******************************************************************************
* This function is called when an interrupt is generated and returns the
* handler for the interrupt type (if registered). It returns NULL if the
* interrupt type is not supported or its handler has not been registered.
******************************************************************************/
interrupt_type_handler_t get_interrupt_type_handler(uint32_t type)
{
if (validate_interrupt_type(type) != 0) {
return NULL;
}
return intr_type_descs[type].handler;
}

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

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

View File

@ -0,0 +1,371 @@
/*
* Copyright (c) 2016-2025, 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_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
ldr r0, =__PER_CPU_START__
ldr r1, =__PER_CPU_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_CPU_DATA_PMF_TS]
/* 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

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

@ -0,0 +1,165 @@
/*
* Copyright (c) 2016-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/bl_common.ld.h>
#include <lib/per_cpu/per_cpu_defs.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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text address is not aligned on a page boundary.");
__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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro address is not aligned on a page boundary.");
__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
PER_CPU >RAM
XLAT_TABLE_SECTION >RAM
__BSS_SIZE__ = SIZEOF(.bss);
#if USE_COHERENT_MEM
/*
* The base address of the coherent memory section must be page-aligned to
* guarantee that the coherent data are stored on their own pages and are
* not mixed with normal data. This is required to set up the correct
* memory attributes for the coherent data page tables.
*/
.coherent_ram (NOLOAD) : ALIGN(PAGE_SIZE) {
__COHERENT_RAM_START__ = .;
/*
* Bakery locks are stored in coherent memory. Each lock's data is
* contiguous and fully allocated by the compiler.
*/
*(.bakery_lock)
*(.tzfw_coherent_mem)
__COHERENT_RAM_END_UNALIGNED__ = .;
/*
* Memory page(s) mapped to this section will be marked as device
* memory. No other unexpected data must creep in. Ensure that the rest
* of the current memory page is unused.
*/
. = ALIGN(PAGE_SIZE);
__COHERENT_RAM_END__ = .;
} >RAM
__COHERENT_RAM_UNALIGNED_SIZE__ =
__COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__;
#endif /* USE_COHERENT_MEM */
__RW_END__ = .;
__BL32_END__ = .;
/DISCARD/ : {
*(.dynsym .dynstr .hash .gnu.hash)
}
ASSERT(. <= BL32_LIMIT, "BL32 image has exceeded its limit.")
RAM_REGION_END = .;
}

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

@ -0,0 +1,84 @@
#
# Copyright (c) 2016-2025, 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 \
lib/per_cpu/per_cpu.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 += services/el3/ven_el3_svc.c \
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
# 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))
ifeq (${TRANSFER_LIST},1)
BL32_SOURCES += $(TRANSFER_LIST_SOURCES)
endif

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

@ -0,0 +1,267 @@
/*
* Copyright (c) 2016-2025, 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/build_message.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(size_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,
size_t 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->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);
}
/*******************************************************************************
* 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;
regs_t *gpregs = get_regs_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(gpregs,
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(gpregs, 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 setup function. Calls platforms init functions
*****************************************************************************/
void sp_min_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
/* Enable early console if EARLY_CONSOLE flag is enabled */
plat_setup_early_console();
/* Perform early platform-specific setup */
sp_min_early_platform_setup2(arg0, arg1, arg2, arg3);
sp_min_plat_arch_setup();
}
/******************************************************************************
* 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", build_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();
console_switch_state(CONSOLE_FLAG_RUNTIME);
}
/******************************************************************************
* 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;
regs_t *gpregs = get_regs_ctx(cm_get_context(NON_SECURE));
u_register_t ns_sctlr;
psci_warmboot_entrypoint(plat_my_core_pos());
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(gpregs,
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(gpregs, CTX_NS_SCTLR);
write_sctlr(ns_sctlr);
isb();
write_scr(read_scr() & ~SCR_NS_BIT);
isb();
}
#if SP_MIN_WITH_SECURE_FIQ
/******************************************************************************
* This function is invoked on secure interrupts. By construction of the
* SP_MIN, secure interrupts can only be handled when core executes in non
* secure state.
*****************************************************************************/
void sp_min_fiq(void)
{
uint32_t id;
id = plat_ic_acknowledge_interrupt();
sp_min_plat_fiq_handler(id);
plat_ic_end_of_interrupt(id);
}
#endif /* SP_MIN_WITH_SECURE_FIQ */

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SP_MIN_PRIVATE_H
#define SP_MIN_PRIVATE_H
#include <stdint.h>
void sp_min_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3);
void sp_min_main(void);
void sp_min_warm_boot(void);
void sp_min_fiq(void);
#endif /* SP_MIN_PRIVATE_H */

View File

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

View File

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

View File

@ -0,0 +1,512 @@
/*
* Copyright (c) 2013-2025, 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
/*---------------------------------------------
* Save arguments x0 - x3 from BL1 for future
* use.
* ---------------------------------------------
*/
mov x20, x0
mov x21, x1
mov x22, x2
mov x23, x3
#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
#if ENABLE_BTI
/* Enable PAC branch type compatibility */
bic x0, x0, #(SCTLR_BT0_BIT | SCTLR_BT1_BIT)
#endif
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
/*---------------------------------------------
* Save arguments x0 - x3 from prio stage for
* future use.
* ---------------------------------------------
*/
mov x0, x20
mov x1, x21
mov x2, x22
mov x3, x23
/* ---------------------------------------------
* Perform TSP setup
* ---------------------------------------------
*/
bl tsp_setup
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1
* and enable pointer authentication
* ---------------------------------------------
*/
bl pauth_init_enable_el1
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/
bl tsp_main
/* ---------------------------------------------
* Tell TSPD that we are done initialising
* ---------------------------------------------
*/
mov x1, x0
mov x0, #TSP_ENTRY_DONE
smc #0
tsp_entrypoint_panic:
b tsp_entrypoint_panic
endfunc tsp_entrypoint
/* -------------------------------------------
* Table of entrypoint vectors provided to the
* TSPD for the various entrypoints
* -------------------------------------------
*/
vector_base tsp_vector_table
b tsp_yield_smc_entry
b tsp_fast_smc_entry
b tsp_cpu_on_entry
b tsp_cpu_off_entry
b tsp_cpu_resume_entry
b tsp_cpu_suspend_entry
b tsp_sel1_intr_entry
b tsp_system_off_entry
b tsp_system_reset_entry
b tsp_abort_yield_smc_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is to be turned off through a CPU_OFF
* psci call to ask the TSP to perform any
* bookeeping necessary. In the current
* implementation, the TSPD expects the TSP to
* re-initialise its state so nothing is done
* here except for acknowledging the request.
* ---------------------------------------------
*/
func tsp_cpu_off_entry
bl tsp_cpu_off_main
restore_args_call_smc
endfunc tsp_cpu_off_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when the
* system is about to be switched off (through
* a SYSTEM_OFF psci call) to ask the TSP to
* perform any necessary bookkeeping.
* ---------------------------------------------
*/
func tsp_system_off_entry
bl tsp_system_off_main
restore_args_call_smc
endfunc tsp_system_off_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when the
* system is about to be reset (through a
* SYSTEM_RESET psci call) to ask the TSP to
* perform any necessary bookkeeping.
* ---------------------------------------------
*/
func tsp_system_reset_entry
bl tsp_system_reset_main
restore_args_call_smc
endfunc tsp_system_reset_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is turned on using a CPU_ON psci call to
* ask the TSP to initialise itself i.e. setup
* the mmu, stacks etc. Minimal architectural
* state will be initialised by the TSPD when
* this function is entered i.e. Caches and MMU
* will be turned off, the execution state
* will be aarch64 and exceptions masked.
* ---------------------------------------------
*/
func tsp_cpu_on_entry
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, tsp_exceptions
msr vbar_el1, x0
isb
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
msr sctlr_el1, x0
isb
/* --------------------------------------------
* Give ourselves a stack whose memory will be
* marked as Normal-IS-WBWA when the MMU is
* enabled.
* --------------------------------------------
*/
bl plat_set_my_stack
/* --------------------------------------------
* Enable MMU and D-caches together.
* --------------------------------------------
*/
mov x0, #0
bl bl32_plat_enable_mmu
#if ENABLE_PAUTH
/* ---------------------------------------------
* Program APIAKey_EL1
* and enable pointer authentication
* ---------------------------------------------
*/
bl pauth_init_enable_el1
#endif /* ENABLE_PAUTH */
/* ---------------------------------------------
* Enter C runtime to perform any remaining
* book keeping
* ---------------------------------------------
*/
bl tsp_cpu_on_main
restore_args_call_smc
/* Should never reach here */
tsp_cpu_on_entry_panic:
b tsp_cpu_on_entry_panic
endfunc tsp_cpu_on_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu is to be suspended through a CPU_SUSPEND
* psci call to ask the TSP to perform any
* bookeeping necessary. In the current
* implementation, the TSPD saves and restores
* the EL1 state.
* ---------------------------------------------
*/
func tsp_cpu_suspend_entry
bl tsp_cpu_suspend_main
restore_args_call_smc
endfunc tsp_cpu_suspend_entry
/*-------------------------------------------------
* This entrypoint is used by the TSPD to pass
* control for `synchronously` handling a S-EL1
* Interrupt which was triggered while executing
* in normal world. 'x0' contains a magic number
* which indicates this. TSPD expects control to
* be handed back at the end of interrupt
* processing. This is done through an SMC.
* The handover agreement is:
*
* 1. PSTATE.DAIF are set upon entry. 'x1' has
* the ELR_EL3 from the non-secure state.
* 2. TSP has to preserve the callee saved
* general purpose registers, SP_EL1/EL0 and
* LR.
* 3. TSP has to preserve the system and vfp
* registers (if applicable).
* 4. TSP can use 'x0-x18' to enable its C
* runtime.
* 5. TSP returns to TSPD using an SMC with
* 'x0' = TSP_HANDLED_S_EL1_INTR
* ------------------------------------------------
*/
func tsp_sel1_intr_entry
#if DEBUG
mov_imm x2, TSP_HANDLE_SEL1_INTR_AND_RETURN
cmp x0, x2
b.ne tsp_sel1_int_entry_panic
#endif
/*-------------------------------------------------
* Save any previous context needed to perform
* an exception return from S-EL1 e.g. context
* from a previous Non secure Interrupt.
* Update statistics and handle the S-EL1
* interrupt before returning to the TSPD.
* IRQ/FIQs are not enabled since that will
* complicate the implementation. Execution
* will be transferred back to the normal world
* in any case. The handler can return 0
* if the interrupt was handled or TSP_PREEMPTED
* if the expected interrupt was preempted
* by an interrupt that should be handled in EL3
* e.g. Group 0 interrupt in GICv3. In both
* the cases switch to EL3 using SMC with id
* TSP_HANDLED_S_EL1_INTR. Any other return value
* from the handler will result in panic.
* ------------------------------------------------
*/
save_eret_context x2 x3
bl tsp_update_sync_sel1_intr_stats
bl tsp_common_int_handler
/* Check if the S-EL1 interrupt has been handled */
cbnz x0, tsp_sel1_intr_check_preemption
b tsp_sel1_intr_return
tsp_sel1_intr_check_preemption:
/* Check if the S-EL1 interrupt has been preempted */
mov_imm x1, TSP_PREEMPTED
cmp x0, x1
b.ne tsp_sel1_int_entry_panic
tsp_sel1_intr_return:
mov_imm x0, TSP_HANDLED_S_EL1_INTR
restore_eret_context x2 x3
smc #0
/* Should never reach here */
tsp_sel1_int_entry_panic:
no_ret plat_panic_handler
endfunc tsp_sel1_intr_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD when this
* cpu resumes execution after an earlier
* CPU_SUSPEND psci call to ask the TSP to
* restore its saved context. In the current
* implementation, the TSPD saves and restores
* EL1 state so nothing is done here apart from
* acknowledging the request.
* ---------------------------------------------
*/
func tsp_cpu_resume_entry
bl tsp_cpu_resume_main
restore_args_call_smc
/* Should never reach here */
no_ret plat_panic_handler
endfunc tsp_cpu_resume_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD to ask
* the TSP to service a fast smc request.
* ---------------------------------------------
*/
func tsp_fast_smc_entry
bl tsp_smc_handler
restore_args_call_smc
/* Should never reach here */
no_ret plat_panic_handler
endfunc tsp_fast_smc_entry
/*---------------------------------------------
* This entrypoint is used by the TSPD to ask
* the TSP to service a Yielding SMC request.
* We will enable preemption during execution
* of tsp_smc_handler.
* ---------------------------------------------
*/
func tsp_yield_smc_entry
msr daifclr, #DAIF_FIQ_BIT | DAIF_IRQ_BIT
bl tsp_smc_handler
msr daifset, #DAIF_FIQ_BIT | DAIF_IRQ_BIT
restore_args_call_smc
/* Should never reach here */
no_ret plat_panic_handler
endfunc tsp_yield_smc_entry
/*---------------------------------------------------------------------
* This entrypoint is used by the TSPD to abort a pre-empted Yielding
* SMC. It could be on behalf of non-secure world or because a CPU
* suspend/CPU off request needs to abort the preempted SMC.
* --------------------------------------------------------------------
*/
func tsp_abort_yield_smc_entry
/*
* Exceptions masking is already done by the TSPD when entering this
* hook so there is no need to do it here.
*/
/* Reset the stack used by the pre-empted SMC */
bl plat_set_my_stack
/*
* Allow some cleanup such as releasing locks.
*/
bl tsp_abort_smc_handler
restore_args_call_smc
/* Should never reach here */
bl plat_panic_handler
endfunc tsp_abort_yield_smc_entry

View File

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

View File

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

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

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

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

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

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

@ -0,0 +1,135 @@
/*
* 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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".text address is not aligned on a page boundary.");
__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 . : {
ASSERT(. == ALIGN(PAGE_SIZE),
".ro address is not aligned on a page boundary.");
__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 = .;
}

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

@ -0,0 +1,60 @@
#
# Copyright (c) 2013-2025, 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 \
bl32/tsp/tsp_context.c \
common/aarch64/early_exceptions.S \
BL32_DEFAULT_LINKER_SCRIPT_SOURCE := bl32/tsp/tsp.ld.S
# CRYPTO_SUPPORT
NEED_AUTH := 0
NEED_HASH := $(if $(filter 1,$(MEASURED_BOOT)),1,)
$(eval $(call set_crypto_support,NEED_AUTH,NEED_HASH))
# BL32_CPPFLAGS
$(eval BL32_CPPFLAGS += $(call make_defines, \
$(sort \
CRYPTO_SUPPORT \
)))
# Numeric_Flags
$(eval $(call assert_numerics,\
$(sort \
CRYPTO_SUPPORT \
)))
# 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

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

@ -0,0 +1,152 @@
/*
* Copyright (c) 2022-2025, 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(u_register_t arg0, u_register_t arg1, u_register_t arg2,
u_register_t arg3)
{
/* Enable early console if EARLY_CONSOLE flag is enabled */
plat_setup_early_console();
/* Perform early platform-specific setup. */
tsp_early_platform_setup(arg0, arg1, arg2, arg3);
/* Perform late platform-specific setup. */
tsp_plat_arch_setup();
}
/*******************************************************************************
* 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: %u smcs, %u 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: %u smcs, %u 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);
}

143
bl32/tsp/tsp_context.c Normal file
View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_features.h>
#include <arch_helpers.h>
#include <bl32/tsp/tsp_el1_context.h>
#include <common/debug.h>
#define DUMMY_CTX_VALUE ULL(0xffffffff)
#define DUMMY_CTX_TCR_VALUE ULL(0xffff0000)
#define DUMMY_CTX_TRF_VALUE ULL(0xf)
#define DUMMY_CTX_GCS_VALUE ULL(0xffff0000)
#define DEFAULT_CTX_VALUE ULL(0x0)
/**
* -------------------------------------------------------
* Private Helper functions required to access and modify
* EL1 context registers at S-EL1.
* -------------------------------------------------------
*/
static void modify_el1_common_regs(uint64_t cm_value)
{
/**
* NOTE: Few EL1 registers "SCTLR_EL1, SPSR_EL1, ELR_EL1" are
* left out consciously as those are important registers for
* execution in each world and overwriting them with dummy value
* would cause unintended crash while executing the test.
*/
write_tcr_el1(cm_value);
write_cpacr_el1(cm_value);
write_csselr_el1(cm_value);
write_esr_el1(cm_value);
write_ttbr0_el1(cm_value);
write_ttbr1_el1(cm_value);
write_mair_el1(cm_value);
write_amair_el1(cm_value);
write_actlr_el1(cm_value);
write_tpidr_el1(cm_value);
write_tpidr_el0(cm_value);
write_tpidrro_el0(cm_value);
write_par_el1(cm_value);
write_far_el1(cm_value);
write_afsr0_el1(cm_value);
write_afsr1_el1(cm_value);
write_contextidr_el1(cm_value);
write_vbar_el1(cm_value);
write_mdccint_el1(cm_value);
write_mdscr_el1(cm_value);
}
static void modify_el1_mte2_regs(uint64_t mte_value)
{
if (is_feat_mte2_supported()) {
write_tfsre0_el1(mte_value);
write_tfsr_el1(mte_value);
write_rgsr_el1(mte_value);
write_gcr_el1(mte_value);
}
}
static void modify_el1_ras_regs(uint64_t ras_value)
{
if (is_feat_ras_supported()) {
write_disr_el1(ras_value);
}
}
static void modify_el1_s1pie_regs(uint64_t s1pie_value)
{
if (is_feat_s1pie_supported()) {
write_pire0_el1(s1pie_value);
write_pir_el1(s1pie_value);
}
}
static void modify_el1_s1poe_regs(uint64_t s1poe_value)
{
if (is_feat_s1poe_supported()) {
write_por_el1(s1poe_value);
}
}
static void modify_el1_s2poe_regs(uint64_t s2poe_value)
{
if (is_feat_s2poe_supported()) {
write_s2por_el1(s2poe_value);
}
}
static void modify_el1_tcr2_regs(uint64_t tcr_value)
{
if (is_feat_tcr2_supported()) {
write_tcr2_el1(tcr_value & DUMMY_CTX_TCR_VALUE);
}
}
static void modify_el1_trf_regs(uint64_t trf_value)
{
if (is_feat_trf_supported()) {
write_trfcr_el1(trf_value & DUMMY_CTX_TRF_VALUE);
}
}
static void modify_el1_gcs_regs(uint64_t gcs_value)
{
if (is_feat_gcs_supported()) {
write_gcscr_el1(gcs_value & DUMMY_CTX_GCS_VALUE);
write_gcscre0_el1(gcs_value & DUMMY_CTX_GCS_VALUE);
write_gcspr_el1(gcs_value & DUMMY_CTX_GCS_VALUE);
write_gcspr_el0(gcs_value & DUMMY_CTX_GCS_VALUE);
}
}
/**
* -----------------------------------------------------
* Public API, to modify/restore EL1 ctx registers:
* -----------------------------------------------------
*/
void modify_el1_ctx_regs(const bool modify_option)
{
uint64_t mask;
if (modify_option == TSP_CORRUPT_EL1_REGS) {
VERBOSE("TSP(S-EL1): Corrupt EL1 Registers with Dummy values\n");
mask = DUMMY_CTX_VALUE;
} else {
VERBOSE("TSP(S-EL1): Restore EL1 Registers with Default values\n");
mask = DEFAULT_CTX_VALUE;
}
modify_el1_common_regs(mask);
modify_el1_mte2_regs(mask);
modify_el1_ras_regs(mask);
modify_el1_s1pie_regs(mask);
modify_el1_s1poe_regs(mask);
modify_el1_s2poe_regs(mask);
modify_el1_tcr2_regs(mask);
modify_el1_trf_regs(mask);
modify_el1_gcs_regs(mask);
}

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

@ -0,0 +1,657 @@
/*
* Copyright (c) 2013-2024, 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/build_message.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", build_version_string);
NOTICE("TSP: %s\n", build_message);
INFO("TSP: Total memory base : 0x%lx\n", (unsigned long) BL32_BASE);
INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_SIZE);
uint32_t linear_id = plat_my_core_pos();
/* Initialize the platform. */
tsp_platform_setup();
/* Initialize secure/applications state here. */
tsp_generic_timer_start();
/* Register secondary entrypoint with the SPMC. */
smc_args = smc_helper(FFA_SECONDARY_EP_REGISTER_SMC64,
(uint64_t) tsp_cpu_on_entry,
0, 0, 0, 0, 0, 0);
if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) {
ERROR("TSP could not register secondary ep (0x%lx)\n",
smc_args._regs[2]);
panic();
}
/* Get TSP's endpoint id. */
smc_args = smc_helper(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0);
if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) {
ERROR("TSP could not get own ID (0x%lx) on core%d\n",
smc_args._regs[2], linear_id);
panic();
}
tsp_id = smc_args._regs[2];
INFO("TSP FF-A endpoint id = 0x%x\n", tsp_id);
/* Get the SPMC ID. */
smc_args = smc_helper(FFA_SPM_ID_GET, 0, 0, 0, 0, 0, 0, 0);
if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) {
ERROR("TSP could not get SPMC ID (0x%lx) on core%d\n",
smc_args._regs[2], linear_id);
panic();
}
spmc_id = smc_args._regs[2];
/* Call RXTX_MAP to map a 4k RX and TX buffer. */
if (ffa_rxtx_map((uintptr_t) send_page,
(uintptr_t) recv_page, 1)) {
ERROR("TSP could not map it's RX/TX Buffers\n");
panic();
}
mailbox.tx_buffer = send_page;
mailbox.rx_buffer = recv_page;
mailbox.rxtx_page_count = 1;
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_on_count++;
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_on_count);
/* Tell SPMD that we are done initialising. */
tsp_loop(set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0));
/* Not reached. */
return 0;
}
/*******************************************************************************
* This function performs any remaining book keeping in the test secure payload
* after this cpu's architectural state has been setup in response to an earlier
* psci cpu_on request.
******************************************************************************/
smc_args_t *tsp_cpu_on_main(void)
{
uint32_t linear_id = plat_my_core_pos();
/* Initialize secure/applications state here. */
tsp_generic_timer_start();
/* Update this cpu's statistics. */
tsp_stats[linear_id].smc_count++;
tsp_stats[linear_id].eret_count++;
tsp_stats[linear_id].cpu_on_count++;
VERBOSE("TSP: cpu 0x%lx turned on\n", read_mpidr());
VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n",
read_mpidr(),
tsp_stats[linear_id].smc_count,
tsp_stats[linear_id].eret_count,
tsp_stats[linear_id].cpu_on_count);
/* ---------------------------------------------
* Jump to the main event loop to return to EL3
* and be ready for the next request on this cpu.
* ---------------------------------------------
*/
return tsp_loop(set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0));
}

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

@ -0,0 +1,115 @@
/*
* Copyright (c) 2014-2025, 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: %u sync s-el1 interrupt requests,"
" %u 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: %u 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 %u\n",
read_mpidr(), id);
VERBOSE("TSP: cpu 0x%lx: %u S-EL1 requests\n",
read_mpidr(), tsp_stats[linear_id].sel1_intr_count);
return 0;
}

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

@ -0,0 +1,301 @@
/*
* Copyright (c) 2013-2025, 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 <bl32/tsp/tsp_el1_context.h>
#include <common/bl_common.h>
#include <common/build_message.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", build_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: %u smcs, %u erets %u 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: %u smcs, %u erets %u 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: %u smcs, %u erets %u 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: %u smcs, %u erets %u 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 %" PRIu64 "\n",
read_mpidr(), max_off_pwrlvl);
INFO("TSP: cpu 0x%lx: %u smcs, %u erets %u 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: %u smcs, %u 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);
/*
* Write a dummy value to an MTE2 register, to simulate usage in the
* secure world
*/
if (is_feat_mte2_supported()) {
write_gcr_el1(0x99);
}
/* 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;
case TSP_MODIFY_EL1_CTX:
/*
* Write dummy values to EL1 context registers, to simulate
* their usage in the secure world.
*/
if (arg1 == TSP_CORRUPT_EL1_REGS) {
modify_el1_ctx_regs(TSP_CORRUPT_EL1_REGS);
} else {
modify_el1_ctx_regs(TSP_RESTORE_EL1_REGS);
}
break;
default:
break;
}
return set_smc_args(func, 0,
results[0],
results[1],
0, 0, 0, 0);
}

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

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

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

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

1603
changelog.yaml Normal file

File diff suppressed because it is too large Load Diff

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

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

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

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

View File

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

View File

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

View File

@ -0,0 +1,31 @@
#
# Copyright (c) 2018-2025, 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
cflags-common += -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))

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