diff --git a/atf-20250711/.checkpatch.conf b/atf-20250711/.checkpatch.conf new file mode 100644 index 000000000..baa983dc9 --- /dev/null +++ b/atf-20250711/.checkpatch.conf @@ -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 diff --git a/atf-20250711/.clang-format b/atf-20250711/.clang-format new file mode 100644 index 000000000..6a89eebf7 --- /dev/null +++ b/atf-20250711/.clang-format @@ -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: '^$' + 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 +... diff --git a/atf-20250711/.commitlintrc.js b/atf-20250711/.commitlintrc.js new file mode 100644 index 000000000..51493eaf0 --- /dev/null +++ b/atf-20250711/.commitlintrc.js @@ -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 */ + }, +}; diff --git a/atf-20250711/.ctags b/atf-20250711/.ctags new file mode 100644 index 000000000..5e608e48c --- /dev/null +++ b/atf-20250711/.ctags @@ -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/ diff --git a/atf-20250711/.cz-adapter.cjs b/atf-20250711/.cz-adapter.cjs new file mode 100644 index 000000000..26aaeb2a0 --- /dev/null +++ b/atf-20250711/.cz-adapter.cjs @@ -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) +} diff --git a/atf-20250711/.cz.json b/atf-20250711/.cz.json new file mode 100644 index 000000000..969a73b86 --- /dev/null +++ b/atf-20250711/.cz.json @@ -0,0 +1,3 @@ +{ + "path": "./.cz-adapter.cjs" +} diff --git a/atf-20250711/.editorconfig b/atf-20250711/.editorconfig new file mode 100644 index 000000000..1b29c8805 --- /dev/null +++ b/atf-20250711/.editorconfig @@ -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 diff --git a/atf-20250711/.github/CODEOWNERS b/atf-20250711/.github/CODEOWNERS new file mode 100644 index 000000000..a2b473293 --- /dev/null +++ b/atf-20250711/.github/CODEOWNERS @@ -0,0 +1,5 @@ +package.json @CJKay +package-lock.json @CJKay + +pyproject.toml @CJKay +poetry.lock @CJKay diff --git a/atf-20250711/.github/dependabot.yml b/atf-20250711/.github/dependabot.yml new file mode 100644 index 000000000..46e5f675d --- /dev/null +++ b/atf-20250711/.github/dependabot.yml @@ -0,0 +1,67 @@ +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.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.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"] diff --git a/atf-20250711/.gitignore b/atf-20250711/.gitignore new file mode 100644 index 000000000..e94257a9a --- /dev/null +++ b/atf-20250711/.gitignore @@ -0,0 +1,59 @@ +# 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/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/ + +# MediaTek related +.vscode/ +Kconfiglib/__pycache__/ diff --git a/atf-20250711/.gitmodules b/atf-20250711/.gitmodules new file mode 100644 index 000000000..858e0c174 --- /dev/null +++ b/atf-20250711/.gitmodules @@ -0,0 +1,4 @@ +[submodule "libtl"] + path = contrib/libtl + url = https://review.trustedfirmware.org/shared/transfer-list-library + shallow = true diff --git a/atf-20250711/.gitreview b/atf-20250711/.gitreview new file mode 100644 index 000000000..afdb74dff --- /dev/null +++ b/atf-20250711/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=review.trustedfirmware.org +port=29418 +project=TF-A/trusted-firmware-a +defaultbranch=integration diff --git a/atf-20250711/.husky/.gitignore b/atf-20250711/.husky/.gitignore new file mode 100644 index 000000000..31354ec13 --- /dev/null +++ b/atf-20250711/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/atf-20250711/.husky/commit-msg b/atf-20250711/.husky/commit-msg new file mode 100755 index 000000000..b5d407b75 --- /dev/null +++ b/atf-20250711/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh + +"$(dirname "$0")/commit-msg.gerrit" "$@" +"$(dirname "$0")/commit-msg.commitlint" "$@" diff --git a/atf-20250711/.husky/commit-msg.commitlint b/atf-20250711/.husky/commit-msg.commitlint new file mode 100755 index 000000000..ca25ce160 --- /dev/null +++ b/atf-20250711/.husky/commit-msg.commitlint @@ -0,0 +1,3 @@ +#!/bin/sh + +npx --no-install commitlint --edit "$1" diff --git a/atf-20250711/.husky/commit-msg.gerrit b/atf-20250711/.husky/commit-msg.gerrit new file mode 100755 index 000000000..b8ce477be --- /dev/null +++ b/atf-20250711/.husky/commit-msg.gerrit @@ -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 diff --git a/atf-20250711/.husky/pre-commit b/atf-20250711/.husky/pre-commit new file mode 100755 index 000000000..f438ddbea --- /dev/null +++ b/atf-20250711/.husky/pre-commit @@ -0,0 +1,3 @@ +#!/bin/sh + +"$(dirname "$0")/pre-commit.copyright" "$@" diff --git a/atf-20250711/.husky/pre-commit.copyright b/atf-20250711/.husky/pre-commit.copyright new file mode 100755 index 000000000..5f838a688 --- /dev/null +++ b/atf-20250711/.husky/pre-commit.copyright @@ -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 diff --git a/atf-20250711/.husky/prepare-commit-msg b/atf-20250711/.husky/prepare-commit-msg new file mode 100755 index 000000000..e38252e15 --- /dev/null +++ b/atf-20250711/.husky/prepare-commit-msg @@ -0,0 +1,5 @@ +#!/bin/sh + +if ! git config --get tf-a.disableCommitizen > /dev/null; then + "$(dirname "$0")/prepare-commit-msg.cz" "$@" +fi diff --git a/atf-20250711/.husky/prepare-commit-msg.cz b/atf-20250711/.husky/prepare-commit-msg.cz new file mode 100755 index 000000000..724527d6f --- /dev/null +++ b/atf-20250711/.husky/prepare-commit-msg.cz @@ -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 diff --git a/atf-20250711/.nvmrc b/atf-20250711/.nvmrc new file mode 100644 index 000000000..ee09fac75 --- /dev/null +++ b/atf-20250711/.nvmrc @@ -0,0 +1 @@ +v20.11.1 diff --git a/atf-20250711/.readthedocs.yaml b/atf-20250711/.readthedocs.yaml new file mode 100644 index 000000000..d308fdd28 --- /dev/null +++ b/atf-20250711/.readthedocs.yaml @@ -0,0 +1,30 @@ +# 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 + +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 diff --git a/atf-20250711/.versionrc.cjs b/atf-20250711/.versionrc.cjs new file mode 100644 index 000000000..ac473b099 --- /dev/null +++ b/atf-20250711/.versionrc.cjs @@ -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; + } + } + } + ] +}; diff --git a/atf-20250711/Config.in b/atf-20250711/Config.in new file mode 100644 index 000000000..591ffac87 --- /dev/null +++ b/atf-20250711/Config.in @@ -0,0 +1,7 @@ +# +# Copyright (c) 2023, MediaTek Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +source "plat/mediatek/apsoc_common/Config.in" diff --git a/atf-20250711/Kconfiglib/LICENSE.txt b/atf-20250711/Kconfiglib/LICENSE.txt new file mode 100644 index 000000000..8b31efca2 --- /dev/null +++ b/atf-20250711/Kconfiglib/LICENSE.txt @@ -0,0 +1,5 @@ +Copyright (c) 2011-2019, Ulf Magnusson + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/atf-20250711/Kconfiglib/MANIFEST.in b/atf-20250711/Kconfiglib/MANIFEST.in new file mode 100644 index 000000000..881a7944b --- /dev/null +++ b/atf-20250711/Kconfiglib/MANIFEST.in @@ -0,0 +1,2 @@ +# Include the license file in source distributions +include LICENSE.txt diff --git a/atf-20250711/Kconfiglib/README.rst b/atf-20250711/Kconfiglib/README.rst new file mode 100644 index 000000000..b59f04ece --- /dev/null +++ b/atf-20250711/Kconfiglib/README.rst @@ -0,0 +1,841 @@ +.. contents:: Table of contents + :backlinks: none + +News +---- + +Dependency loop with recent linux-next kernels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To fix issues with dependency loops on recent linux-next kernels, apply `this +patch `_. Hopefully, +it will be in ``linux-next`` soon. + +``windows-curses`` is no longer automatically installed on Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Starting with Kconfiglib 13.0.0, the `windows-curses +`__ package is no longer +automatically installed on Windows, and needs to be installed manually for the +terminal ``menuconfig`` to work. + +This fixes installation of Kconfiglib on MSYS2, which is not compatible with +``windows-curses``. See `this issue +`__. + +The ``menuconfig`` now shows a hint re. installing ``windows-curses`` when the +``curses`` module can't be imported on Windows. + +Sorry if this change caused problems! + +Overview +-------- + +Kconfiglib is a `Kconfig +`__ +implementation in Python 2/3. It started out as a helper library, but now has a +enough functionality to also work well as a standalone Kconfig implementation +(including `terminal and GUI menuconfig interfaces `_ +and `Kconfig extensions`_). + +The entire library is contained in `kconfiglib.py +`_. The +bundled scripts are implemented on top of it. Implementing your own scripts +should be relatively easy, if needed. + +Kconfiglib is used exclusively by e.g. the `Zephyr +`__, `esp-idf +`__, and `ACRN +`__ projects. It is also used for many small helper +scripts in various projects. + +Since Kconfiglib is based around a library, it can be used e.g. to generate a +`Kconfig cross-reference +`_, using +the same robust Kconfig parser used for other Kconfig tools, instead of brittle +ad-hoc parsing. The documentation generation script can be found `here +`__. + +Kconfiglib implements the recently added `Kconfig preprocessor +`__. +For backwards compatibility, environment variables can be referenced both as +``$(FOO)`` (the new syntax) and as ``$FOO`` (the old syntax). The old syntax is +deprecated, but will probably be supported for a long time, as it's needed to +stay compatible with older Linux kernels. The major version will be increased +if support is ever dropped. Using the old syntax with an undefined environment +variable keeps the string as is. + +Note: See `this issue `__ if +you run into a "macro expanded to blank string" error with kernel 4.18+. + +See `this page +`__ for some +Kconfig tips and best practices. + +Installation +------------ + +Installation with pip +~~~~~~~~~~~~~~~~~~~~~ + +Kconfiglib is available on `PyPI `_ and can be +installed with e.g. + +.. code:: + + $ pip(3) install kconfiglib + +Microsoft Windows is supported. + +The ``pip`` installation will give you both the base library and the following +executables. All but two (``genconfig`` and ``setconfig``) mirror functionality +available in the C tools. + +- `menuconfig `_ + +- `guiconfig `_ + +- `oldconfig `_ + +- `olddefconfig `_ + +- `savedefconfig `_ + +- `defconfig `_ + +- `alldefconfig `_ + +- `allnoconfig `_ + +- `allmodconfig `_ + +- `allyesconfig `_ + +- `listnewconfig `_ + +- `genconfig `_ + +- `setconfig `_ + +``genconfig`` is intended to be run at build time. It generates a C header from +the configuration and (optionally) information that can be used to rebuild only +files that reference Kconfig symbols that have changed value. + +Starting with Kconfiglib version 12.2.0, all utilities are compatible with both +Python 2 and Python 3. Previously, ``menuconfig.py`` only ran under Python 3 +(i.e., it's now more backwards compatible than before). + +**Note:** If you install Kconfiglib with ``pip``'s ``--user`` flag, make sure +that your ``PATH`` includes the directory where the executables end up. You can +list the installed files with ``pip(3) show -f kconfiglib``. + +All releases have a corresponding tag in the git repository, e.g. ``v14.1.0`` +(the latest version). + +`Semantic versioning `_ is used. There's been ten small +changes to the behavior of the API, a Windows packaging change, and a hashbang +change to use ``python3`` +(`1 `_, +`2 `_, +`3 `_, +`4 `_, +`5 `_, +`6 `_, +`7 `_, +`8 `_, +`9 `_, +`10 `_, +`Windows packaging change `_, +`Python 3 hashbang change `_), +which is why the major version is at 14 rather than 2. I do major version bumps +for all behavior changes, even tiny ones, and most of these were fixes for baby +issues in the early days of the Kconfiglib 2 API. + +Manual installation +~~~~~~~~~~~~~~~~~~~ + +Just drop ``kconfiglib.py`` and the scripts you want somewhere. There are no +third-party dependencies, but the terminal ``menuconfig`` won't work on Windows +unless a package like `windows-curses +`__ is installed. + +Installation for the Linux kernel +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See the module docstring at the top of `kconfiglib.py `_. + +Python version compatibility (2.7/3.2+) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Kconfiglib and all utilities run under both Python 2.7 and Python 3.2 and +later. The code mostly uses basic Python features and has no third-party +dependencies, so keeping it backwards-compatible is pretty low effort. + +The 3.2 requirement comes from ``argparse``. ``format()`` with unnumbered +``{}`` is used as well. + +A recent Python 3 version is recommended if you have a choice, as it'll give +you better Unicode handling. + +Getting started +--------------- + +1. `Install `_ the library and the utilities. + +2. Write `Kconfig + `__ + files that describe the available configuration options. See `this page + `__ for some + general Kconfig advice. + +3. Generate an initial configuration with e.g. ``menuconfig``/``guiconfig`` or + ``alldefconfig``. The configuration is saved as ``.config`` by default. + + For more advanced projects, the ``defconfig`` utility can be used to + generate the initial configuration from an existing configuration file. + Usually, this existing configuration file would be a minimal configuration + file, as generated by e.g. ``savedefconfig``. + +4. Run ``genconfig`` to generate a header file. By default, it is saved as + ``config.h``. + + Normally, ``genconfig`` would be run automatically as part of the build. + + Before writing a header file or other configuration output, Kconfiglib + compares the old contents of the file against the new contents. If there's + no change, the write is skipped. This avoids updating file metadata like the + modification time, and might save work depending on your build setup. + + Adding new configuration output formats should be relatively straightforward. + See the implementation of ``write_config()`` in `kconfiglib.py + `_. + The documentation for the ``Symbol.config_string`` property has some tips as + well. + +5. To update an old ``.config`` file after the Kconfig files have changed (e.g. + to add new options), run ``oldconfig`` (prompts for values for new options) + or ``olddefconfig`` (gives new options their default value). Entering the + ``menuconfig`` or ``guiconfig`` interface and saving the configuration will + also update it (the configuration interfaces always prompt for saving + on exit if it would modify the contents of the ``.config`` file). + + Due to Kconfig semantics, simply loading an old ``.config`` file performs an + implicit ``olddefconfig``, so building will normally not be affected by + having an outdated configuration. + +Whenever ``.config`` is overwritten, the previous version of the file is saved +to ``.config.old`` (or, more generally, to ``$KCONFIG_CONFIG.old``). + +Using ``.config`` files as Make input +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``.config`` files use Make syntax and can be included directly in Makefiles to +read configuration values from there. This is why ``n``-valued +``bool``/``tristate`` values are written out as ``# CONFIG_FOO is not set`` (a +Make comment) in ``.config``, allowing them to be tested with ``ifdef`` in +Make. + +If you make use of this, you might want to pass ``--config-out `` to +``genconfig`` and include the configuration file it generates instead of +including ``.config`` directly. This has the advantage that the generated +configuration file will always be a "full" configuration file, even if +``.config`` is outdated. Otherwise, it might be necessary to run +``old(def)config`` or ``menuconfig``/``guiconfig`` before rebuilding with an +outdated ``.config``. + +If you use ``--sync-deps`` to generate incremental build information, you can +include ``deps/auto.conf`` instead, which is also a full configuration file. + +Useful helper macros +~~~~~~~~~~~~~~~~~~~~ + +The `include/linux/kconfig.h +`_ +header in the Linux kernel defines some useful helper macros for testing +Kconfig configuration values. + +``IS_ENABLED()`` is generally useful, allowing configuration values to be +tested in ``if`` statements with no runtime overhead. + +Incremental building +~~~~~~~~~~~~~~~~~~~~ + +See the docstring for ``Kconfig.sync_deps()`` in `kconfiglib.py +`_ for hints +on implementing incremental builds (rebuilding just source files that reference +changed configuration values). + +Running the ``scripts/basic/fixdep.c`` tool from the kernel on the output of +``gcc -MD `` might give you an idea of how it all fits together. + +Library documentation +--------------------- + +Kconfiglib comes with extensive documentation in the form of docstrings. To view it, run e.g. +the following command: + +.. code:: sh + + $ pydoc(3) kconfiglib + +For HTML output, add ``-w``: + +.. code:: sh + + $ pydoc(3) -w kconfiglib + +This will also work after installing Kconfiglib with ``pip(3)``. + +Documentation for other modules can be viewed in the same way (though a plain +``--help`` will work when they're run as executables): + +.. code:: sh + + $ pydoc(3) menuconfig/guiconfig/... + +A good starting point for learning the library is to read the module docstring +(which you could also just read directly at the beginning of `kconfiglib.py +`_). It +gives an introduction to symbol values, the menu tree, and expressions. + +After reading the module docstring, a good next step is to read the ``Kconfig`` +class documentation, and then the documentation for the ``Symbol``, ``Choice``, +and ``MenuNode`` classes. + +Please tell me if something is unclear or can be explained better. + +Library features +---------------- + +Kconfiglib can do the following, among other things: + +- **Programmatically get and set symbol values** + + See `allnoconfig.py + `_ and + `allyesconfig.py + `_, + which are automatically verified to produce identical output to the standard + ``make allnoconfig`` and ``make allyesconfig``. + +- **Read and write .config and defconfig files** + + The generated ``.config`` and ``defconfig`` (minimal configuration) files are + character-for-character identical to what the C implementation would generate + (except for the header comment). The test suite relies on this, as it + compares the generated files. + +- **Write C headers** + + The generated headers use the same format as ``include/generated/autoconf.h`` + from the Linux kernel. Output for symbols appears in the order that they're + defined, unlike in the C tools (where the order depends on the hash table + implementation). + +- **Implement incremental builds** + + This uses the same scheme as the ``include/config`` directory in the kernel: + Symbols are translated into files that are touched when the symbol's value + changes between builds, which can be used to avoid having to do a full + rebuild whenever the configuration is changed. + + See the ``sync_deps()`` function for more information. + +- **Inspect symbols** + + Printing a symbol or other item (which calls ``__str__()``) returns its + definition in Kconfig format. This also works for symbols defined in multiple + locations. + + A helpful ``__repr__()`` is on all objects too. + + All ``__str__()`` and ``__repr__()`` methods are deliberately implemented + with just public APIs, so all symbol information can be fetched separately as + well. + +- **Inspect expressions** + + Expressions use a simple tuple-based format that can be processed manually + if needed. Expression printing and evaluation functions are provided, + implemented with public APIs. + +- **Inspect the menu tree** + + The underlying menu tree is exposed, including submenus created implicitly + from symbols depending on preceding symbols. This can be used e.g. to + implement menuconfig-like functionality. + + See `menuconfig.py + `_/`guiconfig.py + `_ and the + minimalistic `menuconfig_example.py + `_ + example. + +Kconfig extensions +~~~~~~~~~~~~~~~~~~ + +The following Kconfig extensions are available: + +- ``source`` supports glob patterns and includes each matching file. A pattern + is required to match at least one file. + + A separate ``osource`` statement is available for cases where it's okay for + the pattern to match no files (in which case ``osource`` turns into a no-op). + +- A relative ``source`` statement (``rsource``) is available, where file paths + are specified relative to the directory of the current Kconfig file. An + ``orsource`` statement is available as well, analogous to ``osource``. + +- Preprocessor user functions can be defined in Python, which makes it simple + to integrate information from existing Python tools into Kconfig (e.g. to + have Kconfig symbols depend on hardware information stored in some other + format). + + See the *Kconfig extensions* section in the + `kconfiglib.py `_ + module docstring for more information. + +- ``def_int``, ``def_hex``, and ``def_string`` are available in addition to + ``def_bool`` and ``def_tristate``, allowing ``int``, ``hex``, and ``string`` + symbols to be given a type and a default at the same time. + + These can be useful in projects that make use of symbols defined in multiple + locations, and remove some Kconfig inconsistency. + +- Environment variables are expanded directly in e.g. ``source`` and + ``mainmenu`` statements, meaning ``option env`` symbols are redundant. + + This is the standard behavior with the new `Kconfig preprocessor + `__, + which Kconfiglib implements. + + ``option env`` symbols are accepted but ignored, which leads the caveat that + they must have the same name as the environment variables they reference + (Kconfiglib warns if the names differ). This keeps Kconfiglib compatible with + older Linux kernels, where the name of the ``option env`` symbol always + matched the environment variable. Compatibility with older Linux kernels is + the main reason ``option env`` is still supported. + + The C tools have dropped support for ``option env``. + +- Two extra optional warnings can be enabled by setting environment variables, + covering cases that are easily missed when making changes to Kconfig files: + + * ``KCONFIG_WARN_UNDEF``: If set to ``y``, warnings will be generated for all + references to undefined symbols within Kconfig files. The only gotcha is + that all hex literals must be prefixed with ``0x`` or ``0X``, to make it + possible to distinguish them from symbol references. + + Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many + shared Kconfig files, leading to some safe undefined symbol references. + ``KCONFIG_WARN_UNDEF`` is useful in projects that only have a single + Kconfig tree though. + + ``KCONFIG_STRICT`` is an older alias for this environment variable, + supported for backwards compatibility. + + * ``KCONFIG_WARN_UNDEF_ASSIGN``: If set to ``y``, warnings will be generated + for all assignments to undefined symbols within ``.config`` files. By + default, no such warnings are generated. + + This warning can also be enabled/disabled by setting + ``Kconfig.warn_assign_undef`` to ``True``/``False``. + +Other features +-------------- + +- **Single-file implementation** + + The entire library is contained in `kconfiglib.py + `_. + + The tools implemented on top of it are one file each. + +- **Robust and highly compatible with the C Kconfig tools** + +  The `test suite `_ + automatically compares output from Kconfiglib and the C tools + by diffing the generated ``.config`` files for the real kernel Kconfig and + defconfig files, for all ARCHes. + + This currently involves comparing the output for 36 ARCHes and 498 defconfig + files (or over 18000 ARCH/defconfig combinations in "obsessive" test suite + mode). All tests are expected to pass. + + A comprehensive suite of selftests is included as well. + +- **Not horribly slow despite being a pure Python implementation** + + The `allyesconfig.py + `_ + script currently runs in about 1.3 seconds on the Linux kernel on a Core i7 + 2600K (with a warm file cache), including the ``make`` overhead from ``make + scriptconfig``. Note that the Linux kernel Kconfigs are absolutely massive + (over 14k symbols for x86) compared to most projects, and also have overhead + from running shell commands via the Kconfig preprocessor. + + Kconfiglib is especially speedy in cases where multiple ``.config`` files + need to be processed, because the ``Kconfig`` files will only need to be parsed + once. + + For long-running jobs, `PyPy `_ gives a big performance + boost. CPython is faster for short-running jobs as PyPy needs some time to + warm up. + + Kconfiglib also works well with the + `multiprocessing `_ + module. No global state is kept. + +- **Generates more warnings than the C implementation** + + Generates the same warnings as the C implementation, plus additional ones. + Also detects dependency and ``source`` loops. + + All warnings point out the location(s) in the ``Kconfig`` files where a + symbol is defined, where applicable. + +- **Unicode support** + + Unicode characters in string literals in ``Kconfig`` and ``.config`` files are + correctly handled. This support mostly comes for free from Python. + +- **Windows support** + + Nothing Linux-specific is used. Universal newlines mode is used for both + Python 2 and Python 3. + + The `Zephyr `_ project uses Kconfiglib to + generate ``.config`` files and C headers on Linux as well as Windows. + +- **Internals that (mostly) mirror the C implementation** + + While being simpler to understand and tweak. + +Menuconfig interfaces +--------------------- + +Three configuration interfaces are currently available: + +- `menuconfig.py `_ + is a terminal-based configuration interface implemented using the standard + Python ``curses`` module. ``xconfig`` features like showing invisible symbols and + showing symbol names are included, and it's possible to jump directly to a symbol + in the menu tree (even if it's currently invisible). + + .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/menuconfig.gif + + *There is now also a show-help mode that shows the help text of the currently + selected symbol in the help window at the bottom.* + + Starting with Kconfiglib 12.2.0, ``menuconfig.py`` runs under both Python 2 + and Python 3 (previously, it only ran under Python 3, so this was a + backport). Running it under Python 3 provides better support for Unicode text + entry (``get_wch()`` is not available in the ``curses`` module on Python 2). + + There are no third-party dependencies on \*nix. On Windows, + the ``curses`` modules is not available by default, but support + can be added by installing the ``windows-curses`` package: + + .. code-block:: shell + + $ pip install windows-curses + + This uses wheels built from `this repository + `_, which is in turn + based on Christoph Gohlke's `Python Extension Packages for Windows + `_. + + See the docstring at the top of `menuconfig.py + `_ for + more information about the terminal menuconfig implementation. + +- `guiconfig.py + `_ is a + graphical configuration interface written in `Tkinter + `_. Like ``menuconfig.py``, + it supports showing all symbols (with invisible symbols in red) and jumping + directly to symbols. Symbol values can also be changed directly from the + jump-to dialog. + + When single-menu mode is enabled, a single menu is shown at a time, like in + the terminal menuconfig. Only this mode distinguishes between symbols defined + with ``config`` and symbols defined with ``menuconfig``. + + ``guiconfig.py`` has been tested on X11, Windows, and macOS, and is + compatible with both Python 2 and Python 3. + + Despite being part of the Python standard library, ``tkinter`` often isn't + included by default in Python installations on Linux. These commands will + install it on a few different distributions: + + - Ubuntu: ``sudo apt install python-tk``/``sudo apt install python3-tk`` + + - Fedora: ``dnf install python2-tkinter``/``dnf install python3-tkinter`` + + - Arch: ``sudo pacman -S tk`` + + - Clear Linux: ``sudo swupd bundle-add python3-tcl`` + + Screenshot below, with show-all mode enabled and the jump-to dialog open: + + .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/guiconfig.png + + To avoid having to carry around a bunch of GIFs, the image data is embedded + in ``guiconfig.py``. To use separate GIF files instead, change + ``_USE_EMBEDDED_IMAGES`` to ``False`` in ``guiconfig.py``. The image files + can be found in the `screenshots + `_ + branch. + + I did my best with the images, but some are definitely only art adjacent. + Touch-ups are welcome. :) + +- `pymenuconfig `_, built by `RomaVis + `_, is an older portable Python 2/3 TkInter + menuconfig implementation. + + Screenshot below: + + .. image:: https://raw.githubusercontent.com/RomaVis/pymenuconfig/master/screenshot.PNG + + While working on the terminal menuconfig implementation, I added a few APIs + to Kconfiglib that turned out to be handy. ``pymenuconfig`` predates + ``menuconfig.py`` and ``guiconfig.py``, and so didn't have them available. + Blame me for any workarounds. + +Examples +-------- + +Example scripts +~~~~~~~~~~~~~~~ + +The `examples/ `_ directory contains some simple example scripts. Among these are the following ones. Make sure you run them with the latest version of Kconfiglib, as they might make use of newly added features. + +- `eval_expr.py `_ evaluates an expression in the context of a configuration. + +- `find_symbol.py `_ searches through expressions to find references to a symbol, also printing a "backtrace" with parents for each reference found. + +- `help_grep.py `_ searches for a string in all help texts. + +- `print_tree.py `_ prints a tree of all configuration items. + +- `print_config_tree.py `_ is similar to ``print_tree.py``, but dumps the tree as it would appear in ``menuconfig``, including values. This can be handy for visually diffing between ``.config`` files and different versions of ``Kconfig`` files. + +- `list_undefined.py `_ finds references to symbols that are not defined by any architecture in the Linux kernel. + +- `merge_config.py `_ merges configuration fragments to produce a complete .config, similarly to ``scripts/kconfig/merge_config.sh`` from the kernel. + +- `menuconfig_example.py `_ implements a configuration interface that uses notation similar to ``make menuconfig``. It's deliberately kept as simple as possible to demonstrate just the core concepts. + +Real-world examples +~~~~~~~~~~~~~~~~~~~ + +- `kconfig.py + `_ + from the `Zephyr `_ project handles + ``.config`` and header file generation, also doing configuration fragment + merging + +- `genrest.py + `_ + generates a Kconfig symbol cross-reference, which can be viewed `here + `__ + +- `CMake and IDE integration + `_ from + the ESP-IDF project, via a configuration server program. + +- `A script for turning on USB-related options + `_, + from the `syzkaller `_ project. + +- `Various automated checks + `_, + including a check for references to undefined Kconfig symbols in source code. + See the ``KconfigCheck`` class. + +- `Various utilities + `_ + from the `ACRN `_ project + +These use the older Kconfiglib 1 API, which was clunkier and not as general +(functions instead of properties, no direct access to the menu structure or +properties, uglier ``__str__()`` output): + +- `genboardscfg.py `_ from `Das U-Boot `_ generates some sort of legacy board database by pulling information from a newly added Kconfig-based configuration system (as far as I understand it :). + +- `gen-manual-lists.py `_ generated listings for an appendix in the `Buildroot `_ manual. (The listing has since been removed.) + +- `gen_kconfig_doc.py `_ from the `esp-idf `_ project generates documentation from Kconfig files. + +- `SConf `_ builds an interactive configuration interface (like ``menuconfig``) on top of Kconfiglib, for use e.g. with `SCons `_. + +- `kconfig-diff.py `_ -- a script by `dubiousjim `_ that compares kernel configurations. + +- Originally, Kconfiglib was used in chapter 4 of my `master's thesis `_ to automatically generate a "minimal" kernel for a given system. Parts of it bother me a bit now, but that's how it goes with old work. + +Sample ``make iscriptconfig`` session +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following log should give some idea of the functionality available in the API: + +.. code-block:: + + $ make iscriptconfig + A Kconfig instance 'kconf' for the architecture x86 has been created. + >>> kconf # Calls Kconfig.__repr__() + + >>> kconf.mainmenu_text # Expanded main menu text + 'Linux/x86 4.14.0-rc7 Kernel Configuration' + >>> kconf.top_node # The implicit top-level menu + + >>> kconf.top_node.list # First child menu node + + >>> print(kconf.top_node.list) # Calls MenuNode.__str__() + config SRCARCH + string + option env="SRCARCH" + default "x86" + >>> sym = kconf.top_node.list.next.item # Item contained in next menu node + >>> print(sym) # Calls Symbol.__str__() + config 64BIT + bool "64-bit kernel" if ARCH = "x86" + default ARCH != "i386" + help + Say yes to build a 64-bit kernel - formerly known as x86_64 + Say no to build a 32-bit kernel - formerly known as i386 + >>> sym # Calls Symbol.__repr__() + + >>> sym.assignable # Currently assignable values (0, 1, 2 = n, m, y) + (0, 2) + >>> sym.set_value(0) # Set it to n + True + >>> sym.tri_value # Check the new value + 0 + >>> sym = kconf.syms["X86_MPPARSE"] # Look up symbol by name + >>> print(sym) + config X86_MPPARSE + bool "Enable MPS table" if (ACPI || SFI) && X86_LOCAL_APIC + default y if X86_LOCAL_APIC + help + For old smp systems that do not have proper acpi support. Newer systems + (esp with 64bit cpus) with acpi support, MADT and DSDT will override it + >>> default = sym.defaults[0] # Fetch its first default + >>> sym = default[1] # Fetch the default's condition (just a Symbol here) + >>> print(sym) + config X86_LOCAL_APIC + bool + default y + select IRQ_DOMAIN_HIERARCHY + select PCI_MSI_IRQ_DOMAIN if PCI_MSI + depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI + >>> sym.nodes # Show the MenuNode(s) associated with it + [] + >>> kconfiglib.expr_str(sym.defaults[0][1]) # Print the default's condition + 'X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI' + >>> kconfiglib.expr_value(sym.defaults[0][1]) # Evaluate it (0 = n) + 0 + >>> kconf.syms["64BIT"].set_value(2) + True + >>> kconfiglib.expr_value(sym.defaults[0][1]) # Evaluate it again (2 = y) + 2 + >>> kconf.write_config("myconfig") # Save a .config + >>> ^D + $ cat myconfig + # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) + CONFIG_64BIT=y + CONFIG_X86_64=y + CONFIG_X86=y + CONFIG_INSTRUCTION_DECODER=y + CONFIG_OUTPUT_FORMAT="elf64-x86-64" + CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" + CONFIG_LOCKDEP_SUPPORT=y + CONFIG_STACKTRACE_SUPPORT=y + CONFIG_MMU=y + ... + +Test suite +---------- + +The test suite is run with + +.. code:: + + $ python(3) Kconfiglib/testsuite.py + +`pypy `_ works too, and is much speedier for everything except ``allnoconfig.py``/``allnoconfig_simpler.py``/``allyesconfig.py``, where it doesn't have time to warm up since +the scripts are run via ``make scriptconfig``. + +The test suite must be run from the top-level kernel directory. It requires that the +Kconfiglib git repository has been cloned into it and that the makefile patch has been applied. + +To get rid of warnings generated for the kernel ``Kconfig`` files, add ``2>/dev/null`` to the command to +discard ``stderr``. + +**NOTE: Forgetting to apply the Makefile patch will cause some tests that compare generated configurations to fail** + +**NOTE: The test suite overwrites .config in the kernel root, so make sure to back it up.** + +The test suite consists of a set of selftests and a set of compatibility tests that +compare configurations generated by Kconfiglib with +configurations generated by the C tools, for a number of cases. See +`testsuite.py `_ +for the available options. + +The `tests/reltest `_ script runs the test suite +and all the example scripts for both Python 2 and Python 3, verifying that everything works. + +Rarely, the output from the C tools is changed slightly (most recently due to a +`change `_ I added). +If you get test suite failures, try running the test suite again against the +`linux-next tree `_, +which has all the latest changes. I will make it clear if any +non-backwards-compatible changes appear. + +A lot of time is spent waiting around for ``make`` and the C utilities (which need to reparse all the +Kconfig files for each defconfig test). Adding some multiprocessing to the test suite would make sense +too. + +Notes +----- + +* This is version 2 of Kconfiglib, which is not backwards-compatible with + Kconfiglib 1. A summary of changes between Kconfiglib 1 and Kconfiglib + 2 can be found `here + `__. + +* I sometimes see people add custom output formats, which is pretty + straightforward to do (see the implementations of ``write_autoconf()`` and + ``write_config()`` for a template, and also the documentation of the + ``Symbol.config_string`` property). If you come up with something you think + might be useful to other people, I'm happy to take it in upstream. Batteries + included and all that. + +* Kconfiglib assumes the modules symbol is ``MODULES``, which is backwards-compatible. + A warning is printed by default if ``option modules`` is set on some other symbol. + + Let me know if you need proper ``option modules`` support. It wouldn't be that + hard to add. + +Thanks +------ + +- To `RomaVis `_, for making + `pymenuconfig `_ and suggesting + the ``rsource`` keyword. + +- To `Mitja Horvat `_, for adding support + for user-defined styles to the terminal menuconfig. + +- To `Philip Craig `_ for adding + support for the ``allnoconfig_y`` option and fixing an obscure issue + with ``comment``\s inside ``choice``\s (that didn't affect correctness but + made outputs differ). ``allnoconfig_y`` is used to force certain symbols + to ``y`` during ``make allnoconfig`` to improve coverage. + +License +------- + +See `LICENSE.txt `_. SPDX license identifiers are used in the +source code. diff --git a/atf-20250711/Kconfiglib/alldefconfig.py b/atf-20250711/Kconfiglib/alldefconfig.py new file mode 100755 index 000000000..56c4caa92 --- /dev/null +++ b/atf-20250711/Kconfiglib/alldefconfig.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Writes a configuration file where all symbols are set to their their default +values. + +The default output filename is '.config'. A different filename can be passed in +the KCONFIG_CONFIG environment variable. + +Usage for the Linux kernel: + + $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/alldefconfig.py +""" +import kconfiglib + + +def main(): + kconf = kconfiglib.standard_kconfig(__doc__) + kconf.load_allconfig("alldef.config") + print(kconf.write_config()) + + +if __name__ == "__main__": + main() diff --git a/atf-20250711/Kconfiglib/allmodconfig.py b/atf-20250711/Kconfiglib/allmodconfig.py new file mode 100755 index 000000000..bfb72b402 --- /dev/null +++ b/atf-20250711/Kconfiglib/allmodconfig.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Writes a configuration file where as many symbols as possible are set to 'm'. + +The default output filename is '.config'. A different filename can be passed +in the KCONFIG_CONFIG environment variable. + +Usage for the Linux kernel: + + $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/allmodconfig.py +""" +import kconfiglib + + +def main(): + kconf = kconfiglib.standard_kconfig(__doc__) + + # See allnoconfig.py + kconf.warn = False + + for sym in kconf.unique_defined_syms: + if sym.orig_type == kconfiglib.BOOL: + # 'bool' choice symbols get their default value, as determined by + # e.g. 'default's on the choice + if not sym.choice: + # All other bool symbols get set to 'y', like for allyesconfig + sym.set_value(2) + elif sym.orig_type == kconfiglib.TRISTATE: + sym.set_value(1) + + for choice in kconf.unique_choices: + choice.set_value(2 if choice.orig_type == kconfiglib.BOOL else 1) + + kconf.warn = True + + kconf.load_allconfig("allmod.config") + + print(kconf.write_config()) + + +if __name__ == "__main__": + main() diff --git a/atf-20250711/Kconfiglib/allnoconfig.py b/atf-20250711/Kconfiglib/allnoconfig.py new file mode 100755 index 000000000..de90d8bf4 --- /dev/null +++ b/atf-20250711/Kconfiglib/allnoconfig.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Writes a configuration file where as many symbols as possible are set to 'n'. + +The default output filename is '.config'. A different filename can be passed +in the KCONFIG_CONFIG environment variable. + +Usage for the Linux kernel: + + $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/allnoconfig.py +""" + +# See examples/allnoconfig_walk.py for another way to implement this script + +import kconfiglib + + +def main(): + kconf = kconfiglib.standard_kconfig(__doc__) + + # Avoid warnings that would otherwise get printed by Kconfiglib for the + # following: + # + # 1. Assigning a value to a symbol without a prompt, which never has any + # effect + # + # 2. Assigning values invalid for the type (only bool/tristate symbols + # accept 0/1/2, for n/m/y). The assignments will be ignored for other + # symbol types, which is what we want. + kconf.warn = False + for sym in kconf.unique_defined_syms: + sym.set_value(2 if sym.is_allnoconfig_y else 0) + kconf.warn = True + + kconf.load_allconfig("allno.config") + + print(kconf.write_config()) + + +if __name__ == "__main__": + main() diff --git a/atf-20250711/Kconfiglib/allyesconfig.py b/atf-20250711/Kconfiglib/allyesconfig.py new file mode 100755 index 000000000..90eb9b8d6 --- /dev/null +++ b/atf-20250711/Kconfiglib/allyesconfig.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Writes a configuration file where as many symbols as possible are set to 'y'. + +The default output filename is '.config'. A different filename can be passed +in the KCONFIG_CONFIG environment variable. + +Usage for the Linux kernel: + + $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/allyesconfig.py +""" +import kconfiglib + + +def main(): + kconf = kconfiglib.standard_kconfig(__doc__) + + # See allnoconfig.py + kconf.warn = False + + # Try to set all symbols to 'y'. Dependencies might truncate the value down + # later, but this will at least give the highest possible value. + # + # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex + # symbols still take a string, because they preserve formatting). + for sym in kconf.unique_defined_syms: + # Set choice symbols to 'm'. This value will be ignored for choices in + # 'y' mode (the "normal" mode), which will instead just get their + # default selection, but will set all symbols in m-mode choices to 'm', + # which is as high as they can go. + # + # Here's a convoluted example of how you might get an m-mode choice + # even during allyesconfig: + # + # choice + # tristate "weird choice" + # depends on m + sym.set_value(1 if sym.choice else 2) + + # Set all choices to the highest possible mode + for choice in kconf.unique_choices: + choice.set_value(2) + + kconf.warn = True + + kconf.load_allconfig("allyes.config") + + print(kconf.write_config()) + + +if __name__ == "__main__": + main() diff --git a/atf-20250711/Kconfiglib/defconfig.py b/atf-20250711/Kconfiglib/defconfig.py new file mode 100755 index 000000000..b1792731f --- /dev/null +++ b/atf-20250711/Kconfiglib/defconfig.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Reads a specified configuration file, then writes a new configuration file. +This can be used to initialize the configuration from e.g. an arch-specific +configuration file. This input configuration file would usually be a minimal +configuration file, as generated by e.g. savedefconfig. + +The default output filename is '.config'. A different filename can be passed in +the KCONFIG_CONFIG environment variable. +""" +import argparse + +import kconfiglib + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + + parser.add_argument( + "--kconfig", + default="Kconfig", + help="Top-level Kconfig file (default: Kconfig)") + + parser.add_argument( + "config", + metavar="CONFIGURATION", + help="Input configuration file") + + args = parser.parse_args() + + kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True) + print(kconf.load_config(args.config)) + print(kconf.write_config()) + + +if __name__ == "__main__": + main() diff --git a/atf-20250711/Kconfiglib/genconfig.py b/atf-20250711/Kconfiglib/genconfig.py new file mode 100755 index 000000000..62f065ba7 --- /dev/null +++ b/atf-20250711/Kconfiglib/genconfig.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Generates a header file with #defines from the configuration, matching the +format of include/generated/autoconf.h in the Linux kernel. + +Optionally, also writes the configuration output as a .config file. See +--config-out. + +The --sync-deps, --file-list, and --env-list options generate information that +can be used to avoid needless rebuilds/reconfigurations. + +Before writing a header or configuration file, Kconfiglib compares the old +contents of the file against the new contents. If there's no change, the write +is skipped. This avoids updating file metadata like the modification time, and +might save work depending on your build setup. + +By default, the configuration is generated from '.config'. A different +configuration file can be passed in the KCONFIG_CONFIG environment variable. + +A custom header string can be inserted at the beginning of generated +configuration and header files by setting the KCONFIG_CONFIG_HEADER and +KCONFIG_AUTOHEADER_HEADER environment variables, respectively (this also works +for other scripts). The string is not automatically made a comment (this is by +design, to allow anything to be added), and no trailing newline is added, so +add '/* */', '#', and newlines as appropriate. + +See https://www.gnu.org/software/make/manual/make.html#Multi_002dLine for a +handy way to define multi-line variables in makefiles, for use with custom +headers. Remember to export the variable to the environment. +""" +import argparse +import os +import sys + +import kconfiglib + + +DEFAULT_SYNC_DEPS_PATH = "deps/" + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + + parser.add_argument( + "--header-path", + metavar="HEADER_FILE", + help=""" +Path to write the generated header file to. If not specified, the path in the +environment variable KCONFIG_AUTOHEADER is used if it is set, and 'config.h' +otherwise. +""") + + parser.add_argument( + "--config-out", + metavar="CONFIG_FILE", + help=""" +Write the configuration to CONFIG_FILE. This is useful if you include .config +files in Makefiles, as the generated configuration file will be a full .config +file even if .config is outdated. The generated configuration matches what +olddefconfig would produce. If you use sync-deps, you can include +deps/auto.conf instead. --config-out is meant for cases where incremental build +information isn't needed. +""") + + parser.add_argument( + "--sync-deps", + metavar="OUTPUT_DIR", + nargs="?", + const=DEFAULT_SYNC_DEPS_PATH, + help=""" +Enable generation of symbol dependency information for incremental builds, +optionally specifying the output directory (default: {}). See the docstring of +Kconfig.sync_deps() in Kconfiglib for more information. +""".format(DEFAULT_SYNC_DEPS_PATH)) + + parser.add_argument( + "--file-list", + metavar="OUTPUT_FILE", + help=""" +Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. The +paths are relative to $srctree (or to the current directory if $srctree is +unset). Files appear in the order they're 'source'd. +""") + + parser.add_argument( + "--env-list", + metavar="OUTPUT_FILE", + help=""" +Write a list of all environment variables referenced in Kconfig files to +OUTPUT_FILE, with one variable per line. Each line has the format NAME=VALUE. +Only environment variables referenced with the preprocessor $(VAR) syntax are +included, and not variables referenced with the older $VAR syntax (which is +only supported for backwards compatibility). +""") + + parser.add_argument( + "kconfig", + metavar="KCONFIG", + nargs="?", + default="Kconfig", + help="Top-level Kconfig file (default: Kconfig)") + + args = parser.parse_args() + + + kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True) + kconf.load_config() + + if args.header_path is None: + if "KCONFIG_AUTOHEADER" in os.environ: + kconf.write_autoconf() + else: + # Kconfiglib defaults to include/generated/autoconf.h to be + # compatible with the C tools. 'config.h' is used here instead for + # backwards compatibility. It's probably a saner default for tools + # as well. + kconf.write_autoconf("config.h") + else: + kconf.write_autoconf(args.header_path) + + if args.config_out is not None: + kconf.write_config(args.config_out, save_old=False) + + if args.sync_deps is not None: + kconf.sync_deps(args.sync_deps) + + if args.file_list is not None: + with _open_write(args.file_list) as f: + for path in kconf.kconfig_filenames: + f.write(path + "\n") + + if args.env_list is not None: + with _open_write(args.env_list) as f: + for env_var in kconf.env_vars: + f.write("{}={}\n".format(env_var, os.environ[env_var])) + + +def _open_write(path): + # Python 2/3 compatibility. io.open() is available on both, but makes + # write() expect 'unicode' strings on Python 2. + + if sys.version_info[0] < 3: + return open(path, "w") + return open(path, "w", encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/atf-20250711/Kconfiglib/guiconfig.py b/atf-20250711/Kconfiglib/guiconfig.py new file mode 100755 index 000000000..7804fdc7c --- /dev/null +++ b/atf-20250711/Kconfiglib/guiconfig.py @@ -0,0 +1,2324 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Overview +======== + +A Tkinter-based menuconfig implementation, based around a treeview control and +a help display. The interface should feel familiar to people used to qconf +('make xconfig'). Compatible with both Python 2 and Python 3. + +The display can be toggled between showing the full tree and showing just a +single menu (like menuconfig.py). Only single-menu mode distinguishes between +symbols defined with 'config' and symbols defined with 'menuconfig'. + +A show-all mode is available that shows invisible items in red. + +Supports both mouse and keyboard controls. The following keyboard shortcuts are +available: + + Ctrl-S : Save configuration + Ctrl-O : Open configuration + Ctrl-A : Toggle show-all mode + Ctrl-N : Toggle show-name mode + Ctrl-M : Toggle single-menu mode + Ctrl-F, /: Open jump-to dialog + ESC : Close + +Running +======= + +guiconfig.py can be run either as a standalone executable or by calling the +menuconfig() function with an existing Kconfig instance. The second option is a +bit inflexible in that it will still load and save .config, etc. + +When run in standalone mode, the top-level Kconfig file to load can be passed +as a command-line argument. With no argument, it defaults to "Kconfig". + +The KCONFIG_CONFIG environment variable specifies the .config file to load (if +it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used. + +When overwriting a configuration file, the old version is saved to +.old (e.g. .config.old). + +$srctree is supported through Kconfiglib. +""" + +# Note: There's some code duplication with menuconfig.py below, especially for +# the help text. Maybe some of it could be moved into kconfiglib.py or a shared +# helper script, but OTOH it's pretty nice to have things standalone and +# customizable. + +import errno +import os +import sys + +_PY2 = sys.version_info[0] < 3 + +if _PY2: + # Python 2 + from Tkinter import * + import ttk + import tkFont as font + import tkFileDialog as filedialog + import tkMessageBox as messagebox +else: + # Python 3 + from tkinter import * + import tkinter.ttk as ttk + import tkinter.font as font + from tkinter import filedialog, messagebox + +from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \ + BOOL, TRISTATE, STRING, INT, HEX, \ + AND, OR, \ + expr_str, expr_value, split_expr, \ + standard_sc_expr_str, \ + TRI_TO_STR, TYPE_TO_STR, \ + standard_kconfig, standard_config_filename + + +# If True, use GIF image data embedded in this file instead of separate GIF +# files. See _load_images(). +_USE_EMBEDDED_IMAGES = True + + +# Help text for the jump-to dialog +_JUMP_TO_HELP = """\ +Type one or more strings/regexes and press Enter to list items that match all +of them. Python's regex flavor is used (see the 're' module). Double-clicking +an item will jump to it. Item values can be toggled directly within the dialog.\ +""" + + +def _main(): + menuconfig(standard_kconfig(__doc__)) + + +# Global variables used below: +# +# _root: +# The Toplevel instance for the main window +# +# _tree: +# The Treeview in the main window +# +# _jump_to_tree: +# The Treeview in the jump-to dialog. None if the jump-to dialog isn't +# open. Doubles as a flag. +# +# _jump_to_matches: +# List of Nodes shown in the jump-to dialog +# +# _menupath: +# The Label that shows the menu path of the selected item +# +# _backbutton: +# The button shown in single-menu mode for jumping to the parent menu +# +# _status_label: +# Label with status text shown at the bottom of the main window +# ("Modified", "Saved to ...", etc.) +# +# _id_to_node: +# We can't use Node objects directly as Treeview item IDs, so we use their +# id()s instead. This dictionary maps Node id()s back to Nodes. (The keys +# are actually str(id(node)), just to simplify lookups.) +# +# _cur_menu: +# The current menu. Ignored outside single-menu mode. +# +# _show_all_var/_show_name_var/_single_menu_var: +# Tkinter Variable instances bound to the corresponding checkboxes +# +# _show_all/_single_menu: +# Plain Python bools that track _show_all_var and _single_menu_var, to +# speed up and simplify things a bit +# +# _conf_filename: +# File to save the configuration to +# +# _minconf_filename: +# File to save minimal configurations to +# +# _conf_changed: +# True if the configuration has been changed. If False, we don't bother +# showing the save-and-quit dialog. +# +# We reset this to False whenever the configuration is saved. +# +# _*_img: +# PhotoImage instances for images + + +def menuconfig(kconf): + """ + Launches the configuration interface, returning after the user exits. + + kconf: + Kconfig instance to be configured + """ + global _kconf + global _conf_filename + global _minconf_filename + global _jump_to_tree + global _cur_menu + + _kconf = kconf + + _jump_to_tree = None + + _create_id_to_node() + + _create_ui() + + # Filename to save configuration to + _conf_filename = standard_config_filename() + + # Load existing configuration and check if it's outdated + _set_conf_changed(_load_config()) + + # Filename to save minimal configuration to + _minconf_filename = "defconfig" + + # Current menu in single-menu mode + _cur_menu = _kconf.top_node + + # Any visible items in the top menu? + if not _shown_menu_nodes(kconf.top_node): + # Nothing visible. Start in show-all mode and try again. + _show_all_var.set(True) + if not _shown_menu_nodes(kconf.top_node): + # Give up and show an error. It's nice to be able to assume that + # the tree is non-empty in the rest of the code. + _root.wait_visibility() + messagebox.showerror( + "Error", + "Empty configuration -- nothing to configure.\n\n" + "Check that environment variables are set properly.") + _root.destroy() + return + + # Build the initial tree + _update_tree() + + # Select the first item and focus the Treeview, so that keyboard controls + # work immediately + _select(_tree, _tree.get_children()[0]) + _tree.focus_set() + + # Make geometry information available for centering the window. This + # indirectly creates the window, so hide it so that it's never shown at the + # old location. + _root.withdraw() + _root.update_idletasks() + + # Center the window + _root.geometry("+{}+{}".format( + (_root.winfo_screenwidth() - _root.winfo_reqwidth())//2, + (_root.winfo_screenheight() - _root.winfo_reqheight())//2)) + + # Show it + _root.deiconify() + + # Prevent the window from being automatically resized. Otherwise, it + # changes size when scrollbars appear/disappear before the user has + # manually resized it. + _root.geometry(_root.geometry()) + + _root.mainloop() + + +def _load_config(): + # Loads any existing .config file. See the Kconfig.load_config() docstring. + # + # Returns True if .config is missing or outdated. We always prompt for + # saving the configuration in that case. + + print(_kconf.load_config()) + if not os.path.exists(_conf_filename): + # No .config + return True + + return _needs_save() + + +def _needs_save(): + # Returns True if a just-loaded .config file is outdated (would get + # modified when saving) + + if _kconf.missing_syms: + # Assignments to undefined symbols in the .config + return True + + for sym in _kconf.unique_defined_syms: + if sym.user_value is None: + if sym.config_string: + # Unwritten symbol + return True + elif sym.orig_type in (BOOL, TRISTATE): + if sym.tri_value != sym.user_value: + # Written bool/tristate symbol, new value + return True + elif sym.str_value != sym.user_value: + # Written string/int/hex symbol, new value + return True + + # No need to prompt for save + return False + + +def _create_id_to_node(): + global _id_to_node + + _id_to_node = {str(id(node)): node for node in _kconf.node_iter()} + + +def _create_ui(): + # Creates the main window UI + + global _root + global _tree + + # Create the root window. This initializes Tkinter and makes e.g. + # PhotoImage available, so do it early. + _root = Tk() + + _load_images() + _init_misc_ui() + _fix_treeview_issues() + + _create_top_widgets() + # Create the pane with the Kconfig tree and description text + panedwindow, _tree = _create_kconfig_tree_and_desc(_root) + panedwindow.grid(column=0, row=1, sticky="nsew") + _create_status_bar() + + _root.columnconfigure(0, weight=1) + # Only the pane with the Kconfig tree and description grows vertically + _root.rowconfigure(1, weight=1) + + # Start with show-name disabled + _do_showname() + + _tree.bind("", _tree_left_key) + _tree.bind("", _tree_right_key) + # Note: Binding this for the jump-to tree as well would cause issues due to + # the Tk bug mentioned in _tree_open() + _tree.bind("<>", _tree_open) + # add=True to avoid overriding the description text update + _tree.bind("<>", _update_menu_path, add=True) + + _root.bind("", _save) + _root.bind("", _open) + _root.bind("", _toggle_showall) + _root.bind("", _toggle_showname) + _root.bind("", _toggle_tree_mode) + _root.bind("", _jump_to_dialog) + _root.bind("/", _jump_to_dialog) + _root.bind("", _on_quit) + + +def _load_images(): + # Loads GIF images, creating the global _*_img PhotoImage variables. + # Base64-encoded images embedded in this script are used if + # _USE_EMBEDDED_IMAGES is True, and separate image files in the same + # directory as the script otherwise. + # + # Using a global variable indirectly prevents the image from being + # garbage-collected. Passing an image to a Tkinter function isn't enough to + # keep it alive. + + def load_image(name, data): + var_name = "_{}_img".format(name) + + if _USE_EMBEDDED_IMAGES: + globals()[var_name] = PhotoImage(data=data, format="gif") + else: + globals()[var_name] = PhotoImage( + file=os.path.join(os.path.dirname(__file__), name + ".gif"), + format="gif") + + # Note: Base64 data can be put on the clipboard with + # $ base64 -w0 foo.gif | xclip + + load_image("icon", "R0lGODlhMAAwAPEDAAAAAADQAO7u7v///yH5BAUKAAMALAAAAAAwADAAAAL/nI+gy+2Pokyv2jazuZxryQjiSJZmyXxHeLbumH6sEATvW8OLNtf5bfLZRLFITzgEipDJ4mYxYv6A0ubuqYhWk66tVTE4enHer7jcKvt0LLUw6P45lvEprT6c0+v7OBuqhYdHohcoqIbSAHc4ljhDwrh1UlgSydRCWWlp5wiYZvmSuSh4IzrqV6p4cwhkCsmY+nhK6uJ6t1mrOhuJqfu6+WYiCiwl7HtLjNSZZZis/MeM7NY3TaRKS40ooDeoiVqIultsrav92bi9c3a5KkkOsOJZpSS99m4k/0zPng4Gks9JSbB+8DIcoQfnjwpZCHv5W+ip4aQrKrB0uOikYhiMCBw1/uPoQUMBADs=") + load_image("n_bool", "R0lGODdhEAAQAPAAAAgICP///ywAAAAAEAAQAAACIISPacHtvp5kcb5qG85hZ2+BkyiRF8BBaEqtrKkqslEAADs=") + load_image("y_bool", "R0lGODdhEAAQAPEAAAgICADQAP///wAAACwAAAAAEAAQAAACMoSPacLtvlh4YrIYsst2cV19AvaVF9CUXBNJJoum7ymrsKuCnhiupIWjSSjAFuWhSCIKADs=") + load_image("n_tri", "R0lGODlhEAAQAPD/AAEBAf///yH5BAUKAAIALAAAAAAQABAAAAInlI+pBrAKQnCPSUlXvFhznlkfeGwjKZhnJ65h6nrfi6h0st2QXikFADs=") + load_image("m_tri", "R0lGODlhEAAQAPEDAAEBAeQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nI+pBrAWAhPCjYhiAJQCnWmdoElHGVBoiK5M21ofXFpXRIrgiecqxkuNciZIhNOZFRNI24PhfEoLADs=") + load_image("y_tri", "R0lGODlhEAAQAPEDAAICAgDQAP///wAAACH5BAUKAAMALAAAAAAQABAAAAI0nI+pBrAYBhDCRRUypfmergmgZ4xjMpmaw2zmxk7cCB+pWiVqp4MzDwn9FhGZ5WFjIZeGAgA7") + load_image("m_my", "R0lGODlhEAAQAPEDAAAAAOQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nIGpxiAPI2ghxFinq/ZygQhc94zgZopmOLYf67anGr+oZdp02emfV5n9MEHN5QhqICETxkABbQ4KADs=") + load_image("y_my", "R0lGODlhEAAQAPH/AAAAAADQAAPRA////yH5BAUKAAQALAAAAAAQABAAAAM+SArcrhCMSSuIM9Q8rxxBWIXawIBkmWonupLd565Um9G1PIs59fKmzw8WnAlusBYR2SEIN6DmAmqBLBxYSAIAOw==") + load_image("n_locked", "R0lGODlhEAAQAPABAAAAAP///yH5BAUKAAEALAAAAAAQABAAAAIgjB8AyKwN04pu0vMutpqqz4Hih4ydlnUpyl2r23pxUAAAOw==") + load_image("m_locked", "R0lGODlhEAAQAPD/AAAAAOQMuiH5BAUKAAIALAAAAAAQABAAAAIylC8AyKwN04ohnGcqqlZmfXDWI26iInZoyiore05walolV39ftxsYHgL9QBBMBGFEFAAAOw==") + load_image("y_locked", "R0lGODlhEAAQAPD/AAAAAADQACH5BAUKAAIALAAAAAAQABAAAAIylC8AyKzNgnlCtoDTwvZwrHydIYpQmR3KWq4uK74IOnp0HQPmnD3cOVlUIAgKsShkFAAAOw==") + load_image("not_selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIrlA2px6IBw2IpWglOvTYhzmUbGD3kNZ5QqrKn2YrqigCxZoMelU6No9gdCgA7") + load_image("selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIzlA2px6IBw2IpWglOvTah/kTZhimASJomiqonlLov1qptHTsgKSEzh9H8QI0QzNPwmRoFADs=") + load_image("edit", "R0lGODlhEAAQAPIFAAAAAKOLAMuuEPvXCvrxvgAAAAAAAAAAACH5BAUKAAUALAAAAAAQABAAAANCWLqw/gqMBp8cszJxcwVC2FEOEIAi5kVBi3IqWZhuCGMyfdpj2e4pnK+WAshmvxeAcETWlsxPkkBtsqBMa8TIBSQAADs=") + + +def _fix_treeview_issues(): + # Fixes some Treeview issues + + global _treeview_rowheight + + style = ttk.Style() + + # The treeview rowheight isn't adjusted automatically on high-DPI displays, + # so do it ourselves. The font will probably always be TkDefaultFont, but + # play it safe and look it up. + + _treeview_rowheight = font.Font(font=style.lookup("Treeview", "font")) \ + .metrics("linespace") + 2 + + style.configure("Treeview", rowheight=_treeview_rowheight) + + # Work around regression in https://core.tcl.tk/tk/tktview?name=509cafafae, + # which breaks tag background colors + + for option in "foreground", "background": + # Filter out any styles starting with ("!disabled", "!selected", ...). + # style.map() returns an empty list for missing options, so this should + # be future-safe. + style.map( + "Treeview", + **{option: [elm for elm in style.map("Treeview", query_opt=option) + if elm[:2] != ("!disabled", "!selected")]}) + + +def _init_misc_ui(): + # Does misc. UI initialization, like setting the title, icon, and theme + + _root.title(_kconf.mainmenu_text) + # iconphoto() isn't available in Python 2's Tkinter + _root.tk.call("wm", "iconphoto", _root._w, "-default", _icon_img) + # Reducing the width of the window to 1 pixel makes it move around, at + # least on GNOME. Prevent weird stuff like that. + _root.minsize(128, 128) + _root.protocol("WM_DELETE_WINDOW", _on_quit) + + # Use the 'clam' theme on *nix if it's available. It looks nicer than the + # 'default' theme. + if _root.tk.call("tk", "windowingsystem") == "x11": + style = ttk.Style() + if "clam" in style.theme_names(): + style.theme_use("clam") + + +def _create_top_widgets(): + # Creates the controls above the Kconfig tree in the main window + + global _show_all_var + global _show_name_var + global _single_menu_var + global _menupath + global _backbutton + + topframe = ttk.Frame(_root) + topframe.grid(column=0, row=0, sticky="ew") + + ttk.Button(topframe, text="Save", command=_save) \ + .grid(column=0, row=0, sticky="ew", padx=".05c", pady=".05c") + + ttk.Button(topframe, text="Save as...", command=_save_as) \ + .grid(column=1, row=0, sticky="ew") + + ttk.Button(topframe, text="Save minimal (advanced)...", + command=_save_minimal) \ + .grid(column=2, row=0, sticky="ew", padx=".05c") + + ttk.Button(topframe, text="Open...", command=_open) \ + .grid(column=3, row=0) + + ttk.Button(topframe, text="Jump to...", command=_jump_to_dialog) \ + .grid(column=4, row=0, padx=".05c") + + _show_name_var = BooleanVar() + ttk.Checkbutton(topframe, text="Show name", command=_do_showname, + variable=_show_name_var) \ + .grid(column=0, row=1, sticky="nsew", padx=".05c", pady="0 .05c", + ipady=".2c") + + _show_all_var = BooleanVar() + ttk.Checkbutton(topframe, text="Show all", command=_do_showall, + variable=_show_all_var) \ + .grid(column=1, row=1, sticky="nsew", pady="0 .05c") + + # Allow the show-all and single-menu status to be queried via plain global + # Python variables, which is faster and simpler + + def show_all_updated(*_): + global _show_all + _show_all = _show_all_var.get() + + _trace_write(_show_all_var, show_all_updated) + _show_all_var.set(False) + + _single_menu_var = BooleanVar() + ttk.Checkbutton(topframe, text="Single-menu mode", command=_do_tree_mode, + variable=_single_menu_var) \ + .grid(column=2, row=1, sticky="nsew", padx=".05c", pady="0 .05c") + + _backbutton = ttk.Button(topframe, text="<--", command=_leave_menu, + state="disabled") + _backbutton.grid(column=0, row=4, sticky="nsew", padx=".05c", pady="0 .05c") + + def tree_mode_updated(*_): + global _single_menu + _single_menu = _single_menu_var.get() + + if _single_menu: + _backbutton.grid() + else: + _backbutton.grid_remove() + + _trace_write(_single_menu_var, tree_mode_updated) + _single_menu_var.set(False) + + # Column to the right of the buttons that the menu path extends into, so + # that it can grow wider than the buttons + topframe.columnconfigure(5, weight=1) + + _menupath = ttk.Label(topframe) + _menupath.grid(column=0, row=3, columnspan=6, sticky="w", padx="0.05c", + pady="0 .05c") + + +def _create_kconfig_tree_and_desc(parent): + # Creates a Panedwindow with a Treeview that shows Kconfig nodes and a Text + # that shows a description of the selected node. Returns a tuple with the + # Panedwindow and the Treeview. This code is shared between the main window + # and the jump-to dialog. + + panedwindow = ttk.Panedwindow(parent, orient=VERTICAL) + + tree_frame, tree = _create_kconfig_tree(panedwindow) + desc_frame, desc = _create_kconfig_desc(panedwindow) + + panedwindow.add(tree_frame, weight=1) + panedwindow.add(desc_frame) + + def tree_select(_): + # The Text widget does not allow editing the text in its disabled + # state. We need to temporarily enable it. + desc["state"] = "normal" + + sel = tree.selection() + if not sel: + desc.delete("1.0", "end") + desc["state"] = "disabled" + return + + # Text.replace() is not available in Python 2's Tkinter + desc.delete("1.0", "end") + desc.insert("end", _info_str(_id_to_node[sel[0]])) + + desc["state"] = "disabled" + + tree.bind("<>", tree_select) + tree.bind("<1>", _tree_click) + tree.bind("", _tree_double_click) + tree.bind("", _tree_enter) + tree.bind("", _tree_enter) + tree.bind("", _tree_toggle) + tree.bind("n", _tree_set_val(0)) + tree.bind("m", _tree_set_val(1)) + tree.bind("y", _tree_set_val(2)) + + return panedwindow, tree + + +def _create_kconfig_tree(parent): + # Creates a Treeview for showing Kconfig nodes + + frame = ttk.Frame(parent) + + tree = ttk.Treeview(frame, selectmode="browse", height=20, + columns=("name",)) + tree.heading("#0", text="Option", anchor="w") + tree.heading("name", text="Name", anchor="w") + + tree.tag_configure("n-bool", image=_n_bool_img) + tree.tag_configure("y-bool", image=_y_bool_img) + tree.tag_configure("m-tri", image=_m_tri_img) + tree.tag_configure("n-tri", image=_n_tri_img) + tree.tag_configure("m-tri", image=_m_tri_img) + tree.tag_configure("y-tri", image=_y_tri_img) + tree.tag_configure("m-my", image=_m_my_img) + tree.tag_configure("y-my", image=_y_my_img) + tree.tag_configure("n-locked", image=_n_locked_img) + tree.tag_configure("m-locked", image=_m_locked_img) + tree.tag_configure("y-locked", image=_y_locked_img) + tree.tag_configure("not-selected", image=_not_selected_img) + tree.tag_configure("selected", image=_selected_img) + tree.tag_configure("edit", image=_edit_img) + tree.tag_configure("invisible", foreground="red") + + tree.grid(column=0, row=0, sticky="nsew") + + _add_vscrollbar(frame, tree) + + frame.columnconfigure(0, weight=1) + frame.rowconfigure(0, weight=1) + + # Create items for all menu nodes. These can be detached/moved later. + # Micro-optimize this a bit. + insert = tree.insert + id_ = id + Symbol_ = Symbol + for node in _kconf.node_iter(): + item = node.item + insert("", "end", iid=id_(node), + values=item.name if item.__class__ is Symbol_ else "") + + return frame, tree + + +def _create_kconfig_desc(parent): + # Creates a Text for showing the description of the selected Kconfig node + + frame = ttk.Frame(parent) + + desc = Text(frame, height=12, wrap="none", borderwidth=0, + state="disabled") + desc.grid(column=0, row=0, sticky="nsew") + + # Work around not being to Ctrl-C/V text from a disabled Text widget, with a + # tip found in https://stackoverflow.com/questions/3842155/is-there-a-way-to-make-the-tkinter-text-widget-read-only + desc.bind("<1>", lambda _: desc.focus_set()) + + _add_vscrollbar(frame, desc) + + frame.columnconfigure(0, weight=1) + frame.rowconfigure(0, weight=1) + + return frame, desc + + +def _add_vscrollbar(parent, widget): + # Adds a vertical scrollbar to 'widget' that's only shown as needed + + vscrollbar = ttk.Scrollbar(parent, orient="vertical", + command=widget.yview) + vscrollbar.grid(column=1, row=0, sticky="ns") + + def yscrollcommand(first, last): + # Only show the scrollbar when needed. 'first' and 'last' are + # strings. + if float(first) <= 0.0 and float(last) >= 1.0: + vscrollbar.grid_remove() + else: + vscrollbar.grid() + + vscrollbar.set(first, last) + + widget["yscrollcommand"] = yscrollcommand + + +def _create_status_bar(): + # Creates the status bar at the bottom of the main window + + global _status_label + + _status_label = ttk.Label(_root, anchor="e", padding="0 0 0.4c 0") + _status_label.grid(column=0, row=3, sticky="ew") + + +def _set_status(s): + # Sets the text in the status bar to 's' + + _status_label["text"] = s + + +def _set_conf_changed(changed): + # Updates the status re. whether there are unsaved changes + + global _conf_changed + + _conf_changed = changed + if changed: + _set_status("Modified") + + +def _update_tree(): + # Updates the Kconfig tree in the main window by first detaching all nodes + # and then updating and reattaching them. The tree structure might have + # changed. + + # If a selected/focused item is detached and later reattached, it stays + # selected/focused. That can give multiple selections even though + # selectmode=browse. Save and later restore the selection and focus as a + # workaround. + old_selection = _tree.selection() + old_focus = _tree.focus() + + # Detach all tree items before re-stringing them. This is relatively fast, + # luckily. + _tree.detach(*_id_to_node.keys()) + + if _single_menu: + _build_menu_tree() + else: + _build_full_tree(_kconf.top_node) + + _tree.selection_set(old_selection) + _tree.focus(old_focus) + + +def _build_full_tree(menu): + # Updates the tree starting from menu.list, in full-tree mode. To speed + # things up, only open menus are updated. The menu-at-a-time logic here is + # to deal with invisible items that can show up outside show-all mode (see + # _shown_full_nodes()). + + for node in _shown_full_nodes(menu): + _add_to_tree(node, _kconf.top_node) + + # _shown_full_nodes() includes nodes from menus rooted at symbols, so + # we only need to check "real" menus/choices here + if node.list and not isinstance(node.item, Symbol): + if _tree.item(id(node), "open"): + _build_full_tree(node) + else: + # We're just probing here, so _shown_menu_nodes() will work + # fine, and might be a bit faster + shown = _shown_menu_nodes(node) + if shown: + # Dummy element to make the open/closed toggle appear + _tree.move(id(shown[0]), id(shown[0].parent), "end") + + +def _shown_full_nodes(menu): + # Returns the list of menu nodes shown in 'menu' (a menu node for a menu) + # for full-tree mode. A tricky detail is that invisible items need to be + # shown if they have visible children. + + def rec(node): + res = [] + + while node: + if _visible(node) or _show_all: + res.append(node) + if node.list and isinstance(node.item, Symbol): + # Nodes from menu created from dependencies + res += rec(node.list) + + elif node.list and isinstance(node.item, Symbol): + # Show invisible symbols (defined with either 'config' and + # 'menuconfig') if they have visible children. This can happen + # for an m/y-valued symbol with an optional prompt + # ('prompt "foo" is COND') that is currently disabled. + shown_children = rec(node.list) + if shown_children: + res.append(node) + res += shown_children + + node = node.next + + return res + + return rec(menu.list) + + +def _build_menu_tree(): + # Updates the tree in single-menu mode. See _build_full_tree() as well. + + for node in _shown_menu_nodes(_cur_menu): + _add_to_tree(node, _cur_menu) + + +def _shown_menu_nodes(menu): + # Used for single-menu mode. Similar to _shown_full_nodes(), but doesn't + # include children of symbols defined with 'menuconfig'. + + def rec(node): + res = [] + + while node: + if _visible(node) or _show_all: + res.append(node) + if node.list and not node.is_menuconfig: + res += rec(node.list) + + elif node.list and isinstance(node.item, Symbol): + shown_children = rec(node.list) + if shown_children: + # Invisible item with visible children + res.append(node) + if not node.is_menuconfig: + res += shown_children + + node = node.next + + return res + + return rec(menu.list) + + +def _visible(node): + # Returns True if the node should appear in the menu (outside show-all + # mode) + + return node.prompt and expr_value(node.prompt[1]) and not \ + (node.item == MENU and not expr_value(node.visibility)) + + +def _add_to_tree(node, top): + # Adds 'node' to the tree, at the end of its menu. We rely on going through + # the nodes linearly to get the correct order. 'top' holds the menu that + # corresponds to the top-level menu, and can vary in single-menu mode. + + parent = node.parent + _tree.move(id(node), "" if parent is top else id(parent), "end") + _tree.item( + id(node), + text=_node_str(node), + # The _show_all test avoids showing invisible items in red outside + # show-all mode, which could look confusing/broken. Invisible symbols + # are shown outside show-all mode if an invisible symbol has visible + # children in an implicit menu. + tags=_img_tag(node) if _visible(node) or not _show_all else + _img_tag(node) + " invisible") + + +def _node_str(node): + # Returns the string shown to the right of the image (if any) for the node + + if node.prompt: + if node.item == COMMENT: + s = "*** {} ***".format(node.prompt[0]) + else: + s = node.prompt[0] + + if isinstance(node.item, Symbol): + sym = node.item + + # Print "(NEW)" next to symbols without a user value (from e.g. a + # .config), but skip it for choice symbols in choices in y mode, + # and for symbols of UNKNOWN type (which generate a warning though) + if sym.user_value is None and sym.type and not \ + (sym.choice and sym.choice.tri_value == 2): + + s += " (NEW)" + + elif isinstance(node.item, Symbol): + # Symbol without prompt (can show up in show-all) + s = "<{}>".format(node.item.name) + + else: + # Choice without prompt. Use standard_sc_expr_str() so that it shows up + # as ''. + s = standard_sc_expr_str(node.item) + + + if isinstance(node.item, Symbol): + sym = node.item + if sym.orig_type == STRING: + s += ": " + sym.str_value + elif sym.orig_type in (INT, HEX): + s = "({}) {}".format(sym.str_value, s) + + elif isinstance(node.item, Choice) and node.item.tri_value == 2: + # Print the prompt of the selected symbol after the choice for + # choices in y mode + sym = node.item.selection + if sym: + for sym_node in sym.nodes: + # Use the prompt used at this choice location, in case the + # choice symbol is defined in multiple locations + if sym_node.parent is node and sym_node.prompt: + s += " ({})".format(sym_node.prompt[0]) + break + else: + # If the symbol isn't defined at this choice location, then + # just use whatever prompt we can find for it + for sym_node in sym.nodes: + if sym_node.prompt: + s += " ({})".format(sym_node.prompt[0]) + break + + # In single-menu mode, print "--->" next to nodes that have menus that can + # potentially be entered. Print "----" if the menu is empty. We don't allow + # those to be entered. + if _single_menu and node.is_menuconfig: + s += " --->" if _shown_menu_nodes(node) else " ----" + + return s + + +def _img_tag(node): + # Returns the tag for the image that should be shown next to 'node', or the + # empty string if it shouldn't have an image + + item = node.item + + if item in (MENU, COMMENT) or not item.orig_type: + return "" + + if item.orig_type in (STRING, INT, HEX): + return "edit" + + # BOOL or TRISTATE + + if _is_y_mode_choice_sym(item): + # Choice symbol in y-mode choice + return "selected" if item.choice.selection is item else "not-selected" + + if len(item.assignable) <= 1: + # Pinned to a single value + return "" if isinstance(item, Choice) else item.str_value + "-locked" + + if item.type == BOOL: + return item.str_value + "-bool" + + # item.type == TRISTATE + if item.assignable == (1, 2): + return item.str_value + "-my" + return item.str_value + "-tri" + + +def _is_y_mode_choice_sym(item): + # The choice mode is an upper bound on the visibility of choice symbols, so + # we can check the choice symbols' own visibility to see if the choice is + # in y mode + return isinstance(item, Symbol) and item.choice and item.visibility == 2 + + +def _tree_click(event): + # Click on the Kconfig Treeview + + tree = event.widget + if tree.identify_element(event.x, event.y) == "image": + item = tree.identify_row(event.y) + # Select the item before possibly popping up a dialog for + # string/int/hex items, so that its help is visible + _select(tree, item) + _change_node(_id_to_node[item], tree.winfo_toplevel()) + return "break" + + +def _tree_double_click(event): + # Double-click on the Kconfig treeview + + # Do an extra check to avoid weirdness when double-clicking in the tree + # heading area + if not _in_heading(event): + return _tree_enter(event) + + +def _in_heading(event): + # Returns True if 'event' took place in the tree heading + + tree = event.widget + return hasattr(tree, "identify_region") and \ + tree.identify_region(event.x, event.y) in ("heading", "separator") + + +def _tree_enter(event): + # Enter press or double-click within the Kconfig treeview. Prefer to + # open/close/enter menus, but toggle the value if that's not possible. + + tree = event.widget + sel = tree.focus() + if sel: + node = _id_to_node[sel] + + if tree.get_children(sel): + _tree_toggle_open(sel) + elif _single_menu_mode_menu(node, tree): + _enter_menu_and_select_first(node) + else: + _change_node(node, tree.winfo_toplevel()) + + return "break" + + +def _tree_toggle(event): + # Space press within the Kconfig treeview. Prefer to toggle the value, but + # open/close/enter the menu if that's not possible. + + tree = event.widget + sel = tree.focus() + if sel: + node = _id_to_node[sel] + + if _changeable(node): + _change_node(node, tree.winfo_toplevel()) + elif _single_menu_mode_menu(node, tree): + _enter_menu_and_select_first(node) + elif tree.get_children(sel): + _tree_toggle_open(sel) + + return "break" + + +def _tree_left_key(_): + # Left arrow key press within the Kconfig treeview + + if _single_menu: + # Leave the current menu in single-menu mode + _leave_menu() + return "break" + + # Otherwise, default action + + +def _tree_right_key(_): + # Right arrow key press within the Kconfig treeview + + sel = _tree.focus() + if sel: + node = _id_to_node[sel] + # If the node can be entered in single-menu mode, do it + if _single_menu_mode_menu(node, _tree): + _enter_menu_and_select_first(node) + return "break" + + # Otherwise, default action + + +def _single_menu_mode_menu(node, tree): + # Returns True if single-menu mode is on and 'node' is an (interface) + # menu that can be entered + + return _single_menu and tree is _tree and node.is_menuconfig and \ + _shown_menu_nodes(node) + + +def _changeable(node): + # Returns True if 'node' is a Symbol/Choice whose value can be changed + + sc = node.item + + if not isinstance(sc, (Symbol, Choice)): + return False + + # This will hit for invisible symbols, which appear in show-all mode and + # when an invisible symbol has visible children (which can happen e.g. for + # symbols with optional prompts) + if not (node.prompt and expr_value(node.prompt[1])): + return False + + return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \ + or _is_y_mode_choice_sym(sc) + + +def _tree_toggle_open(item): + # Opens/closes the Treeview item 'item' + + if _tree.item(item, "open"): + _tree.item(item, open=False) + else: + node = _id_to_node[item] + if not isinstance(node.item, Symbol): + # Can only get here in full-tree mode + _build_full_tree(node) + _tree.item(item, open=True) + + +def _tree_set_val(tri_val): + def tree_set_val(event): + # n/m/y press within the Kconfig treeview + + # Sets the value of the currently selected item to 'tri_val', if that + # value can be assigned + + sel = event.widget.focus() + if sel: + sc = _id_to_node[sel].item + if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable: + _set_val(sc, tri_val) + + return tree_set_val + + +def _tree_open(_): + # Lazily populates the Kconfig tree when menus are opened in full-tree mode + + if _single_menu: + # Work around https://core.tcl.tk/tk/tktview?name=368fa4561e + # ("ttk::treeview open/closed indicators can be toggled while hidden"). + # Clicking on the hidden indicator will call _build_full_tree() in + # single-menu mode otherwise. + return + + node = _id_to_node[_tree.focus()] + # _shown_full_nodes() includes nodes from menus rooted at symbols, so we + # only need to check "real" menus and choices here + if not isinstance(node.item, Symbol): + _build_full_tree(node) + + +def _update_menu_path(_): + # Updates the displayed menu path when nodes are selected in the Kconfig + # treeview + + sel = _tree.selection() + _menupath["text"] = _menu_path_info(_id_to_node[sel[0]]) if sel else "" + + +def _item_row(item): + # Returns the row number 'item' appears on within the Kconfig treeview, + # starting from the top of the tree. Used to preserve scrolling. + # + # ttkTreeview.c in the Tk sources defines a RowNumber() function that does + # the same thing, but it's not exposed. + + row = 0 + + while True: + prev = _tree.prev(item) + if prev: + item = prev + row += _n_rows(item) + else: + item = _tree.parent(item) + if not item: + return row + row += 1 + + +def _n_rows(item): + # _item_row() helper. Returns the number of rows occupied by 'item' and # + # its children. + + rows = 1 + + if _tree.item(item, "open"): + for child in _tree.get_children(item): + rows += _n_rows(child) + + return rows + + +def _attached(item): + # Heuristic for checking if a Treeview item is attached. Doesn't seem to be + # good APIs for this. Might fail for super-obscure cases with tiny trees, + # but you'd just get a small scroll mess-up. + + return bool(_tree.next(item) or _tree.prev(item) or _tree.parent(item)) + + +def _change_node(node, parent): + # Toggles/changes the value of 'node'. 'parent' is the parent window + # (either the main window or the jump-to dialog), in case we need to pop up + # a dialog. + + if not _changeable(node): + return + + # sc = symbol/choice + sc = node.item + + if sc.type in (INT, HEX, STRING): + s = _set_val_dialog(node, parent) + + # Tkinter can return 'unicode' strings on Python 2, which Kconfiglib + # can't deal with. UTF-8-encode the string to work around it. + if _PY2 and isinstance(s, unicode): + s = s.encode("utf-8", "ignore") + + if s is not None: + _set_val(sc, s) + + elif len(sc.assignable) == 1: + # Handles choice symbols for choices in y mode, which are a special + # case: .assignable can be (2,) while .tri_value is 0. + _set_val(sc, sc.assignable[0]) + + else: + # Set the symbol to the value after the current value in + # sc.assignable, with wrapping + val_index = sc.assignable.index(sc.tri_value) + _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)]) + + +def _set_val(sc, val): + # Wrapper around Symbol/Choice.set_value() for updating the menu state and + # _conf_changed + + # Use the string representation of tristate values. This makes the format + # consistent for all symbol types. + if val in TRI_TO_STR: + val = TRI_TO_STR[val] + + if val != sc.str_value: + sc.set_value(val) + _set_conf_changed(True) + + # Update the tree and try to preserve the scroll. Do a cheaper variant + # than in the show-all case, that might mess up the scroll slightly in + # rare cases, but is fast and flicker-free. + + stayput = _loc_ref_item() # Item to preserve scroll for + old_row = _item_row(stayput) + + _update_tree() + + # If the reference item disappeared (can happen if the change was done + # from the jump-to dialog), then avoid messing with the scroll and hope + # for the best + if _attached(stayput): + _tree.yview_scroll(_item_row(stayput) - old_row, "units") + + if _jump_to_tree: + _update_jump_to_display() + + +def _set_val_dialog(node, parent): + # Pops up a dialog for setting the value of the string/int/hex + # symbol at node 'node'. 'parent' is the parent window. + + def ok(_=None): + # No 'nonlocal' in Python 2 + global _entry_res + + s = entry.get() + if sym.type == HEX and not s.startswith(("0x", "0X")): + s = "0x" + s + + if _check_valid(dialog, entry, sym, s): + _entry_res = s + dialog.destroy() + + def cancel(_=None): + global _entry_res + _entry_res = None + dialog.destroy() + + sym = node.item + + dialog = Toplevel(parent) + dialog.title("Enter {} value".format(TYPE_TO_STR[sym.type])) + dialog.resizable(False, False) + dialog.transient(parent) + dialog.protocol("WM_DELETE_WINDOW", cancel) + + ttk.Label(dialog, text=node.prompt[0] + ":") \ + .grid(column=0, row=0, columnspan=2, sticky="w", padx=".3c", + pady=".2c .05c") + + entry = ttk.Entry(dialog, width=30) + # Start with the previous value in the editbox, selected + entry.insert(0, sym.str_value) + entry.selection_range(0, "end") + entry.grid(column=0, row=1, columnspan=2, sticky="ew", padx=".3c") + entry.focus_set() + + range_info = _range_info(sym) + if range_info: + ttk.Label(dialog, text=range_info) \ + .grid(column=0, row=2, columnspan=2, sticky="w", padx=".3c", + pady=".2c 0") + + ttk.Button(dialog, text="OK", command=ok) \ + .grid(column=0, row=4 if range_info else 3, sticky="e", padx=".3c", + pady=".4c") + + ttk.Button(dialog, text="Cancel", command=cancel) \ + .grid(column=1, row=4 if range_info else 3, padx="0 .3c") + + # Give all horizontal space to the grid cell with the OK button, so that + # Cancel moves to the right + dialog.columnconfigure(0, weight=1) + + _center_on_root(dialog) + + # Hack to scroll the entry so that the end of the text is shown, from + # https://stackoverflow.com/questions/29334544/why-does-tkinters-entry-xview-moveto-fail. + # Related Tk ticket: https://core.tcl.tk/tk/info/2513186fff + def scroll_entry(_): + _root.update_idletasks() + entry.unbind("") + entry.xview_moveto(1) + entry.bind("", scroll_entry) + + # The dialog must be visible before we can grab the input + dialog.wait_visibility() + dialog.grab_set() + + dialog.bind("", ok) + dialog.bind("", ok) + dialog.bind("", cancel) + + # Wait for the user to be done with the dialog + parent.wait_window(dialog) + + # Regrab the input in the parent + parent.grab_set() + + return _entry_res + + +def _center_on_root(dialog): + # Centers 'dialog' on the root window. It often ends up at some bad place + # like the top-left corner of the screen otherwise. See the menuconfig() + # function, which has similar logic. + + dialog.withdraw() + _root.update_idletasks() + + dialog_width = dialog.winfo_reqwidth() + dialog_height = dialog.winfo_reqheight() + + screen_width = _root.winfo_screenwidth() + screen_height = _root.winfo_screenheight() + + x = _root.winfo_rootx() + (_root.winfo_width() - dialog_width)//2 + y = _root.winfo_rooty() + (_root.winfo_height() - dialog_height)//2 + + # Clamp so that no part of the dialog is outside the screen + if x + dialog_width > screen_width: + x = screen_width - dialog_width + elif x < 0: + x = 0 + if y + dialog_height > screen_height: + y = screen_height - dialog_height + elif y < 0: + y = 0 + + dialog.geometry("+{}+{}".format(x, y)) + + dialog.deiconify() + + +def _check_valid(dialog, entry, sym, s): + # Returns True if the string 's' is a well-formed value for 'sym'. + # Otherwise, pops up an error and returns False. + + if sym.type not in (INT, HEX): + # Anything goes for non-int/hex symbols + return True + + base = 10 if sym.type == INT else 16 + try: + int(s, base) + except ValueError: + messagebox.showerror( + "Bad value", + "'{}' is a malformed {} value".format( + s, TYPE_TO_STR[sym.type]), + parent=dialog) + entry.focus_set() + return False + + for low_sym, high_sym, cond in sym.ranges: + if expr_value(cond): + low_s = low_sym.str_value + high_s = high_sym.str_value + + if not int(low_s, base) <= int(s, base) <= int(high_s, base): + messagebox.showerror( + "Value out of range", + "{} is outside the range {}-{}".format(s, low_s, high_s), + parent=dialog) + entry.focus_set() + return False + + break + + return True + + +def _range_info(sym): + # Returns a string with information about the valid range for the symbol + # 'sym', or None if 'sym' doesn't have a range + + if sym.type in (INT, HEX): + for low, high, cond in sym.ranges: + if expr_value(cond): + return "Range: {}-{}".format(low.str_value, high.str_value) + + return None + + +def _save(_=None): + # Tries to save the configuration + + if _try_save(_kconf.write_config, _conf_filename, "configuration"): + _set_conf_changed(False) + + _tree.focus_set() + + +def _save_as(): + # Pops up a dialog for saving the configuration to a specific location + + global _conf_filename + + filename = _conf_filename + while True: + filename = filedialog.asksaveasfilename( + title="Save configuration as", + initialdir=os.path.dirname(filename), + initialfile=os.path.basename(filename), + parent=_root) + + if not filename: + break + + if _try_save(_kconf.write_config, filename, "configuration"): + _conf_filename = filename + break + + _tree.focus_set() + + +def _save_minimal(): + # Pops up a dialog for saving a minimal configuration (defconfig) to a + # specific location + + global _minconf_filename + + filename = _minconf_filename + while True: + filename = filedialog.asksaveasfilename( + title="Save minimal configuration as", + initialdir=os.path.dirname(filename), + initialfile=os.path.basename(filename), + parent=_root) + + if not filename: + break + + if _try_save(_kconf.write_min_config, filename, + "minimal configuration"): + + _minconf_filename = filename + break + + _tree.focus_set() + + +def _open(_=None): + # Pops up a dialog for loading a configuration + + global _conf_filename + + if _conf_changed and \ + not messagebox.askokcancel( + "Unsaved changes", + "You have unsaved changes. Load new configuration anyway?"): + + return + + filename = _conf_filename + while True: + filename = filedialog.askopenfilename( + title="Open configuration", + initialdir=os.path.dirname(filename), + initialfile=os.path.basename(filename), + parent=_root) + + if not filename: + break + + if _try_load(filename): + # Maybe something fancier could be done here later to try to + # preserve the scroll + + _conf_filename = filename + _set_conf_changed(_needs_save()) + + if _single_menu and not _shown_menu_nodes(_cur_menu): + # Turn on show-all if we're in single-menu mode and would end + # up with an empty menu + _show_all_var.set(True) + + _update_tree() + + break + + _tree.focus_set() + + +def _toggle_showname(_): + # Toggles show-name mode on/off + + _show_name_var.set(not _show_name_var.get()) + _do_showname() + + +def _do_showname(): + # Updates the UI for the current show-name setting + + # Columns do not automatically shrink/expand, so we have to update + # column widths ourselves + + tree_width = _tree.winfo_width() + + if _show_name_var.get(): + _tree["displaycolumns"] = ("name",) + _tree["show"] = "tree headings" + name_width = tree_width//3 + _tree.column("#0", width=max(tree_width - name_width, 1)) + _tree.column("name", width=name_width) + else: + _tree["displaycolumns"] = () + _tree["show"] = "tree" + _tree.column("#0", width=tree_width) + + _tree.focus_set() + + +def _toggle_showall(_): + # Toggles show-all mode on/off + + _show_all_var.set(not _show_all) + _do_showall() + + +def _do_showall(): + # Updates the UI for the current show-all setting + + # Don't allow turning off show-all if we'd end up with no visible nodes + if _nothing_shown(): + _show_all_var.set(True) + return + + # Save scroll information. old_scroll can end up negative here, if the + # reference item isn't shown (only invisible items on the screen, and + # show-all being turned off). + + stayput = _vis_loc_ref_item() + # Probe the middle of the first row, to play it safe. identify_row(0) seems + # to return the row before the top row. + old_scroll = _item_row(stayput) - \ + _item_row(_tree.identify_row(_treeview_rowheight//2)) + + _update_tree() + + if _show_all: + # Deep magic: Unless we call update_idletasks(), the scroll adjustment + # below is restricted to the height of the old tree, instead of the + # height of the new tree. Since the tree with show-all on is guaranteed + # to be taller, and we want the maximum range, we only call it when + # turning show-all on. + # + # Strictly speaking, something similar ought to be done when changing + # symbol values, but it causes annoying flicker, and in 99% of cases + # things work anyway there (with usually minor scroll mess-ups in the + # 1% case). + _root.update_idletasks() + + # Restore scroll + _tree.yview(_item_row(stayput) - old_scroll) + + _tree.focus_set() + + +def _nothing_shown(): + # _do_showall() helper. Returns True if no nodes would get + # shown with the current show-all setting. Also handles the + # (obscure) case when there are no visible nodes in the entire + # tree, meaning guiconfig was automatically started in + # show-all mode, which mustn't be turned off. + + return not _shown_menu_nodes( + _cur_menu if _single_menu else _kconf.top_node) + + +def _toggle_tree_mode(_): + # Toggles single-menu mode on/off + + _single_menu_var.set(not _single_menu) + _do_tree_mode() + + +def _do_tree_mode(): + # Updates the UI for the current tree mode (full-tree or single-menu) + + loc_ref_node = _id_to_node[_loc_ref_item()] + + if not _single_menu: + # _jump_to() -> _enter_menu() already updates the tree, but + # _jump_to() -> load_parents() doesn't, because it isn't always needed. + # We always need to update the tree here, e.g. to add/remove "--->". + _update_tree() + + _jump_to(loc_ref_node) + _tree.focus_set() + + +def _enter_menu_and_select_first(menu): + # Enters the menu 'menu' and selects the first item. Used in single-menu + # mode. + + _enter_menu(menu) + _select(_tree, _tree.get_children()[0]) + + +def _enter_menu(menu): + # Enters the menu 'menu'. Used in single-menu mode. + + global _cur_menu + + _cur_menu = menu + _update_tree() + + _backbutton["state"] = "disabled" if menu is _kconf.top_node else "normal" + + +def _leave_menu(): + # Leaves the current menu. Used in single-menu mode. + + global _cur_menu + + if _cur_menu is not _kconf.top_node: + old_menu = _cur_menu + + _cur_menu = _parent_menu(_cur_menu) + _update_tree() + + _select(_tree, id(old_menu)) + + if _cur_menu is _kconf.top_node: + _backbutton["state"] = "disabled" + + _tree.focus_set() + + +def _select(tree, item): + # Selects, focuses, and see()s 'item' in 'tree' + + tree.selection_set(item) + tree.focus(item) + tree.see(item) + + +def _loc_ref_item(): + # Returns a Treeview item that can serve as a reference for the current + # scroll location. We try to make this item stay on the same row on the + # screen when updating the tree. + + # If the selected item is visible, use that + sel = _tree.selection() + if sel and _tree.bbox(sel[0]): + return sel[0] + + # Otherwise, use the middle item on the screen. If it doesn't exist, the + # tree is probably really small, so use the first item in the entire tree. + return _tree.identify_row(_tree.winfo_height()//2) or \ + _tree.get_children()[0] + + +def _vis_loc_ref_item(): + # Like _loc_ref_item(), but finds a visible item around the reference item. + # Used when changing show-all mode, where non-visible (red) items will + # disappear. + + item = _loc_ref_item() + + vis_before = _vis_before(item) + if vis_before and _tree.bbox(vis_before): + return vis_before + + vis_after = _vis_after(item) + if vis_after and _tree.bbox(vis_after): + return vis_after + + return vis_before or vis_after + + +def _vis_before(item): + # _vis_loc_ref_item() helper. Returns the first visible (not red) item, + # searching backwards from 'item'. + + while item: + if not _tree.tag_has("invisible", item): + return item + + prev = _tree.prev(item) + item = prev if prev else _tree.parent(item) + + return None + + +def _vis_after(item): + # _vis_loc_ref_item() helper. Returns the first visible (not red) item, + # searching forwards from 'item'. + + while item: + if not _tree.tag_has("invisible", item): + return item + + next = _tree.next(item) + if next: + item = next + else: + item = _tree.parent(item) + if not item: + break + item = _tree.next(item) + + return None + + +def _on_quit(_=None): + # Called when the user wants to exit + + if not _conf_changed: + _quit("No changes to save (for '{}')".format(_conf_filename)) + return + + while True: + ync = messagebox.askyesnocancel("Quit", "Save changes?") + if ync is None: + return + + if not ync: + _quit("Configuration ({}) was not saved".format(_conf_filename)) + return + + if _try_save(_kconf.write_config, _conf_filename, "configuration"): + # _try_save() already prints the "Configuration saved to ..." + # message + _quit() + return + + +def _quit(msg=None): + # Quits the application + + # Do not call sys.exit() here, in case we're being run from a script + _root.destroy() + if msg: + print(msg) + + +def _try_save(save_fn, filename, description): + # Tries to save a configuration file. Pops up an error and returns False on + # failure. + # + # save_fn: + # Function to call with 'filename' to save the file + # + # description: + # String describing the thing being saved + + try: + # save_fn() returns a message to print + msg = save_fn(filename) + _set_status(msg) + print(msg) + return True + except EnvironmentError as e: + messagebox.showerror( + "Error saving " + description, + "Error saving {} to '{}': {} (errno: {})" + .format(description, e.filename, e.strerror, + errno.errorcode[e.errno])) + return False + + +def _try_load(filename): + # Tries to load a configuration file. Pops up an error and returns False on + # failure. + # + # filename: + # Configuration file to load + + try: + msg = _kconf.load_config(filename) + _set_status(msg) + print(msg) + return True + except EnvironmentError as e: + messagebox.showerror( + "Error loading configuration", + "Error loading '{}': {} (errno: {})" + .format(filename, e.strerror, errno.errorcode[e.errno])) + return False + + +def _jump_to_dialog(_=None): + # Pops up a dialog for jumping directly to a particular node. Symbol values + # can also be changed within the dialog. + # + # Note: There's nothing preventing this from doing an incremental search + # like menuconfig.py does, but currently it's a bit jerky for large Kconfig + # trees, at least when inputting the beginning of the search string. We'd + # need to somehow only update the tree items that are shown in the Treeview + # to fix it. + + global _jump_to_tree + + def search(_=None): + _update_jump_to_matches(msglabel, entry.get()) + + def jump_to_selected(event=None): + # Jumps to the selected node and closes the dialog + + # Ignore double clicks on the image and in the heading area + if event and (tree.identify_element(event.x, event.y) == "image" or + _in_heading(event)): + return + + sel = tree.selection() + if not sel: + return + + node = _id_to_node[sel[0]] + + if node not in _shown_menu_nodes(_parent_menu(node)): + _show_all_var.set(True) + if not _single_menu: + # See comment in _do_tree_mode() + _update_tree() + + _jump_to(node) + + dialog.destroy() + + def tree_select(_): + jumpto_button["state"] = "normal" if tree.selection() else "disabled" + + + dialog = Toplevel(_root) + dialog.geometry("+{}+{}".format( + _root.winfo_rootx() + 50, _root.winfo_rooty() + 50)) + dialog.title("Jump to symbol/choice/menu/comment") + dialog.minsize(128, 128) # See _create_ui() + dialog.transient(_root) + + ttk.Label(dialog, text=_JUMP_TO_HELP) \ + .grid(column=0, row=0, columnspan=2, sticky="w", padx=".1c", + pady=".1c") + + entry = ttk.Entry(dialog) + entry.grid(column=0, row=1, sticky="ew", padx=".1c", pady=".1c") + entry.focus_set() + + entry.bind("", search) + entry.bind("", search) + + ttk.Button(dialog, text="Search", command=search) \ + .grid(column=1, row=1, padx="0 .1c", pady="0 .1c") + + msglabel = ttk.Label(dialog) + msglabel.grid(column=0, row=2, sticky="w", pady="0 .1c") + + panedwindow, tree = _create_kconfig_tree_and_desc(dialog) + panedwindow.grid(column=0, row=3, columnspan=2, sticky="nsew") + + # Clear tree + tree.set_children("") + + _jump_to_tree = tree + + jumpto_button = ttk.Button(dialog, text="Jump to selected item", + state="disabled", command=jump_to_selected) + jumpto_button.grid(column=0, row=4, columnspan=2, sticky="ns", pady=".1c") + + dialog.columnconfigure(0, weight=1) + # Only the pane with the Kconfig tree and description grows vertically + dialog.rowconfigure(3, weight=1) + + # See the menuconfig() function + _root.update_idletasks() + dialog.geometry(dialog.geometry()) + + # The dialog must be visible before we can grab the input + dialog.wait_visibility() + dialog.grab_set() + + tree.bind("", jump_to_selected) + tree.bind("", jump_to_selected) + tree.bind("", jump_to_selected) + # add=True to avoid overriding the description text update + tree.bind("<>", tree_select, add=True) + + dialog.bind("", lambda _: dialog.destroy()) + + # Wait for the user to be done with the dialog + _root.wait_window(dialog) + + _jump_to_tree = None + + _tree.focus_set() + + +def _update_jump_to_matches(msglabel, search_string): + # Searches for nodes matching the search string and updates + # _jump_to_matches. Puts a message in 'msglabel' if there are no matches, + # or regex errors. + + global _jump_to_matches + + _jump_to_tree.selection_set(()) + + try: + # We could use re.IGNORECASE here instead of lower(), but this is + # faster for regexes like '.*debug$' (though the '.*' is redundant + # there). Those probably have bad interactions with re.search(), which + # matches anywhere in the string. + regex_searches = [re.compile(regex).search + for regex in search_string.lower().split()] + except re.error as e: + msg = "Bad regular expression" + # re.error.msg was added in Python 3.5 + if hasattr(e, "msg"): + msg += ": " + e.msg + msglabel["text"] = msg + # Clear tree + _jump_to_tree.set_children("") + return + + _jump_to_matches = [] + add_match = _jump_to_matches.append + + for node in _sorted_sc_nodes(): + # Symbol/choice + sc = node.item + + for search in regex_searches: + # Both the name and the prompt might be missing, since + # we're searching both symbols and choices + + # Does the regex match either the symbol name or the + # prompt (if any)? + if not (sc.name and search(sc.name.lower()) or + node.prompt and search(node.prompt[0].lower())): + + # Give up on the first regex that doesn't match, to + # speed things up a bit when multiple regexes are + # entered + break + + else: + add_match(node) + + # Search menus and comments + + for node in _sorted_menu_comment_nodes(): + for search in regex_searches: + if not search(node.prompt[0].lower()): + break + else: + add_match(node) + + msglabel["text"] = "" if _jump_to_matches else "No matches" + + _update_jump_to_display() + + if _jump_to_matches: + item = id(_jump_to_matches[0]) + _jump_to_tree.selection_set(item) + _jump_to_tree.focus(item) + + +def _update_jump_to_display(): + # Updates the images and text for the items in _jump_to_matches, and sets + # them as the items of _jump_to_tree + + # Micro-optimize a bit + item = _jump_to_tree.item + id_ = id + node_str = _node_str + img_tag = _img_tag + visible = _visible + for node in _jump_to_matches: + item(id_(node), + text=node_str(node), + tags=img_tag(node) if visible(node) else + img_tag(node) + " invisible") + + _jump_to_tree.set_children("", *map(id, _jump_to_matches)) + + +def _jump_to(node): + # Jumps directly to 'node' and selects it + + if _single_menu: + _enter_menu(_parent_menu(node)) + else: + _load_parents(node) + + _select(_tree, id(node)) + + +# Obscure Python: We never pass a value for cached_nodes, and it keeps pointing +# to the same list. This avoids a global. +def _sorted_sc_nodes(cached_nodes=[]): + # Returns a sorted list of symbol and choice nodes to search. The symbol + # nodes appear first, sorted by name, and then the choice nodes, sorted by + # prompt and (secondarily) name. + + if not cached_nodes: + # Add symbol nodes + for sym in sorted(_kconf.unique_defined_syms, + key=lambda sym: sym.name): + # += is in-place for lists + cached_nodes += sym.nodes + + # Add choice nodes + + choices = sorted(_kconf.unique_choices, + key=lambda choice: choice.name or "") + + cached_nodes += sorted( + [node for choice in choices for node in choice.nodes], + key=lambda node: node.prompt[0] if node.prompt else "") + + return cached_nodes + + +def _sorted_menu_comment_nodes(cached_nodes=[]): + # Returns a list of menu and comment nodes to search, sorted by prompt, + # with the menus first + + if not cached_nodes: + def prompt_text(mc): + return mc.prompt[0] + + cached_nodes += sorted(_kconf.menus, key=prompt_text) + cached_nodes += sorted(_kconf.comments, key=prompt_text) + + return cached_nodes + + +def _load_parents(node): + # Menus are lazily populated as they're opened in full-tree mode, but + # jumping to an item needs its parent menus to be populated. This function + # populates 'node's parents. + + # Get all parents leading up to 'node', sorted with the root first + parents = [] + cur = node.parent + while cur is not _kconf.top_node: + parents.append(cur) + cur = cur.parent + parents.reverse() + + for i, parent in enumerate(parents): + if not _tree.item(id(parent), "open"): + # Found a closed menu. Populate it and all the remaining menus + # leading up to 'node'. + for parent in parents[i:]: + # We only need to populate "real" menus/choices. Implicit menus + # are populated when their parents menus are entered. + if not isinstance(parent.item, Symbol): + _build_full_tree(parent) + return + + +def _parent_menu(node): + # Returns the menu node of the menu that contains 'node'. In addition to + # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'. + # "Menu" here means a menu in the interface. + + menu = node.parent + while not menu.is_menuconfig: + menu = menu.parent + return menu + + +def _trace_write(var, fn): + # Makes fn() be called whenever the Tkinter Variable 'var' changes value + + # trace_variable() is deprecated according to the docstring, + # which recommends trace_add() + if hasattr(var, "trace_add"): + var.trace_add("write", fn) + else: + var.trace_variable("w", fn) + + +def _info_str(node): + # Returns information about the menu node 'node' as a string. + # + # The helper functions are responsible for adding newlines. This allows + # them to return "" if they don't want to add any output. + + if isinstance(node.item, Symbol): + sym = node.item + + return ( + _name_info(sym) + + _help_info(sym) + + _direct_dep_info(sym) + + _defaults_info(sym) + + _select_imply_info(sym) + + _kconfig_def_info(sym) + ) + + if isinstance(node.item, Choice): + choice = node.item + + return ( + _name_info(choice) + + _help_info(choice) + + 'Mode: {}\n\n'.format(choice.str_value) + + _choice_syms_info(choice) + + _direct_dep_info(choice) + + _defaults_info(choice) + + _kconfig_def_info(choice) + ) + + # node.item in (MENU, COMMENT) + return _kconfig_def_info(node) + + +def _name_info(sc): + # Returns a string with the name of the symbol/choice. Choices are shown as + # . + + return (sc.name if sc.name else standard_sc_expr_str(sc)) + "\n\n" + + +def _value_info(sym): + # Returns a string showing 'sym's value + + # Only put quotes around the value for string symbols + return "Value: {}\n".format( + '"{}"'.format(sym.str_value) + if sym.orig_type == STRING + else sym.str_value) + + +def _choice_syms_info(choice): + # Returns a string listing the choice symbols in 'choice'. Adds + # "(selected)" next to the selected one. + + s = "Choice symbols:\n" + + for sym in choice.syms: + s += " - " + sym.name + if sym is choice.selection: + s += " (selected)" + s += "\n" + + return s + "\n" + + +def _help_info(sc): + # Returns a string with the help text(s) of 'sc' (Symbol or Choice). + # Symbols and choices defined in multiple locations can have multiple help + # texts. + + s = "" + + for node in sc.nodes: + if node.help is not None: + s += node.help + "\n\n" + + return s + + +def _direct_dep_info(sc): + # Returns a string describing the direct dependencies of 'sc' (Symbol or + # Choice). The direct dependencies are the OR of the dependencies from each + # definition location. The dependencies at each definition location come + # from 'depends on' and dependencies inherited from parent items. + + return "" if sc.direct_dep is _kconf.y else \ + 'Direct dependencies (={}):\n{}\n' \ + .format(TRI_TO_STR[expr_value(sc.direct_dep)], + _split_expr_info(sc.direct_dep, 2)) + + +def _defaults_info(sc): + # Returns a string describing the defaults of 'sc' (Symbol or Choice) + + if not sc.defaults: + return "" + + s = "Default" + if len(sc.defaults) > 1: + s += "s" + s += ":\n" + + for val, cond in sc.orig_defaults: + s += " - " + if isinstance(sc, Symbol): + s += _expr_str(val) + + # Skip the tristate value hint if the expression is just a single + # symbol. _expr_str() already shows its value as a string. + # + # This also avoids showing the tristate value for string/int/hex + # defaults, which wouldn't make any sense. + if isinstance(val, tuple): + s += ' (={})'.format(TRI_TO_STR[expr_value(val)]) + else: + # Don't print the value next to the symbol name for choice + # defaults, as it looks a bit confusing + s += val.name + s += "\n" + + if cond is not _kconf.y: + s += " Condition (={}):\n{}" \ + .format(TRI_TO_STR[expr_value(cond)], + _split_expr_info(cond, 4)) + + return s + "\n" + + +def _split_expr_info(expr, indent): + # Returns a string with 'expr' split into its top-level && or || operands, + # with one operand per line, together with the operand's value. This is + # usually enough to get something readable for long expressions. A fancier + # recursive thingy would be possible too. + # + # indent: + # Number of leading spaces to add before the split expression. + + if len(split_expr(expr, AND)) > 1: + split_op = AND + op_str = "&&" + else: + split_op = OR + op_str = "||" + + s = "" + for i, term in enumerate(split_expr(expr, split_op)): + s += "{}{} {}".format(indent*" ", + " " if i == 0 else op_str, + _expr_str(term)) + + # Don't bother showing the value hint if the expression is just a + # single symbol. _expr_str() already shows its value. + if isinstance(term, tuple): + s += " (={})".format(TRI_TO_STR[expr_value(term)]) + + s += "\n" + + return s + + +def _select_imply_info(sym): + # Returns a string with information about which symbols 'select' or 'imply' + # 'sym'. The selecting/implying symbols are grouped according to which + # value they select/imply 'sym' to (n/m/y). + + def sis(expr, val, title): + # sis = selects/implies + sis = [si for si in split_expr(expr, OR) if expr_value(si) == val] + if not sis: + return "" + + res = title + for si in sis: + res += " - {}\n".format(split_expr(si, AND)[0].name) + return res + "\n" + + s = "" + + if sym.rev_dep is not _kconf.n: + s += sis(sym.rev_dep, 2, + "Symbols currently y-selecting this symbol:\n") + s += sis(sym.rev_dep, 1, + "Symbols currently m-selecting this symbol:\n") + s += sis(sym.rev_dep, 0, + "Symbols currently n-selecting this symbol (no effect):\n") + + if sym.weak_rev_dep is not _kconf.n: + s += sis(sym.weak_rev_dep, 2, + "Symbols currently y-implying this symbol:\n") + s += sis(sym.weak_rev_dep, 1, + "Symbols currently m-implying this symbol:\n") + s += sis(sym.weak_rev_dep, 0, + "Symbols currently n-implying this symbol (no effect):\n") + + return s + + +def _kconfig_def_info(item): + # Returns a string with the definition of 'item' in Kconfig syntax, + # together with the definition location(s) and their include and menu paths + + nodes = [item] if isinstance(item, MenuNode) else item.nodes + + s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \ + .format("s" if len(nodes) > 1 else "") + s += (len(s) - 1)*"=" + + for node in nodes: + s += "\n\n" \ + "At {}:{}\n" \ + "{}" \ + "Menu path: {}\n\n" \ + "{}" \ + .format(node.filename, node.linenr, + _include_path_info(node), + _menu_path_info(node), + node.custom_str(_name_and_val_str)) + + return s + + +def _include_path_info(node): + if not node.include_path: + # In the top-level Kconfig file + return "" + + return "Included via {}\n".format( + " -> ".join("{}:{}".format(filename, linenr) + for filename, linenr in node.include_path)) + + +def _menu_path_info(node): + # Returns a string describing the menu path leading up to 'node' + + path = "" + + while node.parent is not _kconf.top_node: + node = node.parent + + # Promptless choices might appear among the parents. Use + # standard_sc_expr_str() for them, so that they show up as + # ''. + path = " -> " + (node.prompt[0] if node.prompt else + standard_sc_expr_str(node.item)) + path + + return "(Top)" + path + + +def _name_and_val_str(sc): + # Custom symbol/choice printer that shows symbol values after symbols + + # Show the values of non-constant (non-quoted) symbols that don't look like + # numbers. Things like 123 are actually symbol references, and only work as + # expected due to undefined symbols getting their name as their value. + # Showing the symbol value for those isn't helpful though. + if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name): + if not sc.nodes: + # Undefined symbol reference + return "{}(undefined/n)".format(sc.name) + + return '{}(={})'.format(sc.name, sc.str_value) + + # For other items, use the standard format + return standard_sc_expr_str(sc) + + +def _expr_str(expr): + # Custom expression printer that shows symbol values + return expr_str(expr, _name_and_val_str) + + +def _is_num(name): + # Heuristic to see if a symbol name looks like a number, for nicer output + # when printing expressions. Things like 16 are actually symbol names, only + # they get their name as their value when the symbol is undefined. + + try: + int(name) + except ValueError: + if not name.startswith(("0x", "0X")): + return False + + try: + int(name, 16) + except ValueError: + return False + + return True + + +if __name__ == "__main__": + _main() diff --git a/atf-20250711/Kconfiglib/kconfiglib.py b/atf-20250711/Kconfiglib/kconfiglib.py new file mode 100644 index 000000000..c67895ced --- /dev/null +++ b/atf-20250711/Kconfiglib/kconfiglib.py @@ -0,0 +1,7160 @@ +# Copyright (c) 2011-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Overview +======== + +Kconfiglib is a Python 2/3 library for scripting and extracting information +from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt) +configuration systems. + +See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer +overview. + +Since Kconfiglib 12.0.0, the library version is available in +kconfiglib.VERSION, which is a (, , ) tuple, e.g. +(12, 0, 0). + + +Using Kconfiglib on the Linux kernel with the Makefile targets +============================================================== + +For the Linux kernel, a handy interface is provided by the +scripts/kconfig/Makefile patch, which can be applied with either 'git am' or +the 'patch' utility: + + $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am + $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1 + +Warning: Not passing -p1 to patch will cause the wrong file to be patched. + +Please tell me if the patch does not apply. It should be trivial to apply +manually, as it's just a block of text that needs to be inserted near the other +*conf: targets in scripts/kconfig/Makefile. + +Look further down for a motivation for the Makefile patch and for instructions +on how you can use Kconfiglib without it. + +If you do not wish to install Kconfiglib via pip, the Makefile patch is set up +so that you can also just clone Kconfiglib into the kernel root: + + $ git clone git://github.com/ulfalizer/Kconfiglib.git + $ git am Kconfiglib/makefile.patch (or 'patch -p1 < Kconfiglib/makefile.patch') + +Warning: The directory name Kconfiglib/ is significant in this case, because +it's added to PYTHONPATH by the new targets in makefile.patch. + +The targets added by the Makefile patch are described in the following +sections. + + +make kmenuconfig +---------------- + +This target runs the curses menuconfig interface with Python 3. As of +Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only +Python 3 was supported, so this was a backport). + + +make guiconfig +-------------- + +This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3 +are supported. To change the Python interpreter used, pass +PYTHONCMD= to 'make'. The default is 'python'. + + +make [ARCH=] iscriptconfig +-------------------------------- + +This target gives an interactive Python prompt where a Kconfig instance has +been preloaded and is available in 'kconf'. To change the Python interpreter +used, pass PYTHONCMD= to 'make'. The default is 'python'. + +To get a feel for the API, try evaluating and printing the symbols in +kconf.defined_syms, and explore the MenuNode menu tree starting at +kconf.top_node by following 'next' and 'list' pointers. + +The item contained in a menu node is found in MenuNode.item (note that this can +be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all +symbols and choices have a 'nodes' attribute containing their menu nodes +(usually only one). Printing a menu node will print its item, in Kconfig +format. + +If you want to look up a symbol by name, use the kconf.syms dictionary. + + +make scriptconfig SCRIPT= + + + + + + + diff --git a/atf-20250711/tools/nxp/cert_create_helper/cert_create_tbbr.mk b/atf-20250711/tools/nxp/cert_create_helper/cert_create_tbbr.mk new file mode 100644 index 000000000..e3b2e9178 --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/cert_create_tbbr.mk @@ -0,0 +1,31 @@ +# +# Copyright 2021 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# Compile time defines used by NXP platforms + +PLAT_DEF_OID := yes + +ifeq (${PLAT_DEF_OID},yes) + +$(eval $(call add_define, PLAT_DEF_OID)) +$(eval $(call add_define, PDEF_KEYS)) +$(eval $(call add_define, PDEF_CERTS)) +$(eval $(call add_define, PDEF_EXTS)) + + +INC_DIR += -I../../plat/nxp/common/fip_handler/common/ + +PDEF_CERT_TOOL_PATH := ../nxp/cert_create_helper +PLAT_INCLUDE += -I${PDEF_CERT_TOOL_PATH}/include + +PLAT_OBJECTS += ${PDEF_CERT_TOOL_PATH}/src/pdef_tbb_cert.o \ + ${PDEF_CERT_TOOL_PATH}/src/pdef_tbb_ext.o \ + ${PDEF_CERT_TOOL_PATH}/src/pdef_tbb_key.o + +$(shell rm ${PLAT_OBJECTS}) + +OBJECTS += ${PLAT_OBJECTS} +endif diff --git a/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_cert.h b/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_cert.h new file mode 100644 index 000000000..f18561979 --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_cert.h @@ -0,0 +1,21 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PDEF_TBB_CERT_H +#define PDEF_TBB_CERT_H + +#include + +/* + * Enumerate the certificates that are used to establish the chain of trust + */ +enum { + DDR_FW_KEY_CERT = FWU_CERT + 1, + DDR_UDIMM_FW_CONTENT_CERT, + DDR_RDIMM_FW_CONTENT_CERT +}; + +#endif /* PDEF_TBB_CERT_H */ diff --git a/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_ext.h b/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_ext.h new file mode 100644 index 000000000..5fb349cf7 --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_ext.h @@ -0,0 +1,25 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PDEF_TBB_EXT_H +#define PDEF_TBB_EXT_H + +#include + +/* Plat Defined TBBR extensions */ +enum { + DDR_FW_CONTENT_CERT_PK_EXT = FWU_HASH_EXT + 1, + DDR_IMEM_UDIMM_1D_HASH_EXT, + DDR_IMEM_UDIMM_2D_HASH_EXT, + DDR_DMEM_UDIMM_1D_HASH_EXT, + DDR_DMEM_UDIMM_2D_HASH_EXT, + DDR_IMEM_RDIMM_1D_HASH_EXT, + DDR_IMEM_RDIMM_2D_HASH_EXT, + DDR_DMEM_RDIMM_1D_HASH_EXT, + DDR_DMEM_RDIMM_2D_HASH_EXT +}; + +#endif /* PDEF_TBB_EXT_H */ diff --git a/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_key.h b/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_key.h new file mode 100644 index 000000000..b26b65153 --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/include/pdef_tbb_key.h @@ -0,0 +1,18 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PDEF_TBB_KEY_H +#define PDEF_TBB_KEY_H + +#include + +/* + * Enumerate the pltform defined keys that are used to establish the chain of trust + */ +enum { + DDR_FW_CONTENT_KEY = NON_TRUSTED_FW_CONTENT_CERT_KEY + 1, +}; +#endif /* PDEF_TBB_KEY_H */ diff --git a/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_cert.c b/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_cert.c new file mode 100644 index 000000000..40bd9282b --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_cert.c @@ -0,0 +1,62 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +static cert_t pdef_tbb_certs[] = { + [DDR_FW_KEY_CERT - DDR_FW_KEY_CERT] = { + .id = DDR_FW_KEY_CERT, + .opt = "ddr-fw-key-cert", + .help_msg = "DDR Firmware Key Certificate (output file)", + .fn = NULL, + .cn = "DDR Firmware Key Certificate", + .key = TRUSTED_WORLD_KEY, + .issuer = DDR_FW_KEY_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + DDR_FW_CONTENT_CERT_PK_EXT, + }, + .num_ext = 2 + }, + [DDR_UDIMM_FW_CONTENT_CERT - DDR_FW_KEY_CERT] = { + .id = DDR_UDIMM_FW_CONTENT_CERT, + .opt = "ddr-udimm-fw-cert", + .help_msg = "DDR UDIMM Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "DDR UDIMM Firmware Content Certificate", + .key = DDR_FW_CONTENT_KEY, + .issuer = DDR_UDIMM_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + DDR_IMEM_UDIMM_1D_HASH_EXT, + DDR_IMEM_UDIMM_2D_HASH_EXT, + DDR_DMEM_UDIMM_1D_HASH_EXT, + DDR_DMEM_UDIMM_2D_HASH_EXT, + }, + .num_ext = 5 + }, + [DDR_RDIMM_FW_CONTENT_CERT - DDR_FW_KEY_CERT] = { + .id = DDR_RDIMM_FW_CONTENT_CERT, + .opt = "ddr-rdimm-fw-cert", + .help_msg = "DDR RDIMM Firmware Content Certificate (output file)", + .fn = NULL, + .cn = "DDR RDIMM Firmware Content Certificate", + .key = DDR_FW_CONTENT_KEY, + .issuer = DDR_RDIMM_FW_CONTENT_CERT, + .ext = { + TRUSTED_FW_NVCOUNTER_EXT, + DDR_IMEM_RDIMM_1D_HASH_EXT, + DDR_IMEM_RDIMM_2D_HASH_EXT, + DDR_DMEM_RDIMM_1D_HASH_EXT, + DDR_DMEM_RDIMM_2D_HASH_EXT, + }, + .num_ext = 5 + } +}; + +PLAT_REGISTER_COT(pdef_tbb_certs); diff --git a/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_ext.c b/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_ext.c new file mode 100644 index 000000000..f6da6ddde --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_ext.c @@ -0,0 +1,108 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#if USE_TBBR_DEFS +#include +#else +#include +#endif + +#include "ext.h" +#include "tbbr/tbb_ext.h" +#include "tbbr/tbb_key.h" + +#include +#include + +static ext_t pdef_tbb_ext[] = { + [DDR_FW_CONTENT_CERT_PK_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_FW_CONTENT_CERT_PK_OID, + .sn = "DDR FirmwareContentCertPK", + .ln = "DDR Firmware content certificate public key", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_PKEY, + .attr.key = DDR_FW_CONTENT_KEY + }, + [DDR_IMEM_UDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_UDIMM_1D_HASH_OID, + .opt = "ddr-immem-udimm-1d", + .help_msg = "DDR Firmware IMEM UDIMM 1D image file", + .sn = "DDR UDIMM IMEM 1D FirmwareHash", + .ln = "DDR UDIMM IMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_IMEM_UDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_UDIMM_2D_HASH_OID, + .opt = "ddr-immem-udimm-2d", + .help_msg = "DDR Firmware IMEM UDIMM 2D image file", + .sn = "DDR UDIMM IMEM 2D FirmwareHash", + .ln = "DDR UDIMM IMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_UDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_UDIMM_1D_HASH_OID, + .opt = "ddr-dmmem-udimm-1d", + .help_msg = "DDR Firmware DMEM UDIMM 1D image file", + .sn = "DDR UDIMM DMEM 1D FirmwareHash", + .ln = "DDR UDIMM DMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_UDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_UDIMM_2D_HASH_OID, + .opt = "ddr-dmmem-udimm-2d", + .help_msg = "DDR Firmware DMEM UDIMM 2D image file", + .sn = "DDR UDIMM DMEM 2D FirmwareHash", + .ln = "DDR UDIMM DMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_IMEM_RDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_RDIMM_1D_HASH_OID, + .opt = "ddr-immem-rdimm-1d", + .help_msg = "DDR Firmware IMEM RDIMM 1D image file", + .sn = "DDR RDIMM IMEM 1D FirmwareHash", + .ln = "DDR RDIMM IMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_IMEM_RDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_IMEM_RDIMM_2D_HASH_OID, + .opt = "ddr-immem-rdimm-2d", + .help_msg = "DDR Firmware IMEM RDIMM 2D image file", + .sn = "DDR RDIMM IMEM 2D FirmwareHash", + .ln = "DDR RDIMM IMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_RDIMM_1D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_RDIMM_1D_HASH_OID, + .opt = "ddr-dmmem-rdimm-1d", + .help_msg = "DDR Firmware DMEM RDIMM 1D image file", + .sn = "DDR RDIMM DMEM 1D FirmwareHash", + .ln = "DDR RDIMM DMEM 1D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + }, + [DDR_DMEM_RDIMM_2D_HASH_EXT - DDR_FW_CONTENT_CERT_PK_EXT] = { + .oid = DDR_DMEM_RDIMM_2D_HASH_OID, + .opt = "ddr-dmmem-rdimm-2d", + .help_msg = "DDR Firmware DMEM RDIMM 2D image file", + .sn = "DDR RDIMM DMEM 2D FirmwareHash", + .ln = "DDR RDIMM DMEM 2D Firmware hash (SHA256)", + .asn1_type = V_ASN1_OCTET_STRING, + .type = EXT_TYPE_HASH + } +}; + +PLAT_REGISTER_EXTENSIONS(pdef_tbb_ext); diff --git a/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_key.c b/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_key.c new file mode 100644 index 000000000..cd48866c7 --- /dev/null +++ b/atf-20250711/tools/nxp/cert_create_helper/src/pdef_tbb_key.c @@ -0,0 +1,18 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +static cert_key_t pdef_tbb_keys[] = { + [DDR_FW_CONTENT_KEY - DDR_FW_CONTENT_KEY] = { + .id = DDR_FW_CONTENT_KEY, + .opt = "ddr-fw-key", + .help_msg = "DDR Firmware Content Certificate key (input/output file)", + .desc = "DDR Firmware Content Certificate key" + } +}; + +PLAT_REGISTER_KEYS(pdef_tbb_keys); diff --git a/atf-20250711/tools/nxp/create_pbl/Makefile b/atf-20250711/tools/nxp/create_pbl/Makefile new file mode 100644 index 000000000..9285b723e --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/Makefile @@ -0,0 +1,53 @@ +# +# Copyright 2018-2020 NXP +# Copyright (c) 2025, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk + +PROJECT_1 := create_pbl$(.exe) +OBJECTS_1 := create_pbl.o +PROJECT_2 := byte_swap$(.exe) +OBJECTS_2 := byte_swap.o + +override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 +CFLAGS := -Wall -Werror -pedantic -std=c99 +ifeq (${DEBUG},1) + CFLAGS += -g -O0 -DDEBUG +else + CFLAGS += -O2 +endif +LDLIBS := + +INCLUDE_PATHS := + +.PHONY: all clean distclean + +all: create_pbl byte_swap + +${PROJECT_1}: ${OBJECTS_1} Makefile + $(s)echo " LD $@" + $(q)$(host-cc) ${OBJECTS_1} -o $@ ${LDLIBS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo + +${PROJECT_2}: ${OBJECTS_2} Makefile + $(s)echo " LD $@" + $(q)$(host-cc) ${OBJECTS_2} -o $@ ${LDLIBS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo + +${OBJECTS_1} ${OBJECTS_2}: %.o: %.c Makefile + $(s)echo " CC $<" + $(q)$(host-cc) -c ${CPPFLAGS} ${CFLAGS} ${INCLUDE_PATHS} $< -o $@ + +clean: + $(q)rm -rf $(PROJECT_1) $(OBJECTS_1) + $(q)rm -rf $(PROJECT_2) $(OBJECTS_2) diff --git a/atf-20250711/tools/nxp/create_pbl/README b/atf-20250711/tools/nxp/create_pbl/README new file mode 100644 index 000000000..3b6f854c6 --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/README @@ -0,0 +1,65 @@ +Description: +------------ +Tool 'create_pbl' is a standalone tool to create the PBL images. + where, + On the basis of Chassis, + RCW image is placed first followed by the, + PBI commands to copy the, + Input BL2 image stored on the, + Specified boot source (QSPI or SD or NOR) to the, + Specified destination address. + + +Usage in standalone way: +----------------------- + +./create_pbl [options] (mentioned below): + + -r - name of RCW binary file. + -i - file to be added to rcw file. + -c - SoC numeric identifier, may be one of + 1012,1023,1026.1028, + 1043,1046,1088,2080, + 2088,2160 + -b - Boot source id string, may be one of + "qspi", "nor", "nand", "sd", "emmc" + -d
- Destination address where BL2 + image is to be copied + -o - Name of PBL image generated + as an output of the tool. + -e
- [Optional] Entry Point Address + of the BL2.bin + -f
- BL2 image offset + on Boot Source for block copy. + command for chassis >=3.) + (Must for Ch3, Ignored for Ch2) + -h Help. + -s Secure boot. + + -s secure boot + -c SoC Number (see description above) + -b Boot source. + -r RCW binary file. + -i Input file that is to be added to rcw file. + -o Name of output file + -f Source Offset (Block Copy) + -d Destination address to which file has to be copied + -h Help. + +Example: + ./create_pbl -r -i -c -b -d -o + + + +Usage at compilation time: +-------------------------------- + + make pbl RCW=/ + +Example: QSPI Boot For LS1046ARDB- + + make PLAT=ls1046rdb all fip BOOT_MODE=qspi SPD=opteed BL32=tee.bin BL33=u-boot-ls1046.bin pbl RCW=/home/pankaj/flexbuild/packages/firmware/dash-rcw/ls1046ardb/RR_FFSSPPPN_1133_5506/rcw_1600_qspiboot.bin + +Example: QSPI Boot For LX2160ARDB- + + make PLAT=lx2160ardb all fip BOOT_MODE=flexspi_nor SPD=opteed BL32=tee_lx2.bin BL33=u-boot_lx2160.bin pbl RCW=plat/nxp/soc-lx2160/lx2160ardb/rcw_1900_600_1600_19_5_2.bin diff --git a/atf-20250711/tools/nxp/create_pbl/byte_swap.c b/atf-20250711/tools/nxp/create_pbl/byte_swap.c new file mode 100644 index 000000000..1d0bfce7d --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/byte_swap.c @@ -0,0 +1,113 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include +#include +#include +#include + +#include +#include + +#define NUM_MEM_BLOCK 1 +#define FOUR_BYTE_ALIGN 4 +#define EIGHT_BYTE_ALIGN 8 +#define SIZE_TWO_PBL_CMD 24 + +#define SUCCESS 0 +#define FAILURE -1 +#define BYTE_SWAP_32(word) ((((word) & 0xff000000) >> 24)| \ + (((word) & 0x00ff0000) >> 8) | \ + (((word) & 0x0000ff00) << 8) | \ + (((word) & 0x000000ff) << 24)) + + +/* + * Returns: + * SUCCESS, on successful byte swapping. + * FAILURE, on failure. + */ +int do_byteswap(FILE *fp) +{ + int bytes = 0; + uint32_t upper_byte; + uint32_t lower_byte; + uint32_t pad = 0U; + /* Carries number of Padding bytes to be appended to + * make file size 8 byte aligned. + */ + int append_bytes; + int ret = FAILURE; + + fseek(fp, 0L, SEEK_END); + bytes = ftell(fp); + + append_bytes = EIGHT_BYTE_ALIGN - (bytes % EIGHT_BYTE_ALIGN); + if (append_bytes != 0) { + if (fwrite(&pad, append_bytes, NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) { + printf("%s: Error in appending padding bytes.\n", + __func__); + goto byteswap_err; + } + bytes += append_bytes; + } + + rewind(fp); + while (bytes > 0) { + if ((fread(&upper_byte, sizeof(upper_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) && (feof(fp) == 0)) { + printf("%s: Error reading upper bytes.\n", __func__); + goto byteswap_err; + } + if ((fread(&lower_byte, sizeof(lower_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) && (feof(fp) == 0)) { + printf("%s: Error reading lower bytes.\n", __func__); + goto byteswap_err; + } + fseek(fp, -8L, SEEK_CUR); + upper_byte = BYTE_SWAP_32(upper_byte); + lower_byte = BYTE_SWAP_32(lower_byte); + if (fwrite(&lower_byte, sizeof(lower_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) { + printf("%s: Error writing lower bytes.\n", __func__); + goto byteswap_err; + } + if (fwrite(&upper_byte, sizeof(upper_byte), NUM_MEM_BLOCK, fp) + != NUM_MEM_BLOCK) { + printf("%s: Error writing upper bytes.\n", __func__); + goto byteswap_err; + } + bytes -= EIGHT_BYTE_ALIGN; + } + ret = SUCCESS; + +byteswap_err: + return ret; +} + +int main(int argc, char **argv) +{ + FILE *fp = NULL; + int ret = 0; + + if (argc > 2) { + printf("Usage format is byteswap "); + return -1; + } + + fp = fopen(argv[1], "rb+"); + if (fp == NULL) { + printf("%s: Error opening the input file: %s\n", + __func__, argv[1]); + return -1; + } + + ret = do_byteswap(fp); + fclose(fp); + return ret; +} diff --git a/atf-20250711/tools/nxp/create_pbl/create_pbl.c b/atf-20250711/tools/nxp/create_pbl/create_pbl.c new file mode 100644 index 000000000..c277e391a --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/create_pbl.c @@ -0,0 +1,1000 @@ +/* + * Copyright 2021-2022 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define NUM_MEM_BLOCK 1 +#define FOUR_BYTE_ALIGN 4 +#define EIGHT_BYTE_ALIGN 8 +#define SIZE_TWO_PBL_CMD 24 + +/* Define for add_boot_ptr_cmd() */ +#define BOOTPTR_ADDR 0x09570604 +#define CSF_ADDR_SB 0x09ee0200 +/* CCSR write command to address 0x1e00400 i.e BOOTLOCPTR */ +#define BOOTPTR_ADDR_CH3 0x31e00400 +/* Load CSF header command */ +#define CSF_ADDR_SB_CH3 0x80220000 + +#define MAND_ARG_MASK 0xFFF3 +#define ARG_INIT_MASK 0xFF00 +#define RCW_FILE_NAME_ARG_MASK 0x0080 +#define IN_FILE_NAME_ARG_MASK 0x0040 +#define CHASSIS_ARG_MASK 0x0020 +#define BOOT_SRC_ARG_MASK 0x0010 +#define ENTRY_POINT_ADDR_ARG_MASK 0x0008 +#define BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK 0x0004 +#define BL2_BIN_CPY_DEST_ADDR_ARG_MASK 0x0002 +#define OP_FILE_NAME_ARG_MASK 0x0001 + +/* Define for add_cpy_cmd() */ +#define OFFSET_MASK 0x00ffffff +#define WRITE_CMD_BASE 0x81000000 +#define MAX_PBI_DATA_LEN_BYTE 64 + +/* 140 Bytes = Preamble + LOAD RCW command + RCW (128 bytes) + Checksum */ +#define CHS3_CRC_PAYLOAD_START_OFFSET 140 + +#define PBI_CRC_POLYNOMIAL 0x04c11db7 + +typedef enum { + CHASSIS_UNKNOWN, + CHASSIS_2, + CHASSIS_3, + CHASSIS_3_2, + CHASSIS_MAX /* must be last item in list */ +} chassis_t; + +typedef enum { + UNKNOWN_BOOT = 0, + IFC_NOR_BOOT, + IFC_NAND_BOOT, + QSPI_BOOT, + SD_BOOT, + EMMC_BOOT, + FLXSPI_NOR_BOOT, + FLXSPI_NAND_BOOT, + FLXSPI_NAND4K_BOOT, + MAX_BOOT /* must be last item in list */ +} boot_src_t; + +/* Base Addresses where PBL image is copied depending on the boot source. + * Boot address map varies as per Chassis architecture. + */ +#define BASE_ADDR_UNDEFINED 0xFFFFFFFF +#define BASE_ADDR_QSPI 0x20000000 +#define BASE_ADDR_SD 0x00001000 +#define BASE_ADDR_IFC_NOR 0x30000000 +#define BASE_ADDR_EMMC 0x00001000 +#define BASE_ADDR_FLX_NOR 0x20000000 +#define BASE_ADDR_NAND 0x20000000 + +uint32_t base_addr_ch3[MAX_BOOT] = { + BASE_ADDR_UNDEFINED, + BASE_ADDR_IFC_NOR, + BASE_ADDR_UNDEFINED, /*IFC NAND */ + BASE_ADDR_QSPI, + BASE_ADDR_SD, + BASE_ADDR_EMMC, + BASE_ADDR_UNDEFINED, /*FLXSPI NOR */ + BASE_ADDR_UNDEFINED, /*FLXSPI NAND 2K */ + BASE_ADDR_UNDEFINED /*FLXSPI NAND 4K */ +}; + +uint32_t base_addr_ch32[MAX_BOOT] = { + BASE_ADDR_UNDEFINED, + BASE_ADDR_UNDEFINED, /* IFC NOR */ + BASE_ADDR_UNDEFINED, /* IFC NAND */ + BASE_ADDR_UNDEFINED, /* QSPI */ + BASE_ADDR_SD, + BASE_ADDR_EMMC, + BASE_ADDR_FLX_NOR, + BASE_ADDR_UNDEFINED, /*FLXSPI NAND 2K */ + BASE_ADDR_UNDEFINED /*FLXSPI NAND 4K */ +}; + +/* for Chassis 3 */ +uint32_t blk_cpy_hdr_map_ch3[] = { + + 0, /* Unknown Boot Source */ + 0x80000020, /* NOR_BOOT */ + 0x0, /* NAND_BOOT */ + 0x80000062, /* QSPI_BOOT */ + 0x80000040, /* SD_BOOT */ + 0x80000041, /* EMMC_BOOT */ + 0x0, /* FLEXSPI NOR_BOOT */ + 0x0, /* FLEX SPI NAND2K BOOT */ + 0x0, /* CHASIS3_2_NAND4K_BOOT */ +}; + +uint32_t blk_cpy_hdr_map_ch32[] = { + 0, /* Unknown Boot Source */ + 0x0, /* NOR_BOOT */ + 0x0, /* NAND_BOOT */ + 0x0, /* QSPI_BOOT */ + 0x80000008, /* SD_BOOT */ + 0x80000009, /* EMMC_BOOT */ + 0x8000000F, /* FLEXSPI NOR_BOOT */ + 0x8000000C, /* FLEX SPI NAND2K BOOT */ + 0x8000000D, /* CHASIS3_2_NAND4K_BOOT */ +}; + +char *boot_src_string[] = { + "UNKNOWN_BOOT", + "IFC_NOR_BOOT", + "IFC_NAND_BOOT", + "QSPI_BOOT", + "SD_BOOT", + "EMMC_BOOT", + "FLXSPI_NOR_BOOT", + "FLXSPI_NAND_BOOT", + "FLXSPI_NAND4K_BOOT", +}; + +enum stop_command { + STOP_COMMAND = 0, + CRC_STOP_COMMAND +}; + +/* Structure will get populated in the main function + * as part of parsing the command line arguments. + * All member parameters are mandatory except: + * -ep + * -src_addr + */ +struct pbl_image { + char *rcw_nm; /* Input RCW File */ + char *sec_imgnm; /* Input BL2 binary */ + char *imagefile; /* Generated output file */ + boot_src_t boot_src; /* Boot Source - QSPI, SD, NOR, NAND etc */ + uint32_t src_addr; /* Source Address */ + uint32_t addr; /* Load address */ + uint32_t ep; /* Entry point default is load address */ + chassis_t chassis; /* Chassis type */ +} pblimg; + +#define SUCCESS 0 +#define FAILURE -1 +#define CRC_STOP_CMD_ARM 0x08610040 +#define CRC_STOP_CMD_ARM_CH3 0x808f0000 +#define STOP_CMD_ARM_CH3 0x80ff0000 +#define BYTE_SWAP_32(word) ((((word) & 0xff000000) >> 24)| \ + (((word) & 0x00ff0000) >> 8) | \ + (((word) & 0x0000ff00) << 8) | \ + (((word) & 0x000000ff) << 24)) + +#define PBI_LEN_MASK 0xFFF00000 +#define PBI_LEN_SHIFT 20 +#define NUM_RCW_WORD 35 +#define PBI_LEN_ADD 6 + +#define MAX_CRC_ENTRIES 256 + +/* SoC numeric identifier */ +#define SOC_LS1012 1012 +#define SOC_LS1023 1023 +#define SOC_LS1026 1026 +#define SOC_LS1028 1028 +#define SOC_LS1043 1043 +#define SOC_LS1046 1046 +#define SOC_LS1088 1088 +#define SOC_LS2080 2080 +#define SOC_LS2088 2088 +#define SOC_LX2160 2160 + +static uint32_t pbl_size; +bool sb_flag; + +/*************************************************************************** + * Description : CRC32 Lookup Table + ***************************************************************************/ +static uint32_t crc32_lookup[] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + +static void print_usage(void) +{ + printf("\nCorrect Usage of Tool is:\n"); + printf("\n ./create_pbl [options] (mentioned below):\n\n"); + printf("\t-r - name of RCW binary file.\n"); + printf("\t-i - file to be added to rcw file.\n"); + printf("\t-c - Chassis Architecture (=2 or =3\n"); + printf("\t or =4 for 3.2).\n"); + printf("\t-b - Boot source.\n"); + printf("\t-d
- Destination address where BL2\n"); + printf("\t image is to be copied\n"); + printf("\t-o - Name of PBL image generated\n"); + printf("\t as an output of the tool.\n"); + printf("\t-f
- BL2 image Src Offset\n"); + printf("\t on Boot Source for block copy.\n"); + printf("\t command for chassis >=3.)\n"); + printf("\t-e
- [Optional] Entry Point Address\n"); + printf("\t of the BL2.bin\n"); + printf("\t-s Secure Boot.\n"); + printf("\t-h Help.\n"); + printf("\n\n"); + exit(0); + +} + +/*************************************************************************** + * Function : crypto_calculate_checksum() + * Arguments : data - Pointer to FILE + * num - Number of 32 bit words for checksum + * Return : Checksum Value + * Description : Calculate Checksum over the data + ***************************************************************************/ +uint32_t crypto_calculate_checksum(FILE *fp_rcw_pbi_op, uint32_t num) +{ + uint32_t i; + uint64_t sum = 0; + uint32_t word; + + fseek(fp_rcw_pbi_op, 0L, SEEK_SET); + for (i = 0; i < num ; i++) { + if ((fread(&word, sizeof(word), NUM_MEM_BLOCK, fp_rcw_pbi_op)) + < NUM_MEM_BLOCK) { + printf("%s: Error reading word.\n", __func__); + return FAILURE; + } + sum = sum + word; + sum = sum & 0xFFFFFFFF; + } + return (uint32_t)sum; +} + +/*************************************************************************** + * Function : add_pbi_stop_cmd + * Arguments : fp_rcw_pbi_op - output rcw_pbi file pointer + * Return : SUCCESS or FAILURE + * Description : This function insert pbi stop command. + ***************************************************************************/ +int add_pbi_stop_cmd(FILE *fp_rcw_pbi_op, enum stop_command flag) +{ + int ret = FAILURE; + int32_t pbi_stop_cmd; + uint32_t pbi_crc = 0xffffffff, i, j, c; + uint32_t crc_table[MAX_CRC_ENTRIES]; + uint8_t data; + + switch (pblimg.chassis) { + case CHASSIS_2: + pbi_stop_cmd = BYTE_SWAP_32(CRC_STOP_CMD_ARM); + break; + case CHASSIS_3: + case CHASSIS_3_2: + /* Based on flag add the corresponsding cmd + * -- stop cmd or stop with CRC cmd + */ + if (flag == CRC_STOP_COMMAND) { + pbi_stop_cmd = CRC_STOP_CMD_ARM_CH3; + } else { + pbi_stop_cmd = STOP_CMD_ARM_CH3; + } + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + default: + printf("Internal Error: Invalid Chassis val = %d.\n", + pblimg.chassis); + goto pbi_stop_err; + } + + if (fwrite(&pbi_stop_cmd, sizeof(pbi_stop_cmd), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI STOP CMD\n", __func__); + goto pbi_stop_err; + } + + if (flag == CRC_STOP_COMMAND) { + for (i = 0; i < MAX_CRC_ENTRIES; i++) { + c = i << 24; + for (j = 0; j < 8; j++) { + c = (c & 0x80000000) ? + PBI_CRC_POLYNOMIAL ^ (c << 1) : c << 1; + } + + crc_table[i] = c; + } + } + + switch (pblimg.chassis) { + case CHASSIS_2: + /* Chassis 2: CRC is calculated on RCW + PBL cmd.*/ + fseek(fp_rcw_pbi_op, 0L, SEEK_SET); + break; + case CHASSIS_3: + case CHASSIS_3_2: + /* Chassis 3: CRC is calculated on PBL cmd only. */ + fseek(fp_rcw_pbi_op, CHS3_CRC_PAYLOAD_START_OFFSET, SEEK_SET); + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + printf("%s: Unknown Chassis.\n", __func__); + goto pbi_stop_err; + } + + while ((fread(&data, sizeof(data), NUM_MEM_BLOCK, fp_rcw_pbi_op)) + == NUM_MEM_BLOCK) { + if (flag == CRC_STOP_COMMAND) { + if (pblimg.chassis == CHASSIS_2) { + pbi_crc = crc_table + [((pbi_crc >> 24) ^ (data)) & 0xff] ^ + (pbi_crc << 8); + } else { + pbi_crc = (pbi_crc >> 8) ^ + crc32_lookup[((pbi_crc) ^ + (data)) & 0xff]; + } + } + } + + switch (pblimg.chassis) { + case CHASSIS_2: + pbi_crc = BYTE_SWAP_32(pbi_crc); + break; + case CHASSIS_3: + case CHASSIS_3_2: + if (flag == CRC_STOP_COMMAND) { + pbi_crc = pbi_crc ^ 0xFFFFFFFF; + } else { + pbi_crc = 0x00000000; + } + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + printf("%s: Unknown Chassis.\n", __func__); + goto pbi_stop_err; + } + + if (fwrite(&pbi_crc, sizeof(pbi_crc), NUM_MEM_BLOCK, fp_rcw_pbi_op) + != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI PBI CRC\n", __func__); + goto pbi_stop_err; + } + ret = SUCCESS; + +pbi_stop_err: + return ret; +} + +/* + * Returns: + * File size in bytes, on Success. + * FAILURE, on failure. + */ +int get_filesize(const char *c) +{ + FILE *fp; + int ret = FAILURE; + + fp = fopen(c, "rb"); + if (fp == NULL) { + fprintf(stderr, "%s: Error in opening the file: %s\n", + __func__, c); + goto filesize_err; + } + + fseek(fp, 0L, SEEK_END); + ret = ftell(fp); + fclose(fp); + +filesize_err: + return ret; +} + +/*************************************************************************** + * Function : get_bootptr + * Arguments : fp_rcw_pbi_op - Pointer to output file + * Return : SUCCESS or FAILURE + * Description : Add bootptr pbi command to output file + ***************************************************************************/ +int add_boot_ptr_cmd(FILE *fp_rcw_pbi_op) +{ + uint32_t bootptr_addr; + int ret = FAILURE; + + switch (pblimg.chassis) { + case CHASSIS_2: + if (sb_flag == true) + bootptr_addr = BYTE_SWAP_32(CSF_ADDR_SB); + else + bootptr_addr = BYTE_SWAP_32(BOOTPTR_ADDR); + pblimg.ep = BYTE_SWAP_32(pblimg.ep); + break; + case CHASSIS_3: + case CHASSIS_3_2: + if (sb_flag == true) + bootptr_addr = CSF_ADDR_SB_CH3; + else + bootptr_addr = BOOTPTR_ADDR_CH3; + break; + case CHASSIS_UNKNOWN: + case CHASSIS_MAX: + default: + printf("Internal Error: Invalid Chassis val = %d.\n", + pblimg.chassis); + goto bootptr_err; + } + + if (fwrite(&bootptr_addr, sizeof(bootptr_addr), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI Words:[%d].\n", + __func__, ret); + goto bootptr_err; + } + + if (pblimg.ep != 0) { + if (fwrite(&pblimg.ep, sizeof(pblimg.ep), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in Writing PBI Words\n", __func__); + goto bootptr_err; + } + } + + printf("\nBoot Location Pointer= 0x%x\n", + pblimg.chassis == CHASSIS_2 ? BYTE_SWAP_32(pblimg.ep) : + pblimg.ep); + ret = SUCCESS; + +bootptr_err: + return ret; +} + +/*************************************************************************** + * Function : add_blk_cpy_cmd + * Arguments : pbi_word - pointer to pbi commands + * args - Command line args flag. + * Return : SUCCESS or FAILURE + * Description : Add pbi commands for block copy cmd in pbi_words + ***************************************************************************/ +int add_blk_cpy_cmd(FILE *fp_rcw_pbi_op, uint16_t args) +{ + uint32_t blk_cpy_hdr; + uint32_t file_size, new_file_size; + uint32_t align = 4; + int ret = FAILURE; + int num_pad_bytes = 0; + + if ((args & BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK) == 0) { + printf("ERROR: Offset not specified for Block Copy Cmd.\n"); + printf("\tSee Usage and use -f option\n"); + goto blk_copy_err; + } + + switch (pblimg.chassis) { + case CHASSIS_3: + /* Block copy command */ + blk_cpy_hdr = blk_cpy_hdr_map_ch3[pblimg.boot_src]; + pblimg.src_addr += base_addr_ch3[pblimg.boot_src]; + break; + case CHASSIS_3_2: + /* Block copy command */ + blk_cpy_hdr = blk_cpy_hdr_map_ch32[pblimg.boot_src]; + pblimg.src_addr += base_addr_ch32[pblimg.boot_src]; + break; + default: + printf("%s: Error invalid chassis type for this command.\n", + __func__); + goto blk_copy_err; + } + + file_size = get_filesize(pblimg.sec_imgnm); + if (file_size > 0) { + new_file_size = (file_size + (file_size % align)); + + /* Add Block copy command */ + if (fwrite(&blk_cpy_hdr, sizeof(blk_cpy_hdr), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing blk_cpy_hdr to the file.\n", + __func__); + goto blk_copy_err; + } + + if ((args & BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK) == 0) + num_pad_bytes = pblimg.src_addr % 4; + + /* Add Src address word */ + if (fwrite(&pblimg.src_addr + num_pad_bytes, + sizeof(pblimg.src_addr), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing BLK SRC Addr to the file.\n", + __func__); + goto blk_copy_err; + } + + /* Add Dest address word */ + if (fwrite(&pblimg.addr, sizeof(pblimg.addr), + NUM_MEM_BLOCK, fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing DST Addr to the file.\n", + __func__); + goto blk_copy_err; + } + + /* Add size */ + if (fwrite(&new_file_size, sizeof(new_file_size), + NUM_MEM_BLOCK, fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing size to the file.\n", + __func__); + goto blk_copy_err; + } + } + + ret = SUCCESS; + +blk_copy_err: + return ret; +} + +/*************************************************************************** + * Function : add_cpy_cmd + * Arguments : pbi_word - pointer to pbi commands + * Return : SUCCESS or FAILURE + * Description : Append pbi commands for copying BL2 image to the + * load address stored in pbl_image.addr + ***************************************************************************/ +int add_cpy_cmd(FILE *fp_rcw_pbi_op) +{ + uint32_t ALTCBAR_ADDRESS = BYTE_SWAP_32(0x09570158); + uint32_t WAIT_CMD_WRITE_ADDRESS = BYTE_SWAP_32(0x096100c0); + uint32_t WAIT_CMD = BYTE_SWAP_32(0x000FFFFF); + int file_size; + uint32_t pbi_cmd, altcbar; + uint8_t pbi_data[MAX_PBI_DATA_LEN_BYTE]; + uint32_t dst_offset; + FILE *fp_img = NULL; + int ret = FAILURE; + + altcbar = pblimg.addr; + dst_offset = pblimg.addr; + fp_img = fopen(pblimg.sec_imgnm, "rb"); + if (fp_img == NULL) { + printf("%s: Error in opening the file: %s\n", __func__, + pblimg.sec_imgnm); + goto add_cpy_err; + } + file_size = get_filesize(pblimg.sec_imgnm); + altcbar = 0xfff00000 & altcbar; + altcbar = BYTE_SWAP_32(altcbar >> 16); + if (fwrite(&ALTCBAR_ADDRESS, sizeof(ALTCBAR_ADDRESS), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in writing address of ALTCFG CMD.\n", + __func__); + goto add_cpy_err; + } + if (fwrite(&altcbar, sizeof(altcbar), NUM_MEM_BLOCK, fp_rcw_pbi_op) + != NUM_MEM_BLOCK) { + printf("%s: Error in writing ALTCFG CMD.\n", __func__); + goto add_cpy_err; + } + if (fwrite(&WAIT_CMD_WRITE_ADDRESS, sizeof(WAIT_CMD_WRITE_ADDRESS), + NUM_MEM_BLOCK, fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error in writing address of WAIT_CMD.\n", + __func__); + goto add_cpy_err; + } + if (fwrite(&WAIT_CMD, sizeof(WAIT_CMD), NUM_MEM_BLOCK, fp_rcw_pbi_op) + != NUM_MEM_BLOCK) { + printf("%s: Error in writing WAIT_CMD.\n", __func__); + goto add_cpy_err; + } + do { + memset(pbi_data, 0, MAX_PBI_DATA_LEN_BYTE); + + ret = fread(&pbi_data, MAX_PBI_DATA_LEN_BYTE, + NUM_MEM_BLOCK, fp_img); + if ((ret != NUM_MEM_BLOCK) && (!feof(fp_img))) { + printf("%s: Error writing ALTCFG Word: [%d].\n", + __func__, ret); + goto add_cpy_err; + } + + dst_offset &= OFFSET_MASK; + pbi_cmd = WRITE_CMD_BASE | dst_offset; + pbi_cmd = BYTE_SWAP_32(pbi_cmd); + if (fwrite(&pbi_cmd, sizeof(pbi_cmd), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing ALTCFG Word write cmd.\n", + __func__); + goto add_cpy_err; + } + if (fwrite(&pbi_data, MAX_PBI_DATA_LEN_BYTE, NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: Error writing ALTCFG_Word.\n", __func__); + goto add_cpy_err; + } + dst_offset += MAX_PBI_DATA_LEN_BYTE; + file_size -= MAX_PBI_DATA_LEN_BYTE; + } while (!feof(fp_img)); + + ret = SUCCESS; + +add_cpy_err: + if (fp_img != NULL) { + fclose(fp_img); + } + return ret; +} + +int main(int argc, char **argv) +{ + FILE *file = NULL; + char *ptr; + int opt; + int tmp; + uint16_t args = ARG_INIT_MASK; + FILE *fp_rcw_pbi_ip = NULL, *fp_rcw_pbi_op = NULL; + uint32_t word, word_1; + int ret = FAILURE; + bool bootptr_flag = false; + enum stop_command flag_stop_cmd = CRC_STOP_COMMAND; + + /* Initializing the global structure to zero. */ + memset(&pblimg, 0x0, sizeof(struct pbl_image)); + + while ((opt = getopt(argc, argv, + ":b:f:r:i:e:d:c:o:h:s")) != -1) { + switch (opt) { + case 'd': + pblimg.addr = strtoull(optarg, &ptr, 16); + if (*ptr != 0) { + fprintf(stderr, "CMD Error: invalid load or destination address %s\n", optarg); + goto exit_main; + } + args |= BL2_BIN_CPY_DEST_ADDR_ARG_MASK; + break; + case 'r': + pblimg.rcw_nm = optarg; + file = fopen(pblimg.rcw_nm, "r"); + if (file == NULL) { + printf("CMD Error: Opening the RCW File.\n"); + goto exit_main; + } else { + args |= RCW_FILE_NAME_ARG_MASK; + fclose(file); + } + break; + case 'e': + bootptr_flag = true; + pblimg.ep = strtoull(optarg, &ptr, 16); + if (*ptr != 0) { + fprintf(stderr, + "CMD Error: Invalid entry point %s\n", optarg); + goto exit_main; + } + break; + case 'h': + print_usage(); + break; + case 'i': + pblimg.sec_imgnm = optarg; + file = fopen(pblimg.sec_imgnm, "r"); + if (file == NULL) { + printf("CMD Error: Opening Input file.\n"); + goto exit_main; + } else { + args |= IN_FILE_NAME_ARG_MASK; + fclose(file); + } + break; + case 'c': + tmp = atoi(optarg); + switch (tmp) { + case SOC_LS1012: + case SOC_LS1023: + case SOC_LS1026: + case SOC_LS1043: + case SOC_LS1046: + pblimg.chassis = CHASSIS_2; + break; + case SOC_LS1088: + case SOC_LS2080: + case SOC_LS2088: + pblimg.chassis = CHASSIS_3; + break; + case SOC_LS1028: + case SOC_LX2160: + pblimg.chassis = CHASSIS_3_2; + break; + default: + printf("CMD Error: Invalid SoC Val = %d.\n", tmp); + goto exit_main; + } + + args |= CHASSIS_ARG_MASK; + break; + case 'o': + pblimg.imagefile = optarg; + args |= OP_FILE_NAME_ARG_MASK; + break; + case 's': + sb_flag = true; + break; + case 'b': + if (strcmp(optarg, "qspi") == 0) { + pblimg.boot_src = QSPI_BOOT; + } else if (strcmp(optarg, "nor") == 0) { + pblimg.boot_src = IFC_NOR_BOOT; + } else if (strcmp(optarg, "nand") == 0) { + pblimg.boot_src = IFC_NAND_BOOT; + } else if (strcmp(optarg, "sd") == 0) { + pblimg.boot_src = SD_BOOT; + } else if (strcmp(optarg, "emmc") == 0) { + pblimg.boot_src = EMMC_BOOT; + } else if (strcmp(optarg, "flexspi_nor") == 0) { + pblimg.boot_src = FLXSPI_NOR_BOOT; + } else if (strcmp(optarg, "flexspi_nand") == 0) { + pblimg.boot_src = FLXSPI_NAND_BOOT; + } else if (strcmp(optarg, "flexspi_nand2k") == 0) { + pblimg.boot_src = FLXSPI_NAND4K_BOOT; + } else { + printf("CMD Error: Invalid boot source.\n"); + goto exit_main; + } + args |= BOOT_SRC_ARG_MASK; + break; + case 'f': + pblimg.src_addr = strtoull(optarg, &ptr, 16); + if (*ptr != 0) { + fprintf(stderr, + "CMD Error: Invalid src offset %s\n", optarg); + goto exit_main; + } + args |= BL2_BIN_STRG_LOC_BOOT_SRC_ARG_MASK; + break; + default: + /* issue a warning and skip the unknown arg */ + printf("Cmd Warning: Invalid Arg = %c.\n", opt); + } + } + + if ((args & MAND_ARG_MASK) != MAND_ARG_MASK + || pblimg.rcw_nm == NULL + || pblimg.imagefile == NULL) { + print_usage(); + } + + fp_rcw_pbi_ip = fopen(pblimg.rcw_nm, "rb"); + if (fp_rcw_pbi_ip == NULL) { + printf("%s: Error in opening the rcw file: %s\n", + __func__, pblimg.rcw_nm); + goto exit_main; + } + + fp_rcw_pbi_op = fopen(pblimg.imagefile, "wb+"); + if (fp_rcw_pbi_op == NULL) { + printf("%s: Error opening the input file: %s\n", + __func__, pblimg.imagefile); + goto exit_main; + } + + printf("\nInput Boot Source: %s\n", boot_src_string[pblimg.boot_src]); + printf("Input RCW File: %s\n", pblimg.rcw_nm); + printf("Input BL2 Binary File: %s\n", pblimg.sec_imgnm); + printf("Input load address for BL2 Binary File: 0x%x\n", pblimg.addr); + + printf("Chassis Type: %d\n", pblimg.chassis); + switch (pblimg.chassis) { + case CHASSIS_2: + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, fp_rcw_pbi_ip) + != NUM_MEM_BLOCK) { + printf("%s: Error in reading word from the rcw file.\n", + __func__); + goto exit_main; + } + while (BYTE_SWAP_32(word) != 0x08610040) { + if (BYTE_SWAP_32(word) == 0x09550000 + || BYTE_SWAP_32(word) == 0x000f400c) { + break; + } + if (fwrite(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: [CH2] Error in Writing PBI Words\n", + __func__); + goto exit_main; + } + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_ip) != NUM_MEM_BLOCK) { + printf("%s: [CH2] Error in Reading PBI Words\n", + __func__); + goto exit_main; + } + } + + if (bootptr_flag == true) { + /* Add command to set boot_loc ptr */ + ret = add_boot_ptr_cmd(fp_rcw_pbi_op); + if (ret != SUCCESS) { + goto exit_main; + } + } + + /* Write acs write commands to output file */ + ret = add_cpy_cmd(fp_rcw_pbi_op); + if (ret != SUCCESS) { + goto exit_main; + } + + /* Add stop command after adding pbi commands + * For Chasis 2.0 platforms it is always CRC & + * Stop command + */ + flag_stop_cmd = CRC_STOP_COMMAND; + ret = add_pbi_stop_cmd(fp_rcw_pbi_op, flag_stop_cmd); + if (ret != SUCCESS) { + goto exit_main; + } + + break; + + case CHASSIS_3: + case CHASSIS_3_2: + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, fp_rcw_pbi_ip) + != NUM_MEM_BLOCK) { + printf("%s: Error reading PBI Cmd.\n", __func__); + goto exit_main; + } + while (word != 0x808f0000 && word != 0x80ff0000) { + pbl_size++; + /* 11th words in RCW has PBL length. Update it + * with new length. 2 commands get added + * Block copy + CCSR Write/CSF header write + */ + if (pbl_size == 11) { + word_1 = (word & PBI_LEN_MASK) + + (PBI_LEN_ADD << 20); + word = word & ~PBI_LEN_MASK; + word = word | word_1; + } + /* Update the CRC command */ + /* Check load command.. + * add a check if command is Stop with CRC + * or stop without checksum + */ + if (pbl_size == 35) { + word = crypto_calculate_checksum(fp_rcw_pbi_op, + NUM_RCW_WORD - 1); + if (word == FAILURE) { + goto exit_main; + } + } + if (fwrite(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_op) != NUM_MEM_BLOCK) { + printf("%s: [CH3] Error in Writing PBI Words\n", + __func__); + goto exit_main; + } + if (fread(&word, sizeof(word), NUM_MEM_BLOCK, + fp_rcw_pbi_ip) != NUM_MEM_BLOCK) { + printf("%s: [CH3] Error in Reading PBI Words\n", + __func__); + goto exit_main; + } + + if (word == CRC_STOP_CMD_ARM_CH3) { + flag_stop_cmd = CRC_STOP_COMMAND; + } else if (word == STOP_CMD_ARM_CH3) { + flag_stop_cmd = STOP_COMMAND; + } + } + if (bootptr_flag == true) { + /* Add command to set boot_loc ptr */ + ret = add_boot_ptr_cmd(fp_rcw_pbi_op); + if (ret != SUCCESS) { + printf("%s: add_boot_ptr_cmd return failure.\n", + __func__); + goto exit_main; + } + } + + /* Write acs write commands to output file */ + ret = add_blk_cpy_cmd(fp_rcw_pbi_op, args); + if (ret != SUCCESS) { + printf("%s: Function add_blk_cpy_cmd return failure.\n", + __func__); + goto exit_main; + } + + /* Add stop command after adding pbi commands */ + ret = add_pbi_stop_cmd(fp_rcw_pbi_op, flag_stop_cmd); + if (ret != SUCCESS) { + goto exit_main; + } + + break; + + default: + printf("%s: Unknown chassis type.\n", + __func__); + } + + if (ret == SUCCESS) { + printf("Output file successfully created with name: %s\n\n", + pblimg.imagefile); + } + +exit_main: + if (fp_rcw_pbi_op != NULL) { + fclose(fp_rcw_pbi_op); + } + if (fp_rcw_pbi_ip != NULL) { + fclose(fp_rcw_pbi_ip); + } + + return ret; +} diff --git a/atf-20250711/tools/nxp/create_pbl/create_pbl.mk b/atf-20250711/tools/nxp/create_pbl/create_pbl.mk new file mode 100644 index 000000000..81f6d13aa --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/create_pbl.mk @@ -0,0 +1,53 @@ +# +# Copyright 2018-2020 NXP +# Copyright (c) 2025, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# + +CREATE_PBL ?= ${CREATE_PBL_TOOL_PATH}/create_pbl$(.exe) +BYTE_SWAP ?= ${CREATE_PBL_TOOL_PATH}/byte_swap$(.exe) + +HOST_GCC := gcc + +#SWAP is required for Chassis 2 platforms - LS102, ls1043 and ls1046 for QSPI +ifeq (${SOC},ls1046a) +SOC_NUM := 1046a +SWAP = 1 +CH = 2 +else ifeq (${SOC},ls1043a) +SOC_NUM := 1043a +SWAP = 1 +CH = 2 +else ifeq (${SOC},ls1012a) +SOC_NUM := 1012a +SWAP = 1 +CH = 2 +else ifeq (${SOC},ls1088a) +SOC_NUM := 1088a +CH = 3 +else ifeq (${SOC},ls2088a) +SOC_NUM := 2088a +CH = 3 +else ifeq (${SOC},lx2160a) +SOC_NUM := 2160a +CH = 3 +else ifeq (${SOC},ls1028a) +SOC_NUM := 1028a +CH = 3 +else +$(error "Check SOC Not defined in create_pbl.mk.") +endif + +ifeq (${CH},2) + +include ${CREATE_PBL_TOOL_PATH}/pbl_ch2.mk + +endif #CH2 + +ifeq (${CH},3) + +include ${CREATE_PBL_TOOL_PATH}/pbl_ch3.mk + +endif #CH3 diff --git a/atf-20250711/tools/nxp/create_pbl/pbl_ch2.mk b/atf-20250711/tools/nxp/create_pbl/pbl_ch2.mk new file mode 100644 index 000000000..47ff8922c --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/pbl_ch2.mk @@ -0,0 +1,58 @@ +# +# Copyright 2020 NXP +# Copyright (c) 2025, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# + +CREATE_PBL ?= ${CREATE_PBL_TOOL_PATH}/create_pbl$(.exe) +BYTE_SWAP ?= ${CREATE_PBL_TOOL_PATH}/byte_swap$(.exe) + +HOST_GCC := gcc + +.PHONY: pbl +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(SECURE_BOOT),yes) +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(RCW),"") + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + # Generate header for bl2.bin + $(q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} + # Compile create_pbl tool + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ + # Add bl2.bin to RCW + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE}\ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl ;\ + # Add header to RCW + ${CREATE_PBL} -r ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -i ${BUILD_PLAT}/hdr_bl2 -b ${BOOT_MODE} -c ${SOC_NUM} \ + -d ${BL2_HDR_LOC} -e ${BL2_HDR_LOC} -o ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl -s;\ + rm ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl +# Swapping of RCW is required for QSPi Chassis 2 devices +ifeq (${BOOT_MODE}, qspi) +ifeq ($(SWAP),1) + $(s)echo "Byteswapping RCW for QSPI" + ${BYTE_SWAP} ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl; +endif # SWAP +endif # BOOT_MODE + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +else # NON SECURE_BOOT +ifeq ($(RCW),"") + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + # -a option appends the image for Chassis 3 devices in case of non secure boot + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE} \ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl ; +# Swapping of RCW is required for QSPi Chassis 2 devices +ifeq (${BOOT_MODE}, qspi) +ifeq ($(SWAP),1) + $(s)echo "Byteswapping RCW for QSPI" + ${BYTE_SWAP} ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl; +endif # SWAP +endif # BOOT_MODE + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +endif # SECURE_BOOT diff --git a/atf-20250711/tools/nxp/create_pbl/pbl_ch3.mk b/atf-20250711/tools/nxp/create_pbl/pbl_ch3.mk new file mode 100644 index 000000000..f5d42cdbe --- /dev/null +++ b/atf-20250711/tools/nxp/create_pbl/pbl_ch3.mk @@ -0,0 +1,72 @@ +# +# Copyright 2018-2022 NXP +# Copyright (c) 2025, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# +SHELL=/bin/bash + +CREATE_PBL ?= ${CREATE_PBL_TOOL_PATH}/create_pbl$(.exe) +BYTE_SWAP ?= ${CREATE_PBL_TOOL_PATH}/byte_swap$(.exe) + +HOST_GCC := gcc + +BL2_SRC_OFFSET ?= 0x9000 +BL2_HDR_SRC_OFFSET ?= 0x5000 +bl2_hdr_loc=$(shell echo $$(( $(BL2_HDR_SRC_OFFSET) / 1024 ))) +bl2_loc=$(shell echo $$(( $(BL2_SRC_OFFSET) / 1024 ))) + +.PHONY: pbl +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(SECURE_BOOT),yes) +pbl: ${BUILD_PLAT}/bl2.bin +ifeq ($(RCW),"") + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + # Generate header for bl2.bin + $(q)$(CST_DIR)/create_hdr_isbc --in ${BUILD_PLAT}/bl2.bin --out ${BUILD_PLAT}/hdr_bl2 ${BL2_INPUT_FILE} + + # Compile create_pbl tool + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH};\ + + # Add Block Copy command for bl2.bin to RCW + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE}\ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -f ${BL2_SRC_OFFSET};\ + + # Add Block Copy command and Load CSF header command to RCW + ${CREATE_PBL} -r ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -i ${BUILD_PLAT}/hdr_bl2 -b ${BOOT_MODE} -c ${SOC_NUM} \ + -d ${BL2_HDR_LOC} -e ${BL2_HDR_LOC} -s -f ${BL2_HDR_SRC_OFFSET} \ + -o ${BUILD_PLAT}/rcw_sec.pbl + + # Sign and add "Load Security Header command to PBI commands + $(q)$(CST_DIR)/create_hdr_pbi --out ${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl --in ${BUILD_PLAT}/rcw_sec.pbl ${PBI_INPUT_FILE} + + # Append the bl2_hdr to the RCW image + $(s)echo "${bl2_hdr_loc}" + dd if=${BUILD_PLAT}/hdr_bl2 of=${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl bs=1K seek=${bl2_hdr_loc} + + # Append the bl2.bin to the RCW image + $(s)echo "${bl2_loc}" + dd if=${BUILD_PLAT}/bl2.bin of=${BUILD_PLAT}/bl2_${BOOT_MODE}_sec.pbl bs=1K seek=${bl2_loc} + + rm ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +else #SECURE_BOOT +ifeq ($(RCW),"") + $(s)echo "Platform ${PLAT} requires rcw file. Please set RCW to point to the right RCW file for boot mode ${BOOT_MODE}" +else + $(q)${MAKE} CPPFLAGS="-DVERSION='\"${VERSION_STRING}\"'" --no-print-directory -C ${CREATE_PBL_TOOL_PATH}; + + # Add Block Copy command and populate boot loc ptrfor bl2.bin to RCW + ${CREATE_PBL} -r ${RCW} -i ${BUILD_PLAT}/bl2.bin -b ${BOOT_MODE} -c ${SOC_NUM} -d ${BL2_BASE} -e ${BL2_BASE} \ + -o ${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl -f ${BL2_SRC_OFFSET}; + + # Append the bl2.bin to the RCW image + $(s)echo "bl2_loc is ${bl2_loc} KB" + dd if=${BUILD_PLAT}/bl2.bin of=${BUILD_PLAT}/bl2_${BOOT_MODE}.pbl bs=1K seek=${bl2_loc} + + cd ${CREATE_PBL_TOOL_PATH}; ${MAKE} clean ; cd -; +endif +endif # SECURE_BOOT diff --git a/atf-20250711/tools/renesas/rcar_layout_create/makefile b/atf-20250711/tools/renesas/rcar_layout_create/makefile new file mode 100644 index 000000000..f89f379e9 --- /dev/null +++ b/atf-20250711/tools/renesas/rcar_layout_create/makefile @@ -0,0 +1,125 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +toolchains := aarch64 + +include ../../../make_helpers/build-rules.mk +include ../../../make_helpers/common.mk +include ../../../make_helpers/toolchain.mk + +################################################### +# makefile +################################################### + +#output file name +FILE_NAME_SA0 = bootparam_sa0 +FILE_NAME_SA6 = cert_header_sa6 + +OUTPUT_FILE_SA0 = $(FILE_NAME_SA0).elf +OUTPUT_FILE_SA6 = $(FILE_NAME_SA6).elf + +#object file name +OBJ_FILE_SA0 = sa0.o +OBJ_FILE_SA6 = sa6.o + +#linker script name +MEMORY_DEF_SA0 = sa0.ld.S +MEMORY_DEF_SA6 = sa6.ld.S + +################################################### +# Convenience function for adding build definitions +# $(eval $(call add_define,FOO)) will have: +# -DFOO if $(FOO) is empty; -DFOO=$(FOO) otherwise +define add_define +DEFINES += -D$(1)$(if $(value $(1)),=$(value $(1)),) +endef + +# Process RCAR_SA0_SIZE flag +ifndef RCAR_SA0_SIZE +RCAR_SA0_SIZE := 1 +else +ifeq (${RCAR_SA0_SIZE},0) +RCAR_SA0_SIZE := 0 +else +RCAR_SA0_SIZE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA0_SIZE)) + +# Process RCAR_SA6_TYPE flag +ifndef RCAR_SA6_TYPE +RCAR_SA6_TYPE := 0 +else +ifeq (${RCAR_SA6_TYPE},0) +RCAR_SA6_TYPE := 0 +else +RCAR_SA6_TYPE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA6_TYPE)) + +# Handle different VMA adjustment on D3 +ifeq (${RCAR_LSI},${RCAR_D3}) +RCAR_VMA_ADJUST_ADDR := 0xE6320000 +else +RCAR_VMA_ADJUST_ADDR := 0xE6312000 +endif +$(eval $(call add_define,RCAR_VMA_ADJUST_ADDR)) + + +################################################### + +#c compiler +CFLAGS += ${DEFINES} +CFLAGS += -I../../include/lib/stdlib + +#clean +CL = rm -f + +################################################### +.SUFFIXES : .s .c .o + +################################################### +# command + +.PHONY: all + +all: $(FILE_NAME_SA0).srec $(FILE_NAME_SA0).bin +all: $(FILE_NAME_SA6).srec $(FILE_NAME_SA6).bin + +################################################### +# Linker +################################################### + +$(FILE_NAME_SA0).srec: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec + +$(FILE_NAME_SA0).bin: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin + +$(OUTPUT_FILE_SA0): $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA0) -nostdlib -static -Wl,--build-id=none -T $(MEMORY_DEF_SA0) -o $(OUTPUT_FILE_SA0) -Wl,-Map $(FILE_NAME_SA0).map + +$(FILE_NAME_SA6).srec: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec + +$(FILE_NAME_SA6).bin: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin + +$(OUTPUT_FILE_SA6): $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA6) -nostdlib -static -Wl,--build-id=none -T $(MEMORY_DEF_SA6) -o $(OUTPUT_FILE_SA6) -Wl,-Map $(FILE_NAME_SA6).map + +################################################### +# Compile +################################################### + +%.o: %.c | $$(@D)/ + $(aarch64-cc) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +.PHONY: clean +clean: + $(CL) *.bin *.map *.srec *.elf *.o diff --git a/atf-20250711/tools/renesas/rcar_layout_create/sa0.c b/atf-20250711/tools/renesas/rcar_layout_create/sa0.c new file mode 100644 index 000000000..79354eca6 --- /dev/null +++ b/atf-20250711/tools/renesas/rcar_layout_create/sa0.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define RCAR_SA0_SIZE_SMALL (0) /* for E3/D3 */ +#define RCAR_SA0_SIZE_NORMAL (1) /* for H3/M3/M3N */ + +#define BL2_ADDRESS (0xE6304000) /* BL2 start address */ + +#if (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) +#define BL2_SIZE (80*1024/4) /* BL2 size is 80KB(0x00005000) */ +#else /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ +#define BL2_SIZE (170*1024/4) /* BL2 size is 170KB(0x0000AA00) */ +#endif /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ + +/* SA0 */ +/* 0x00000000 */ +const unsigned int __attribute__ ((section (".sa0_bootrom"))) bootrom_paramA = 0x00000100; +/* 0x00000080 (Map Type 3 for eMMC Boot)*/ +/* 0x000001D4 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_addr3"))) bl2dst_addr3 = BL2_ADDRESS; +/* 0x000002E4 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_size3"))) bl2dst_size3 = BL2_SIZE; +/* 0x00000C00 (Map Type 1 for HyperFlash/QSPI Flash Boot)*/ +/* 0x00000D54 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_addr1"))) bl2dst_addr1 = BL2_ADDRESS; +/* 0x00000E64 */ +const unsigned int __attribute__ ((section (".sa0_bl2dst_size1"))) bl2dst_size1 = BL2_SIZE; diff --git a/atf-20250711/tools/renesas/rcar_layout_create/sa0.ld.S b/atf-20250711/tools/renesas/rcar_layout_create/sa0.ld.S new file mode 100644 index 000000000..98fee23af --- /dev/null +++ b/atf-20250711/tools/renesas/rcar_layout_create/sa0.ld.S @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa0_bootrom)) + /* Map Type 3 for eMMC Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x000001D4; /* H'00000080 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr3)) + /* A-side IPL content cert "Size" */ + . = 0x000002E4; /* H'00000080 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size3)) + /* Map Type 1 for HyperFlash/QSPI Flash Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x00000D54; /* H'00000C00 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr1)) + /* A-side IPL content cert "Size" */ + . = 0x00000E64; /* H'00000C00 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size1)) + } + +} diff --git a/atf-20250711/tools/renesas/rcar_layout_create/sa6.c b/atf-20250711/tools/renesas/rcar_layout_create/sa6.c new file mode 100644 index 000000000..58881f971 --- /dev/null +++ b/atf-20250711/tools/renesas/rcar_layout_create/sa6.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2015-2023, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#define RCAR_SA6_TYPE_HYPERFLASH (0) +#define RCAR_SA6_TYPE_EMMC (1) + +#if (RCAR_SA6_TYPE == RCAR_SA6_TYPE_HYPERFLASH) + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on flash for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x001C0000U) +/* Reserved */ +#define RCAR_BL31_PARTITION (0x00000000U) +/* Source address on flash for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Reserved */ +#define RCAR_BL32_PARTITION (0x00000000U) +/* Source address on flash for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00640000U) +/* Reserved */ +#define RCAR_BL33_PARTITION (0x00000000U) +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL332_PARTITION (0x00000000U) +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL333_PARTITION (0x00000000U) +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL334_PARTITION (0x00000000U) +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL335_PARTITION (0x00000000U) +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL336_PARTITION (0x00000000U) +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL337_PARTITION (0x00000000U) +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL338_PARTITION (0x00000000U) + +#else /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_HYPERFLASH */ + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on eMMC for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x00040000U) +/* Source partition on eMMC for BL31 */ +#define RCAR_BL31_PARTITION (0x00000001U) +/* Source address on eMMC for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Source partition on eMMC for BL32 */ +#define RCAR_BL32_PARTITION (0x00000001U) +/* Source address on eMMC for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00000000U) +/* Source partition on eMMC for BL33 */ +#define RCAR_BL33_PARTITION (0x00000002U) +/* Reserved */ +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +#define RCAR_BL332_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +#define RCAR_BL333_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +#define RCAR_BL334_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +#define RCAR_BL335_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +#define RCAR_BL336_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +#define RCAR_BL337_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +#define RCAR_BL338_PARTITION (0x00000000U) + +#endif /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_HYPERFLASH */ + +/* Destination address for BL31 */ +#define RCAR_BL31DST_ADDRESS (0x44000000U) +#define RCAR_BL31DST_ADDRESSH (0x00000000U) +/* Destination size for BL31 */ +#define RCAR_BL31DST_SIZE (0x0000F800U) +/* Destination address for BL32 */ +#define RCAR_BL32DST_ADDRESS (0x44100000U) +#define RCAR_BL32DST_ADDRESSH (0x00000000U) +/* Destination size for BL32 */ +#define RCAR_BL32DST_SIZE (0x00080000U) +/* Destination address for BL33 */ +#define RCAR_BL33DST_ADDRESS (0x50000000U) +#define RCAR_BL33DST_ADDRESSH (0x00000000U) +/* Destination size for BL33 */ +#define RCAR_BL33DST_SIZE (0x00040000U) +/* Reserved */ +#define RCAR_BL332DST_ADDRESS (0x00000000U) +#define RCAR_BL332DST_ADDRESSH (0x00000000U) +#define RCAR_BL332DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL333DST_ADDRESS (0x00000000U) +#define RCAR_BL333DST_ADDRESSH (0x00000000U) +#define RCAR_BL333DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL334DST_ADDRESS (0x00000000U) +#define RCAR_BL334DST_ADDRESSH (0x00000000U) +#define RCAR_BL334DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL335DST_ADDRESS (0x00000000U) +#define RCAR_BL335DST_ADDRESSH (0x00000000U) +#define RCAR_BL335DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL336DST_ADDRESS (0x00000000U) +#define RCAR_BL336DST_ADDRESSH (0x00000000U) +#define RCAR_BL336DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL337DST_ADDRESS (0x00000000U) +#define RCAR_BL337DST_ADDRESSH (0x00000000U) +#define RCAR_BL337DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL338DST_ADDRESS (0x00000000U) +#define RCAR_BL338DST_ADDRESSH (0x00000000U) +#define RCAR_BL338DST_SIZE (0x00000000U) + +/* SA6 */ +const uint64_t __attribute__ ((section (".sa6_image_num"))) image_num = RCAR_IMAGE_NUM; +const uint64_t __attribute__ ((section (".sa6_bl31src_addr"))) bl31src_addr = RCAR_BL31SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl31partition"))) bl31partition = RCAR_BL31_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl32src_addr"))) bl32src_addr = RCAR_BL32SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl32partition"))) bl32partition = RCAR_BL32_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl33src_addr"))) bl33src_addr = RCAR_BL33SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl33partition"))) bl33partition = RCAR_BL33_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl332src_addr"))) bl332src_addr = RCAR_BL332SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl332partition")))bl332partition = RCAR_BL332_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl333src_addr"))) bl333src_addr = RCAR_BL333SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl333partition")))bl333partition = RCAR_BL333_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl334src_addr"))) bl334src_addr = RCAR_BL334SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl334partition")))bl334partition = RCAR_BL334_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl335src_addr"))) bl335src_addr = RCAR_BL335SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl335partition")))bl335partition = RCAR_BL335_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl336src_addr"))) bl336src_addr = RCAR_BL336SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl336partition")))bl336partition = RCAR_BL336_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl337src_addr"))) bl337src_addr = RCAR_BL337SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl337partition")))bl337partition = RCAR_BL337_PARTITION; +const uint64_t __attribute__ ((section (".sa6_bl338src_addr"))) bl338src_addr = RCAR_BL338SRC_ADDRESS; +const uint64_t __attribute__ ((section (".sa6_bl338partition")))bl338partition = RCAR_BL338_PARTITION; +const uint32_t __attribute__ ((section (".sa6_bl31dst_addr"))) bl31dst_addr = RCAR_BL31DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl31dst_addrh"))) bl31dst_addrh = RCAR_BL31DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl31dst_size"))) bl31dst_size = RCAR_BL31DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl32dst_addr"))) bl32dst_addr = RCAR_BL32DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl32dst_addrh"))) bl32dst_addrh = RCAR_BL32DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl32dst_size"))) bl32dst_size = RCAR_BL32DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl33dst_addr"))) bl33dst_addr = RCAR_BL33DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl33dst_addrh"))) bl33dst_addrh = RCAR_BL33DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl33dst_size"))) bl33dst_size = RCAR_BL33DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl332dst_addr"))) bl332dst_addr = RCAR_BL332DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl332dst_addrh")))bl332dst_addrh = RCAR_BL332DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl332dst_size"))) bl332dst_size = RCAR_BL332DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl333dst_addr"))) bl333dst_addr = RCAR_BL333DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl333dst_addrh")))bl333dst_addrh = RCAR_BL333DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl333dst_size"))) bl333dst_size = RCAR_BL333DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl334dst_addr"))) bl334dst_addr = RCAR_BL334DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl334dst_addrh")))bl334dst_addrh = RCAR_BL334DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl334dst_size"))) bl334dst_size = RCAR_BL334DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl335dst_addr"))) bl335dst_addr = RCAR_BL335DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl335dst_addrh")))bl335dst_addrh = RCAR_BL335DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl335dst_size"))) bl335dst_size = RCAR_BL335DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl336dst_addr"))) bl336dst_addr = RCAR_BL336DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl336dst_addrh")))bl336dst_addrh = RCAR_BL336DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl336dst_size"))) bl336dst_size = RCAR_BL336DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl337dst_addr"))) bl337dst_addr = RCAR_BL337DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl337dst_addrh")))bl337dst_addrh = RCAR_BL337DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl337dst_size"))) bl337dst_size = RCAR_BL337DST_SIZE; +const uint32_t __attribute__ ((section (".sa6_bl338dst_addr"))) bl338dst_addr = RCAR_BL338DST_ADDRESS; +const uint32_t __attribute__ ((section (".sa6_bl338dst_addrh")))bl338dst_addrh = RCAR_BL338DST_ADDRESSH; +const uint32_t __attribute__ ((section (".sa6_bl338dst_size"))) bl338dst_size = RCAR_BL338DST_SIZE; diff --git a/atf-20250711/tools/renesas/rcar_layout_create/sa6.ld.S b/atf-20250711/tools/renesas/rcar_layout_create/sa6.ld.S new file mode 100644 index 000000000..9ca0c1d08 --- /dev/null +++ b/atf-20250711/tools/renesas/rcar_layout_create/sa6.ld.S @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa6_image_num)) + . = 0x00000008; + KEEP(*(.sa6_bl31src_addr)) + . = 0x00000010; + KEEP(*(.sa6_bl31partition)) + . = 0x00000018; + KEEP(*(.sa6_bl32src_addr)) + . = 0x00000020; + KEEP(*(.sa6_bl32partition)) + . = 0x00000028; + KEEP(*(.sa6_bl33src_addr)) + . = 0x00000030; + KEEP(*(.sa6_bl33partition)) + . = 0x00000038; + KEEP(*(.sa6_bl332src_addr)) + . = 0x00000040; + KEEP(*(.sa6_bl332partition)) + . = 0x00000048; + KEEP(*(.sa6_bl333src_addr)) + . = 0x00000050; + KEEP(*(.sa6_bl333partition)) + . = 0x00000058; + KEEP(*(.sa6_bl334src_addr)) + . = 0x00000060; + KEEP(*(.sa6_bl334partition)) + . = 0x00000068; + KEEP(*(.sa6_bl335src_addr)) + . = 0x00000070; + KEEP(*(.sa6_bl335partition)) + . = 0x00000078; + KEEP(*(.sa6_bl336src_addr)) + . = 0x00000080; + KEEP(*(.sa6_bl336partition)) + . = 0x00000088; + KEEP(*(.sa6_bl337src_addr)) + . = 0x00000090; + KEEP(*(.sa6_bl337partition)) + . = 0x00000098; + KEEP(*(.sa6_bl338src_addr)) + . = 0x000000A0; + KEEP(*(.sa6_bl338partition)) + . = 0x00000554; + KEEP(*(.sa6_bl31dst_addr)) + . = 0x00000558; + KEEP(*(.sa6_bl31dst_addrh)) + . = 0x00000664; + KEEP(*(.sa6_bl31dst_size)) + . = 0x00000D54; + KEEP(*(.sa6_bl32dst_addr)) + . = 0x00000D58; + KEEP(*(.sa6_bl32dst_addrh)) + . = 0x00000E64; + KEEP(*(.sa6_bl32dst_size)) + . = 0x00001554; + KEEP(*(.sa6_bl33dst_addr)) + . = 0x00001558; + KEEP(*(.sa6_bl33dst_addrh)) + . = 0x00001664; + KEEP(*(.sa6_bl33dst_size)) + . = 0x00001D54; + KEEP(*(.sa6_bl332dst_addr)) + . = 0x00001D58; + KEEP(*(.sa6_bl332dst_addrh)) + . = 0x00001E64; + KEEP(*(.sa6_bl332dst_size)) + . = 0x00002554; + KEEP(*(.sa6_bl333dst_addr)) + . = 0x00002558; + KEEP(*(.sa6_bl333dst_addrh)) + . = 0x00002664; + KEEP(*(.sa6_bl333dst_size)) + . = 0x00002D54; + KEEP(*(.sa6_bl334dst_addr)) + . = 0x00002D58; + KEEP(*(.sa6_bl334dst_addrh)) + . = 0x00002E64; + KEEP(*(.sa6_bl334dst_size)) + . = 0x00003554; + KEEP(*(.sa6_bl335dst_addr)) + . = 0x00003558; + KEEP(*(.sa6_bl335dst_addrh)) + . = 0x00003664; + KEEP(*(.sa6_bl335dst_size)) + . = 0x00003D54; + KEEP(*(.sa6_bl336dst_addr)) + . = 0x00003D58; + KEEP(*(.sa6_bl336dst_addrh)) + . = 0x00003E64; + KEEP(*(.sa6_bl336dst_size)) + . = 0x00004554; + KEEP(*(.sa6_bl337dst_addr)) + . = 0x00004558; + KEEP(*(.sa6_bl337dst_addrh)) + . = 0x00004664; + KEEP(*(.sa6_bl337dst_size)) + . = 0x00004D54; + KEEP(*(.sa6_bl338dst_addr)) + . = 0x00004D58; + KEEP(*(.sa6_bl338dst_addrh)) + . = 0x00004E64; + KEEP(*(.sa6_bl338dst_size)) + } + +} diff --git a/atf-20250711/tools/renesas/rzg_layout_create/makefile b/atf-20250711/tools/renesas/rzg_layout_create/makefile new file mode 100644 index 000000000..936420dbc --- /dev/null +++ b/atf-20250711/tools/renesas/rzg_layout_create/makefile @@ -0,0 +1,122 @@ +# +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +toolchains := aarch64 + +include ../../../make_helpers/build-rules.mk +include ../../../make_helpers/common.mk +include ../../../make_helpers/toolchain.mk + +################################################### +# makefile +################################################### + +#output file name +FILE_NAME_SA0 = bootparam_sa0 +FILE_NAME_SA6 = cert_header_sa6 + +OUTPUT_FILE_SA0 = $(FILE_NAME_SA0).elf +OUTPUT_FILE_SA6 = $(FILE_NAME_SA6).elf + +#object file name +OBJ_FILE_SA0 = sa0.o +OBJ_FILE_SA6 = sa6.o + +#linker script name +MEMORY_DEF_SA0 = sa0.ld.S +MEMORY_DEF_SA6 = sa6.ld.S + +################################################### +# Convenience function for adding build definitions +# $(eval $(call add_define,FOO)) will have: +# -DFOO if $(FOO) is empty; -DFOO=$(FOO) otherwise +define add_define +DEFINES += -D$(1)$(if $(value $(1)),=$(value $(1)),) +endef + +# Process RCAR_SA0_SIZE flag +ifndef RCAR_SA0_SIZE +RCAR_SA0_SIZE := 1 +else +ifeq (${RCAR_SA0_SIZE},0) +RCAR_SA0_SIZE := 0 +else +RCAR_SA0_SIZE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA0_SIZE)) + +# Process RCAR_SA6_TYPE flag +ifndef RCAR_SA6_TYPE +RCAR_SA6_TYPE := 0 +else +ifeq (${RCAR_SA6_TYPE},0) +RCAR_SA6_TYPE := 0 +else +RCAR_SA6_TYPE := 1 +endif +endif +$(eval $(call add_define,RCAR_SA6_TYPE)) + +RCAR_VMA_ADJUST_ADDR := 0xE6320000 +$(eval $(call add_define,RCAR_VMA_ADJUST_ADDR)) + + +################################################### + +#c compiler +CFLAGS += ${DEFINES} +CFLAGS += -nostdinc \ + -I../../../include/lib/libc \ + -I../../../include/lib/libc/aarch64 + +#clean +CL = rm -f + +################################################### +.SUFFIXES : .s .c .o + +################################################### +# command + +.PHONY: all + +all: $(FILE_NAME_SA0).srec $(FILE_NAME_SA0).bin +all: $(FILE_NAME_SA6).srec $(FILE_NAME_SA6).bin + +################################################### +# Linker +################################################### + +$(FILE_NAME_SA0).srec: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).srec + +$(FILE_NAME_SA0).bin: $(OUTPUT_FILE_SA0) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA0) $(FILE_NAME_SA0).bin + +$(OUTPUT_FILE_SA0): $(MEMORY_DEF_SA0) $(OBJ_FILE_SA0) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA0) -nostdlib -T $(MEMORY_DEF_SA0) -o $(OUTPUT_FILE_SA0) -Wl,-Map $(FILE_NAME_SA0).map + +$(FILE_NAME_SA6).srec: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O srec --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).srec + +$(FILE_NAME_SA6).bin: $(OUTPUT_FILE_SA6) | $$(@D)/ + $(aarch64-oc) -O binary --adjust-vma=$(RCAR_VMA_ADJUST_ADDR) --srec-forceS3 $(OUTPUT_FILE_SA6) $(FILE_NAME_SA6).bin + +$(OUTPUT_FILE_SA6): $(MEMORY_DEF_SA6) $(OBJ_FILE_SA6) | $$(@D)/ + $(aarch64-ld) $(OBJ_FILE_SA6) -nostdlib -T $(MEMORY_DEF_SA6) -o $(OUTPUT_FILE_SA6) -Wl,-Map $(FILE_NAME_SA6).map + +################################################### +# Compile +################################################### + +%.o: %.c | $$(@D)/ + $(aarch64-cc) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +.PHONY: clean +clean: + $(CL) *.bin *.map *.srec *.elf *.o diff --git a/atf-20250711/tools/renesas/rzg_layout_create/sa0.c b/atf-20250711/tools/renesas/rzg_layout_create/sa0.c new file mode 100644 index 000000000..763d3a536 --- /dev/null +++ b/atf-20250711/tools/renesas/rzg_layout_create/sa0.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define RCAR_SA0_SIZE_SMALL (0) /* for RZ/G2E */ +#define RCAR_SA0_SIZE_NORMAL (1) /* for RZ/G2[HMN] */ + +#define BL2_ADDRESS (0xE6304000) /* BL2 start address */ + +#if (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) +#define BL2_SIZE (80*1024/4) /* BL2 size is 80KB(0x00005000) */ +#else /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ +#define BL2_SIZE (170*1024/4) /* BL2 size is 170KB(0x0000AA00) */ +#endif /* (RCAR_SA0_SIZE == RCAR_SA0_SIZE_SMALL) */ + +/* SA0 */ +/* 0x00000000 */ +const unsigned int __attribute__ ((section(".sa0_bootrom"))) bootrom_paramA = 0x00000100; +/* 0x00000080 (Map Type 3 for eMMC Boot)*/ +/* 0x000001D4 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_addr3"))) bl2dst_addr3 = BL2_ADDRESS; +/* 0x000002E4 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_size3"))) bl2dst_size3 = BL2_SIZE; +/* 0x00000C00 (Map Type 1 for HyperFlash/QSPI Flash Boot)*/ +/* 0x00000D54 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_addr1"))) bl2dst_addr1 = BL2_ADDRESS; +/* 0x00000E64 */ +const unsigned int __attribute__ ((section(".sa0_bl2dst_size1"))) bl2dst_size1 = BL2_SIZE; diff --git a/atf-20250711/tools/renesas/rzg_layout_create/sa0.ld.S b/atf-20250711/tools/renesas/rzg_layout_create/sa0.ld.S new file mode 100644 index 000000000..23e2b237f --- /dev/null +++ b/atf-20250711/tools/renesas/rzg_layout_create/sa0.ld.S @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa0_bootrom)) + /* Map Type 3 for eMMC Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x000001D4; /* H'00000080 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr3)) + /* A-side IPL content cert "Size" */ + . = 0x000002E4; /* H'00000080 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size3)) + /* Map Type 1 for HyperFlash/QSPI Flash Boot */ + /* A-side IPL content cert "Start Address" */ + . = 0x00000D54; /* H'00000C00 + H'00000154 */ + KEEP(*(.sa0_bl2dst_addr1)) + /* A-side IPL content cert "Size" */ + . = 0x00000E64; /* H'00000C00 + H'00000264 */ + KEEP(*(.sa0_bl2dst_size1)) + } + +} diff --git a/atf-20250711/tools/renesas/rzg_layout_create/sa6.c b/atf-20250711/tools/renesas/rzg_layout_create/sa6.c new file mode 100644 index 000000000..76e3dc5e3 --- /dev/null +++ b/atf-20250711/tools/renesas/rzg_layout_create/sa6.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#define RCAR_SA6_TYPE_QSPIFLASH (0) +#define RCAR_SA6_TYPE_EMMC (1) + +#if (RCAR_SA6_TYPE == RCAR_SA6_TYPE_QSPIFLASH) + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on flash for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x001C0000U) +/* Reserved */ +#define RCAR_BL31_PARTITION (0x00000000U) +/* Source address on flash for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Reserved */ +#define RCAR_BL32_PARTITION (0x00000000U) +/* Source address on flash for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00300000U) +/* Reserved */ +#define RCAR_BL33_PARTITION (0x00000000U) +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL332_PARTITION (0x00000000U) +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL333_PARTITION (0x00000000U) +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL334_PARTITION (0x00000000U) +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL335_PARTITION (0x00000000U) +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL336_PARTITION (0x00000000U) +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL337_PARTITION (0x00000000U) +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +/* Reserved */ +#define RCAR_BL338_PARTITION (0x00000000U) + +#else /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_EMMC */ + +/* Number of content cert for Non-secure Target Program(BL33x) */ +#define RCAR_IMAGE_NUM (0x00000001U) +/* Source address on eMMC for BL31 */ +#define RCAR_BL31SRC_ADDRESS (0x00040000U) +/* Source partition on eMMC for BL31 */ +#define RCAR_BL31_PARTITION (0x00000001U) +/* Source address on eMMC for BL32 */ +#define RCAR_BL32SRC_ADDRESS (0x00200000U) +/* Source partition on eMMC for BL32 */ +#define RCAR_BL32_PARTITION (0x00000001U) +/* Source address on eMMC for BL33 */ +#define RCAR_BL33SRC_ADDRESS (0x00000000U) +/* Source partition on eMMC for BL33 */ +#define RCAR_BL33_PARTITION (0x00000002U) +/* Reserved */ +#define RCAR_BL332SRC_ADDRESS (0x00000000U) +#define RCAR_BL332_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL333SRC_ADDRESS (0x00000000U) +#define RCAR_BL333_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL334SRC_ADDRESS (0x00000000U) +#define RCAR_BL334_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL335SRC_ADDRESS (0x00000000U) +#define RCAR_BL335_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL336SRC_ADDRESS (0x00000000U) +#define RCAR_BL336_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL337SRC_ADDRESS (0x00000000U) +#define RCAR_BL337_PARTITION (0x00000000U) +/* Reserved */ +#define RCAR_BL338SRC_ADDRESS (0x00000000U) +#define RCAR_BL338_PARTITION (0x00000000U) + +#endif /* RCAR_SA6_TYPE == RCAR_SA6_TYPE_QSPIFLASH */ + +/* Destination address for BL31 */ +#define RCAR_BL31DST_ADDRESS (0x44000000U) +#define RCAR_BL31DST_ADDRESSH (0x00000000U) +/* Destination size for BL31 */ +#define RCAR_BL31DST_SIZE (0x00004000U) +/* Destination address for BL32 */ +#define RCAR_BL32DST_ADDRESS (0x44100000U) +#define RCAR_BL32DST_ADDRESSH (0x00000000U) +/* Destination size for BL32 */ +#define RCAR_BL32DST_SIZE (0x00040000U) +/* Destination address for BL33 */ +#define RCAR_BL33DST_ADDRESS (0x50000000U) +#define RCAR_BL33DST_ADDRESSH (0x00000000U) +/* Destination size for BL33 */ +#define RCAR_BL33DST_SIZE (0x00040000U) +/* Reserved */ +#define RCAR_BL332DST_ADDRESS (0x00000000U) +#define RCAR_BL332DST_ADDRESSH (0x00000000U) +#define RCAR_BL332DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL333DST_ADDRESS (0x00000000U) +#define RCAR_BL333DST_ADDRESSH (0x00000000U) +#define RCAR_BL333DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL334DST_ADDRESS (0x00000000U) +#define RCAR_BL334DST_ADDRESSH (0x00000000U) +#define RCAR_BL334DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL335DST_ADDRESS (0x00000000U) +#define RCAR_BL335DST_ADDRESSH (0x00000000U) +#define RCAR_BL335DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL336DST_ADDRESS (0x00000000U) +#define RCAR_BL336DST_ADDRESSH (0x00000000U) +#define RCAR_BL336DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL337DST_ADDRESS (0x00000000U) +#define RCAR_BL337DST_ADDRESSH (0x00000000U) +#define RCAR_BL337DST_SIZE (0x00000000U) +/* Reserved */ +#define RCAR_BL338DST_ADDRESS (0x00000000U) +#define RCAR_BL338DST_ADDRESSH (0x00000000U) +#define RCAR_BL338DST_SIZE (0x00000000U) + +/* SA6 */ +const uint64_t __attribute__ ((section(".sa6_image_num"))) + image_num = RCAR_IMAGE_NUM; +const uint64_t __attribute__ ((section(".sa6_bl31src_addr"))) + bl31src_addr = RCAR_BL31SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl31partition"))) + bl31partition = RCAR_BL31_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl32src_addr"))) + bl32src_addr = RCAR_BL32SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl32partition"))) + bl32partition = RCAR_BL32_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl33src_addr"))) + bl33src_addr = RCAR_BL33SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl33partition"))) + bl33partition = RCAR_BL33_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl332src_addr"))) + bl332src_addr = RCAR_BL332SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl332partition"))) + bl332partition = RCAR_BL332_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl333src_addr"))) + bl333src_addr = RCAR_BL333SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl333partition"))) + bl333partition = RCAR_BL333_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl334src_addr"))) + bl334src_addr = RCAR_BL334SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl334partition"))) + bl334partition = RCAR_BL334_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl335src_addr"))) + bl335src_addr = RCAR_BL335SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl335partition"))) + bl335partition = RCAR_BL335_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl336src_addr"))) + bl336src_addr = RCAR_BL336SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl336partition"))) + bl336partition = RCAR_BL336_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl337src_addr"))) + bl337src_addr = RCAR_BL337SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl337partition"))) + bl337partition = RCAR_BL337_PARTITION; +const uint64_t __attribute__ ((section(".sa6_bl338src_addr"))) + bl338src_addr = RCAR_BL338SRC_ADDRESS; +const uint64_t __attribute__ ((section(".sa6_bl338partition"))) + bl338partition = RCAR_BL338_PARTITION; +const uint32_t __attribute__ ((section(".sa6_bl31dst_addr"))) + bl31dst_addr = RCAR_BL31DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl31dst_addrh"))) + bl31dst_addrh = RCAR_BL31DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl31dst_size"))) + bl31dst_size = RCAR_BL31DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl32dst_addr"))) + bl32dst_addr = RCAR_BL32DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl32dst_addrh"))) + bl32dst_addrh = RCAR_BL32DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl32dst_size"))) + bl32dst_size = RCAR_BL32DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl33dst_addr"))) + bl33dst_addr = RCAR_BL33DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl33dst_addrh"))) + bl33dst_addrh = RCAR_BL33DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl33dst_size"))) + bl33dst_size = RCAR_BL33DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl332dst_addr"))) + bl332dst_addr = RCAR_BL332DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl332dst_addrh"))) + bl332dst_addrh = RCAR_BL332DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl332dst_size"))) + bl332dst_size = RCAR_BL332DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl333dst_addr"))) + bl333dst_addr = RCAR_BL333DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl333dst_addrh"))) + bl333dst_addrh = RCAR_BL333DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl333dst_size"))) + bl333dst_size = RCAR_BL333DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl334dst_addr"))) + bl334dst_addr = RCAR_BL334DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl334dst_addrh"))) + bl334dst_addrh = RCAR_BL334DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl334dst_size"))) + bl334dst_size = RCAR_BL334DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl335dst_addr"))) + bl335dst_addr = RCAR_BL335DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl335dst_addrh"))) + bl335dst_addrh = RCAR_BL335DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl335dst_size"))) + bl335dst_size = RCAR_BL335DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl336dst_addr"))) + bl336dst_addr = RCAR_BL336DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl336dst_addrh"))) + bl336dst_addrh = RCAR_BL336DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl336dst_size"))) + bl336dst_size = RCAR_BL336DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl337dst_addr"))) + bl337dst_addr = RCAR_BL337DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl337dst_addrh"))) + bl337dst_addrh = RCAR_BL337DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl337dst_size"))) + bl337dst_size = RCAR_BL337DST_SIZE; +const uint32_t __attribute__ ((section(".sa6_bl338dst_addr"))) + bl338dst_addr = RCAR_BL338DST_ADDRESS; +const uint32_t __attribute__ ((section(".sa6_bl338dst_addrh"))) + bl338dst_addrh = RCAR_BL338DST_ADDRESSH; +const uint32_t __attribute__ ((section(".sa6_bl338dst_size"))) + bl338dst_size = RCAR_BL338DST_SIZE; diff --git a/atf-20250711/tools/renesas/rzg_layout_create/sa6.ld.S b/atf-20250711/tools/renesas/rzg_layout_create/sa6.ld.S new file mode 100644 index 000000000..efe40b0c8 --- /dev/null +++ b/atf-20250711/tools/renesas/rzg_layout_create/sa6.ld.S @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +SECTIONS +{ + . = 0x00000000; + .rodata : { + KEEP(*(.sa6_image_num)) + . = 0x00000008; + KEEP(*(.sa6_bl31src_addr)) + . = 0x00000010; + KEEP(*(.sa6_bl31partition)) + . = 0x00000018; + KEEP(*(.sa6_bl32src_addr)) + . = 0x00000020; + KEEP(*(.sa6_bl32partition)) + . = 0x00000028; + KEEP(*(.sa6_bl33src_addr)) + . = 0x00000030; + KEEP(*(.sa6_bl33partition)) + . = 0x00000038; + KEEP(*(.sa6_bl332src_addr)) + . = 0x00000040; + KEEP(*(.sa6_bl332partition)) + . = 0x00000048; + KEEP(*(.sa6_bl333src_addr)) + . = 0x00000050; + KEEP(*(.sa6_bl333partition)) + . = 0x00000058; + KEEP(*(.sa6_bl334src_addr)) + . = 0x00000060; + KEEP(*(.sa6_bl334partition)) + . = 0x00000068; + KEEP(*(.sa6_bl335src_addr)) + . = 0x00000070; + KEEP(*(.sa6_bl335partition)) + . = 0x00000078; + KEEP(*(.sa6_bl336src_addr)) + . = 0x00000080; + KEEP(*(.sa6_bl336partition)) + . = 0x00000088; + KEEP(*(.sa6_bl337src_addr)) + . = 0x00000090; + KEEP(*(.sa6_bl337partition)) + . = 0x00000098; + KEEP(*(.sa6_bl338src_addr)) + . = 0x000000A0; + KEEP(*(.sa6_bl338partition)) + . = 0x00000554; + KEEP(*(.sa6_bl31dst_addr)) + . = 0x00000558; + KEEP(*(.sa6_bl31dst_addrh)) + . = 0x00000664; + KEEP(*(.sa6_bl31dst_size)) + . = 0x00000D54; + KEEP(*(.sa6_bl32dst_addr)) + . = 0x00000D58; + KEEP(*(.sa6_bl32dst_addrh)) + . = 0x00000E64; + KEEP(*(.sa6_bl32dst_size)) + . = 0x00001554; + KEEP(*(.sa6_bl33dst_addr)) + . = 0x00001558; + KEEP(*(.sa6_bl33dst_addrh)) + . = 0x00001664; + KEEP(*(.sa6_bl33dst_size)) + . = 0x00001D54; + KEEP(*(.sa6_bl332dst_addr)) + . = 0x00001D58; + KEEP(*(.sa6_bl332dst_addrh)) + . = 0x00001E64; + KEEP(*(.sa6_bl332dst_size)) + . = 0x00002554; + KEEP(*(.sa6_bl333dst_addr)) + . = 0x00002558; + KEEP(*(.sa6_bl333dst_addrh)) + . = 0x00002664; + KEEP(*(.sa6_bl333dst_size)) + . = 0x00002D54; + KEEP(*(.sa6_bl334dst_addr)) + . = 0x00002D58; + KEEP(*(.sa6_bl334dst_addrh)) + . = 0x00002E64; + KEEP(*(.sa6_bl334dst_size)) + . = 0x00003554; + KEEP(*(.sa6_bl335dst_addr)) + . = 0x00003558; + KEEP(*(.sa6_bl335dst_addrh)) + . = 0x00003664; + KEEP(*(.sa6_bl335dst_size)) + . = 0x00003D54; + KEEP(*(.sa6_bl336dst_addr)) + . = 0x00003D58; + KEEP(*(.sa6_bl336dst_addrh)) + . = 0x00003E64; + KEEP(*(.sa6_bl336dst_size)) + . = 0x00004554; + KEEP(*(.sa6_bl337dst_addr)) + . = 0x00004558; + KEEP(*(.sa6_bl337dst_addrh)) + . = 0x00004664; + KEEP(*(.sa6_bl337dst_size)) + . = 0x00004D54; + KEEP(*(.sa6_bl338dst_addr)) + . = 0x00004D58; + KEEP(*(.sa6_bl338dst_addrh)) + . = 0x00004E64; + KEEP(*(.sa6_bl338dst_size)) + } + +} diff --git a/atf-20250711/tools/sptool/Makefile b/atf-20250711/tools/sptool/Makefile new file mode 100644 index 000000000..f579a42eb --- /dev/null +++ b/atf-20250711/tools/sptool/Makefile @@ -0,0 +1,42 @@ +# +# Copyright (c) 2018-2025, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk + +SPTOOL ?= sptool$(.exe) +PROJECT := $(notdir ${SPTOOL}) +OBJECTS := sptool.o + +override CPPFLAGS += -D_GNU_SOURCE -D_XOPEN_SOURCE=700 +HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +INCLUDE_PATHS := -I../../include/tools_share + +.PHONY: all clean distclean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} -o $@ ${LDLIBS} + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo + +%.o: %.c Makefile + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${CPPFLAGS} ${HOSTCCFLAGS} ${INCLUDE_PATHS} $< -o $@ + +clean: + $(q)rm -rf $(PROJECT) $(OBJECTS) diff --git a/atf-20250711/tools/sptool/hob.py b/atf-20250711/tools/sptool/hob.py new file mode 100644 index 000000000..dced08650 --- /dev/null +++ b/atf-20250711/tools/sptool/hob.py @@ -0,0 +1,425 @@ +#!/usr/bin/python3 +# Copyright (c) 2025, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +import struct + +EFI_HOB_HANDOFF_TABLE_VERSION = 0x000A + +PAGE_SIZE_SHIFT = 12 # TODO assuming 4K page size + +# HobType values of EFI_HOB_GENERIC_HEADER. + +EFI_HOB_TYPE_HANDOFF = 0x0001 +EFI_HOB_TYPE_MEMORY_ALLOCATION = 0x0002 +EFI_HOB_TYPE_RESOURCE_DESCRIPTOR = 0x0003 +EFI_HOB_TYPE_GUID_EXTENSION = 0x0004 +EFI_HOB_TYPE_FV = 0x0005 +EFI_HOB_TYPE_CPU = 0x0006 +EFI_HOB_TYPE_MEMORY_POOL = 0x0007 +EFI_HOB_TYPE_FV2 = 0x0009 +EFI_HOB_TYPE_LOAD_PEIM_UNUSED = 0x000A +EFI_HOB_TYPE_UEFI_CAPSULE = 0x000B +EFI_HOB_TYPE_FV3 = 0x000C +EFI_HOB_TYPE_UNUSED = 0xFFFE +EFI_HOB_TYPE_END_OF_HOB_LIST = 0xFFFF + +# GUID values +"""struct efi_guid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_and_node[8]; +}""" + +MM_PEI_MMRAM_MEMORY_RESERVE_GUID = ( + 0x0703F912, + 0xBF8D, + 0x4E2A, + (0xBE, 0x07, 0xAB, 0x27, 0x25, 0x25, 0xC5, 0x92), +) +MM_NS_BUFFER_GUID = ( + 0xF00497E3, + 0xBFA2, + 0x41A1, + (0x9D, 0x29, 0x54, 0xC2, 0xE9, 0x37, 0x21, 0xC5), +) + +# MMRAM states and capabilities +# See UEFI Platform Initialization Specification Version 1.8, IV-5.3.5 +EFI_MMRAM_OPEN = 0x00000001 +EFI_MMRAM_CLOSED = 0x00000002 +EFI_MMRAM_LOCKED = 0x00000004 +EFI_CACHEABLE = 0x00000008 +EFI_ALLOCATED = 0x00000010 +EFI_NEEDS_TESTING = 0x00000020 +EFI_NEEDS_ECC_INITIALIZATION = 0x00000040 + +EFI_SMRAM_OPEN = EFI_MMRAM_OPEN +EFI_SMRAM_CLOSED = EFI_MMRAM_CLOSED +EFI_SMRAM_LOCKED = EFI_MMRAM_LOCKED + +# EFI boot mode. +EFI_BOOT_WITH_FULL_CONFIGURATION = 0x00 +EFI_BOOT_WITH_MINIMAL_CONFIGURATION = 0x01 +EFI_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES = 0x02 +EFI_BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS = 0x03 +EFI_BOOT_WITH_DEFAULT_SETTINGS = 0x04 +EFI_BOOT_ON_S4_RESUME = 0x05 +EFI_BOOT_ON_S5_RESUME = 0x06 +EFI_BOOT_WITH_MFG_MODE_SETTINGS = 0x07 +EFI_BOOT_ON_S2_RESUME = 0x10 +EFI_BOOT_ON_S3_RESUME = 0x11 +EFI_BOOT_ON_FLASH_UPDATE = 0x12 +EFI_BOOT_IN_RECOVERY_MODE = 0x20 + +STMM_BOOT_MODE = EFI_BOOT_WITH_FULL_CONFIGURATION +STMM_MMRAM_REGION_STATE_DEFAULT = EFI_CACHEABLE | EFI_ALLOCATED +STMM_MMRAM_REGION_STATE_HEAP = EFI_CACHEABLE + +"""`struct` python module allows user to specify endianness. +We are expecting FVP or STMM platform as target and that they will be +little-endian. See `struct` python module documentation if other endianness is +needed.""" +ENDIANNESS = "<" + + +def struct_pack_with_endianness(format_str, *args): + return struct.pack((ENDIANNESS + format_str), *args) + + +def struct_calcsize_with_endianness(format_str): + return struct.calcsize(ENDIANNESS + format_str) + + +# Helper for fdt node property parsing +def get_integer_property_value(fdt_node, name): + if fdt_node.exist_property(name): + p = fdt_node.get_property(name) + + # Device Tree value + if len(p) == 1: + return p.value + # Device Tree value represented as two 32-bit values + if len(p) == 2: + msb = p[0] + lsb = p[1] + return lsb | (msb << 32) + return None + + +class EfiGuid: + """Class representing EFI GUID (Globally Unique Identifier) as described by + the UEFI Specification v2.10""" + + def __init__(self, time_low, time_mid, time_hi_and_version, clock_seq_and_node): + self.time_low = time_low + self.time_mid = time_mid + self.time_hi_and_version = time_hi_and_version + self.clock_seq_and_node = clock_seq_and_node + self.format_str = "IHH8B" + + def pack(self): + return struct_pack_with_endianness( + self.format_str, + self.time_low, + self.time_mid, + self.time_hi_and_version, + *self.clock_seq_and_node, + ) + + def __str__(self): + return f"{hex(self.time_low)}, {hex(self.time_mid)}, \ + {hex(self.time_hi_and_version)}, {[hex(i) for i in self.clock_seq_and_node]}" + + +class HobGenericHeader: + """Class representing the Hob Generic Header data type as described + in the UEFI Platform Initialization Specification version 1.8. + + Each HOB is required to contain this header specifying the type and length + of the HOB. + """ + + def __init__(self, hob_type, hob_length): + self.format_str = "HHI" + self.hob_type = hob_type + self.hob_length = struct_calcsize_with_endianness(self.format_str) + hob_length + self.reserved = 0 + + def pack(self): + return struct_pack_with_endianness( + self.format_str, self.hob_type, self.hob_length, self.reserved + ) + + def __str__(self): + return f"Hob Type: {self.hob_type} Hob Length: {self.hob_length}" + + +class HobGuid: + """Class representing the Guid Extension HOB as described in the UEFI + Platform Initialization Specification version 1.8. + + Allows the production of HOBs whose types are not defined by the + specification by generating a GUID for the HOB entry.""" + + def __init__(self, name: EfiGuid, data_format_str, data): + hob_length = struct_calcsize_with_endianness( + name.format_str + ) + struct_calcsize_with_endianness(data_format_str) + self.header = HobGenericHeader(EFI_HOB_TYPE_GUID_EXTENSION, hob_length) + self.name = name + self.data = data + self.data_format_str = data_format_str + self.format_str = ( + self.header.format_str + self.name.format_str + data_format_str + ) + + def pack(self): + return ( + self.header.pack() + + self.name.pack() + + struct_pack_with_endianness(self.data_format_str, *self.data) + ) + + def __str__(self): + return f"Header: {self.header}\n Name: {self.name}\n Data: {self.data}" + + +class HandoffInfoTable: + """Class representing the Handoff Info Table HOB (also known as PHIT HOB) + as described in the UEFI Platform Initialization Specification version 1.8. + + Must be the first HOB in the HOB list. Contains general state + information. + + For an SP, the range `memory_bottom` to `memory_top` will be the memory + range for the SP starting at the load address. `free_memory_bottom` to + `free_memory_top` indicates space where more HOB's could be added to the + HOB List.""" + + def __init__(self, memory_base, memory_size, free_memory_base, free_memory_size): + # header,uint32t,uint32t, uint64_t * 5 + self.format_str = "II5Q" + hob_length = struct_calcsize_with_endianness(self.format_str) + self.header = HobGenericHeader(EFI_HOB_TYPE_HANDOFF, hob_length) + self.version = EFI_HOB_HANDOFF_TABLE_VERSION + self.boot_mode = STMM_BOOT_MODE + self.memory_top = memory_base + memory_size + self.memory_bottom = memory_base + self.free_memory_top = free_memory_base + free_memory_size + self.free_memory_bottom = free_memory_base + self.header.hob_length + self.hob_end = None + + def set_hob_end_addr(self, hob_end_addr): + self.hob_end = hob_end_addr + + def set_free_memory_bottom_addr(self, addr): + self.free_memory_bottom = addr + + def pack(self): + return self.header.pack() + struct_pack_with_endianness( + self.format_str, + self.version, + self.boot_mode, + self.memory_top, + self.memory_bottom, + self.free_memory_top, + self.free_memory_bottom, + self.hob_end, + ) + + +class FirmwareVolumeHob: + """Class representing the Firmware Volume HOB type as described in the + UEFI Platform Initialization Specification version 1.8. + + For an SP this will detail where the SP binary is located. + """ + + def __init__(self, base_address, img_offset, img_size): + # header, uint64_t, uint64_t + self.data_format_str = "2Q" + hob_length = struct_calcsize_with_endianness(self.data_format_str) + self.header = HobGenericHeader(EFI_HOB_TYPE_FV, hob_length) + self.format_str = self.header.format_str + self.data_format_str + self.base_address = base_address + img_offset + self.length = img_size - img_offset + + def pack(self): + return self.header.pack() + struct_pack_with_endianness( + self.data_format_str, self.base_address, self.length + ) + + +class EndOfHobListHob: + """Class representing the End of HOB List HOB type as described in the + UEFI Platform Initialization Specification version 1.8. + + Must be the last entry in a HOB list. + """ + + def __init__(self): + self.header = HobGenericHeader(EFI_HOB_TYPE_END_OF_HOB_LIST, 0) + self.format_str = "" + + def pack(self): + return self.header.pack() + + +class HobList: + """Class representing a HOB (Handoff Block list) based on the UEFI Platform + Initialization Sepcification version 1.8""" + + def __init__(self, phit: HandoffInfoTable): + if phit is None: + raise Exception("HobList must be initialized with valid PHIT HOB") + final_hob = EndOfHobListHob() + phit.hob_end = phit.free_memory_bottom + phit.free_memory_bottom += final_hob.header.hob_length + self.hob_list = [phit, final_hob] + + def add(self, hob): + if hob is not None: + if hob.header.hob_length > ( + self.get_phit().free_memory_top - self.get_phit().free_memory_bottom + ): + raise MemoryError( + f"Cannot add HOB of length {hob.header.hob_length}. \ + Resulting table size would exceed max table size of \ + {self.max_size}. Current table size: {self.size}." + ) + self.hob_list.insert(-1, hob) + self.get_phit().hob_end += hob.header.hob_length + self.get_phit().free_memory_bottom += hob.header.hob_length + + def get_list(self): + return self.hob_list + + def get_phit(self): + if self.hob_list is not None: + if type(self.hob_list[0]) is not HandoffInfoTable: + raise Exception("First hob in list must be of type PHIT") + return self.hob_list[0] + + +def generate_mmram_desc(base_addr, page_count, granule, region_state): + physical_size = page_count << (PAGE_SIZE_SHIFT + (granule << 1)) + physical_start = base_addr + cpu_start = base_addr + + return ("4Q", (physical_start, cpu_start, physical_size, region_state)) + + +def generate_stmm_region_descriptor(base_addr, physical_size): + region_state = STMM_MMRAM_REGION_STATE_DEFAULT + physical_start = base_addr + cpu_start = base_addr + return ("4Q", (physical_start, cpu_start, physical_size, region_state)) + + +def generate_ns_buffer_guid(mmram_desc): + return HobGuid(EfiGuid(*MM_NS_BUFFER_GUID), *mmram_desc) + + +def generate_pei_mmram_memory_reserve_guid(regions): + # uint32t n_reserved regions, 4 bytes for padding so that array is aligned, + # array of mmram descriptors + format_str = "I4x" + data = [len(regions)] + for desc_format_str, mmram_desc in regions: + format_str += desc_format_str + data.extend(mmram_desc) + guid_data = (format_str, data) + return HobGuid(EfiGuid(*MM_PEI_MMRAM_MEMORY_RESERVE_GUID), *guid_data) + + +def generate_hob_from_fdt_node(sp_fdt, hob_offset, hob_size=None): + """Create a HOB list binary from an SP FDT.""" + fv_hob = None + ns_buffer_hob = None + mmram_reserve_hob = None + shared_buf_hob = None + + load_address = get_integer_property_value(sp_fdt, "load-address") + img_size = get_integer_property_value(sp_fdt, "image-size") + entrypoint_offset = get_integer_property_value(sp_fdt, "entrypoint-offset") + + if entrypoint_offset is None: + entrypoint_offset = 0x0 + if hob_offset is None: + hob_offset = 0x0 + if img_size is None: + img_size = 0x0 + + regions = [] + + # StMM requires the first memory region described in the + # MM_PEI_MMRAM_MEMORY_RESERVE_GUID describe the full partition layout. + regions.append(generate_stmm_region_descriptor(load_address, img_size)) + + if sp_fdt.exist_node("memory-regions"): + if sp_fdt.exist_property("xlat-granule"): + granule = int(sp_fdt.get_property("xlat-granule").value) + else: + # Default granule to 4K + granule = 0 + memory_regions = sp_fdt.get_node("memory-regions") + for node in memory_regions.nodes: + base_addr = get_integer_property_value(node, "base-address") + page_count = get_integer_property_value(node, "pages-count") + + if base_addr is None: + offset = get_integer_property_value( + node, "load-address-relative-offset" + ) + if offset is None: + # Cannot create memory descriptor without base address, so skip + # node if base address cannot be defined + continue + else: + base_addr = load_address + offset + + if node.name.strip() == "heap": + region_state = STMM_MMRAM_REGION_STATE_HEAP + else: + region_state = STMM_MMRAM_REGION_STATE_DEFAULT + + mmram_desc = generate_mmram_desc( + base_addr, page_count, granule, region_state + ) + + if node.name.strip() == "ns_comm_buffer": + ns_buffer_hob = generate_ns_buffer_guid(mmram_desc) + + regions.append(mmram_desc) + + mmram_reserve_hob = generate_pei_mmram_memory_reserve_guid(regions) + + fv_hob = FirmwareVolumeHob(load_address, entrypoint_offset, img_size) + hob_list_base = load_address + hob_offset + + # TODO assuming default of 1 page allocated for HOB List + if hob_size is not None: + max_table_size = hob_size + else: + max_table_size = 1 << PAGE_SIZE_SHIFT + phit = HandoffInfoTable( + load_address, entrypoint_offset + img_size, hob_list_base, max_table_size + ) + + # Create a HobList containing only PHIT and EndofHobList HOBs. + hob_list = HobList(phit) + + # Add HOBs to HOB list + if fv_hob is not None: + hob_list.add(fv_hob) + if ns_buffer_hob is not None: + hob_list.add(ns_buffer_hob) + if mmram_reserve_hob is not None: + hob_list.add(mmram_reserve_hob) + if shared_buf_hob is not None: + hob_list.add(shared_buf_hob) + + return hob_list diff --git a/atf-20250711/tools/sptool/sp_mk_generator.py b/atf-20250711/tools/sptool/sp_mk_generator.py new file mode 100644 index 000000000..3dd1d4e68 --- /dev/null +++ b/atf-20250711/tools/sptool/sp_mk_generator.py @@ -0,0 +1,367 @@ +#!/usr/bin/python3 +# Copyright (c) 2020-2025, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +""" +This script is invoked by Make system and generates secure partition makefile. +It expects platform provided secure partition layout file which contains list +of Secure Partition Images and Partition manifests(PM). +Layout file can exist outside of TF-A tree and the paths of Image and PM files +must be relative to it. + +This script parses the layout file and generates a make file which updates +FDT_SOURCES, FIP_ARGS, CRT_ARGS and SPTOOL_ARGS which are used in later build +steps. +If the SP entry in the layout file has a "uuid" field the scripts gets the UUID +from there, otherwise it parses the associated partition manifest and extracts +the UUID from there. + +param1: Generated mk file "sp_gen.mk" +param2: "SP_LAYOUT_FILE", json file containing platform provided information +param3: plat out directory +param4: CoT parameter +param5: Generated dts file "sp_list_fragment.dts" + +Generated "sp_gen.mk" file contains triplet of following information for each +Secure Partition entry + FDT_SOURCES += sp1.dts + SPTOOL_ARGS += -i sp1.bin:sp1.dtb -o sp1.pkg + FIP_ARGS += --blob uuid=XXXXX-XXX...,file=sp1.pkg + CRT_ARGS += --sp-pkg1 sp1.pkg + +It populates the number of SP in the defined macro 'NUM_SP' + $(eval $(call add_define_val,NUM_SP,{len(sp_layout.keys())})) + +A typical SP_LAYOUT_FILE file will look like +{ + "SP1" : { + "image": "sp1.bin", + "pm": "test/sp1.dts" + }, + + "SP2" : { + "image": "sp2.bin", + "pm": "test/sp2.dts", + "uuid": "1b1820fe-48f7-4175-8999-d51da00b7c9f" + } + + ... +} + +""" +import json +import os +import re +import sys +import uuid +import fdt +from spactions import SpSetupActions +import hob +import struct +from hob import HobList + +MAX_SP = 8 +UUID_LEN = 4 +HOB_OFFSET_DEFAULT=0x2000 + +# Some helper functions to access args propagated to the action functions in +# SpSetupActions framework. +def check_sp_mk_gen(args :dict): + if "sp_gen_mk" not in args.keys(): + raise Exception(f"Path to file sp_gen.mk needs to be in 'args'.") + +def check_out_dir(args :dict): + if "out_dir" not in args.keys() or not os.path.isdir(args["out_dir"]): + raise Exception("Define output folder with \'out_dir\' key.") + +def check_sp_layout_dir(args :dict): + if "sp_layout_dir" not in args.keys() or not os.path.isdir(args["sp_layout_dir"]): + raise Exception("Define output folder with \'sp_layout_dir\' key.") + +def write_to_sp_mk_gen(content, args :dict): + check_sp_mk_gen(args) + with open(args["sp_gen_mk"], "a") as f: + f.write(f"{content}\n") + +def get_sp_manifest_full_path(sp_node, args :dict): + check_sp_layout_dir(args) + return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["pm"])) + +def get_sp_img_full_path(sp_node, args :dict): + check_sp_layout_dir(args) + return os.path.join(args["sp_layout_dir"], get_file_from_layout(sp_node["image"])) + +def get_size(sp_node): + if not "size" in sp_node: + print("WARNING: default image size 0x100000") + return 0x100000 + + # Try if it was a decimal value. + try: + return int(sp_node["size"]) + except ValueError: + print("WARNING: trying to parse base 16 size") + # Try if it is of base 16 + return int(sp_node["size"], 16) + +def get_sp_pkg(sp, args :dict): + check_out_dir(args) + return os.path.join(args["out_dir"], f"{sp}.pkg") + +def is_line_in_sp_gen(line, args :dict): + with open(args["sp_gen_mk"], "r") as f: + sppkg_rule = [l for l in f if line in l] + return len(sppkg_rule) != 0 + +def get_file_from_layout(node): + ''' Helper to fetch a file path from sp_layout.json. ''' + if type(node) is dict and "file" in node.keys(): + return node["file"] + return node + +def get_offset_from_layout(node): + ''' Helper to fetch an offset from sp_layout.json. ''' + if type(node) is dict and "offset" in node.keys(): + return int(node["offset"], 0) + return None + +def get_image_offset(node): + ''' Helper to fetch image offset from sp_layout.json ''' + return get_offset_from_layout(node["image"]) + +def get_pm_offset(node): + ''' Helper to fetch pm offset from sp_layout.json ''' + return get_offset_from_layout(node["pm"]) + +def get_uuid(sp_layout, sp, args :dict): + ''' Helper to fetch uuid from pm file listed in sp_layout.json''' + if "uuid" in sp_layout[sp]: + # Extract the UUID from the JSON file if the SP entry has a 'uuid' field + uuid_std = uuid.UUID(sp_layout[sp]['uuid']) + else: + with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f: + uuid_lines = [l for l in pm_f if 'uuid' in l] + assert(len(uuid_lines) == 1) + # The uuid field in SP manifest is the little endian representation + # mapped to arguments as described in SMCCC section 5.3. + # Convert each unsigned integer value to a big endian representation + # required by fiptool. + uuid_parsed = re.findall("0x([0-9a-f]+)", uuid_lines[0]) + y = list(map(bytearray.fromhex, uuid_parsed)) + z = [int.from_bytes(i, byteorder='little', signed=False) for i in y] + uuid_std = uuid.UUID(f'{z[0]:08x}{z[1]:08x}{z[2]:08x}{z[3]:08x}') + return uuid_std + +def get_load_address(sp_layout, sp, args :dict): + ''' Helper to fetch load-address from pm file listed in sp_layout.json''' + with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f: + load_address_lines = [l for l in pm_f if re.search(r'load-address[^-]', l)] + + if len(load_address_lines) != 1: + return None + + load_address_parsed = re.search("(0x[0-9a-f]+)", load_address_lines[0]) + return load_address_parsed.group(0) + +@SpSetupActions.sp_action(global_action=True) +def check_max_sps(sp_layout, _, args :dict): + ''' Check validate the maximum number of SPs is respected. ''' + if len(sp_layout.keys()) > MAX_SP: + raise Exception(f"Too many SPs in SP layout file. Max: {MAX_SP}") + return args + +@SpSetupActions.sp_action(global_action=True) +def count_sps(sp_layout, _, args :dict): + ''' Count number of SP and put in NUM_SP ''' + write_to_sp_mk_gen(f"$(eval $(call add_define_val,NUM_SP,{len(sp_layout.keys())}))", args) + return args + +@SpSetupActions.sp_action +def gen_fdt_sources(sp_layout, sp, args :dict): + ''' Generate FDT_SOURCES values for a given SP. ''' + manifest_path = get_sp_manifest_full_path(sp_layout[sp], args) + write_to_sp_mk_gen(f"FDT_SOURCES += {manifest_path}", args) + return args + +@SpSetupActions.sp_action(exec_order=1) +def generate_hob_list(sp_layout, sp, args: dict): + ''' + Generates a HOB file for the partition, if it requested it in its FF-A + manifest. + ''' + with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as f: + sp_fdt = fdt.parse_dts(f.read()) + + if sp_fdt.exist_property('hob_list', '/boot-info'): + sp_hob_name = os.path.basename(sp + ".hob.bin") + sp_hob_name = os.path.join(args["out_dir"], f"{sp_hob_name}") + + # Add to the args so it can be consumed by the TL pkg function. + sp_layout[sp]["hob_path"] = sp_hob_name + hob_list = hob.generate_hob_from_fdt_node(sp_fdt, HOB_OFFSET_DEFAULT) + with open(sp_hob_name, "wb") as h: + for block in hob_list.get_list(): + h.write(block.pack()) + + return args + +def generate_sp_pkg(sp_node, pkg, sp_img, sp_dtb): + ''' Generates the rule in case SP is to be generated in an SP Pkg. ''' + pm_offset = get_pm_offset(sp_node) + sptool_args = f" --pm-offset {pm_offset}" if pm_offset is not None else "" + image_offset = get_image_offset(sp_node) + sptool_args += f" --img-offset {image_offset}" if image_offset is not None else "" + sptool_args += f" -o {pkg}" + return f''' +{pkg}: {sp_dtb} {sp_img} +\t$(Q)echo Generating {pkg} +\t$(Q)$(PYTHON) $(SPTOOL) -i {sp_img}:{sp_dtb} {sptool_args} +''' + +def generate_tl_pkg(sp_node, pkg, sp_img, sp_dtb, hob_path = None): + ''' Generate make rules for a Transfer List type package. ''' + # TE Type for the FF-A manifest. + TE_FFA_MANIFEST = 0x106 + # TE Type for the SP binary. + TE_SP_BINARY = 0x103 + # TE Type for the HOB List. + TE_HOB_LIST = 0x3 + tlc_add_hob = f"\t$(Q)$(TLCTOOL) add --entry {TE_HOB_LIST} {hob_path} {pkg}" if hob_path is not None else "" + return f''' +{pkg}: {sp_dtb} {sp_img} +\t$(Q)echo Generating {pkg} +\t$(Q)$(TLCTOOL) create --size {get_size(sp_node)} --entry {TE_FFA_MANIFEST} {sp_dtb} {pkg} --align 12 +{tlc_add_hob} +\t$(Q)$(TLCTOOL) add --entry {TE_SP_BINARY} {sp_img} {pkg} +''' + +@SpSetupActions.sp_action +def gen_partition_pkg(sp_layout, sp, args :dict): + ''' Generate Sp Pkgs rules. ''' + pkg = get_sp_pkg(sp, args) + + sp_dtb_name = os.path.basename(get_file_from_layout(sp_layout[sp]["pm"]))[:-1] + "b" + sp_dtb = os.path.join(args["out_dir"], f"fdts/{sp_dtb_name}") + sp_img = get_sp_img_full_path(sp_layout[sp], args) + + # Do not generate rule if already there. + if is_line_in_sp_gen(f'{pkg}:', args): + return args + + # This should include all packages of all kinds. + write_to_sp_mk_gen(f"SP_PKGS += {pkg}\n", args) + package_type = sp_layout[sp]["package"] if "package" in sp_layout[sp] else "sp_pkg" + + if package_type == "sp_pkg": + partition_pkg_rule = generate_sp_pkg(sp_layout[sp], pkg, sp_img, sp_dtb) + elif package_type == "tl_pkg": + # Conditionally provide the Hob. + hob_path = sp_layout[sp]["hob_path"] if "hob_path" in sp_layout[sp] else None + partition_pkg_rule = generate_tl_pkg( + sp_layout[sp], pkg, sp_img, sp_dtb, hob_path) + else: + raise ValueError(f"Specified invalid pkg type {package_type}") + + write_to_sp_mk_gen(partition_pkg_rule, args) + return args + +@SpSetupActions.sp_action(global_action=True, exec_order=1) +def check_dualroot(sp_layout, _, args :dict): + ''' Validate the amount of SPs from SiP and Platform owners. ''' + if not args.get("dualroot"): + return args + args["split"] = int(MAX_SP / 2) + owners = [sp_layout[sp].get("owner") for sp in sp_layout] + args["plat_max_count"] = owners.count("Plat") + + # If it is owned by the platform owner, it is assigned to the SiP. + args["sip_max_count"] = len(sp_layout.keys()) - args["plat_max_count"] + if args["sip_max_count"] > args["split"] or args["sip_max_count"] > args["split"]: + print(f"WARN: SiP Secure Partitions should not be more than {args['split']}") + # Counters for gen_crt_args. + args["sip_count"] = 1 + args["plat_count"] = 1 + return args + +@SpSetupActions.sp_action +def gen_crt_args(sp_layout, sp, args :dict): + ''' Append CRT_ARGS. ''' + # If "dualroot" is configured, 'sp_pkg_idx' depends on whether the SP is owned + # by the "SiP" or the "Plat". + if args.get("dualroot"): + # If the owner is not specified as "Plat", default to "SiP". + if sp_layout[sp].get("owner") == "Plat": + if args["plat_count"] > args["plat_max_count"]: + raise ValueError("plat_count can't surpass plat_max_count in args.") + sp_pkg_idx = args["plat_count"] + args["split"] + args["plat_count"] += 1 + else: + if args["sip_count"] > args["sip_max_count"]: + raise ValueError("sip_count can't surpass sip_max_count in args.") + sp_pkg_idx = args["sip_count"] + args["sip_count"] += 1 + else: + sp_pkg_idx = [k for k in sp_layout.keys()].index(sp) + 1 + write_to_sp_mk_gen(f"CRT_ARGS += --sp-pkg{sp_pkg_idx} {get_sp_pkg(sp, args)}\n", args) + return args + +@SpSetupActions.sp_action +def gen_fiptool_args(sp_layout, sp, args :dict): + ''' Generate arguments for the FIP Tool. ''' + uuid_std = get_uuid(sp_layout, sp, args) + write_to_sp_mk_gen(f"FIP_ARGS += --blob uuid={str(uuid_std)},file={get_sp_pkg(sp, args)}\n", args) + return args + +@SpSetupActions.sp_action +def gen_fconf_fragment(sp_layout, sp, args: dict): + ''' Generate the fconf fragment file''' + with open(args["fconf_fragment"], "a") as f: + uuid = get_uuid(sp_layout, sp, args) + owner = "Plat" if sp_layout[sp].get("owner") == "Plat" else "SiP" + + if "physical-load-address" in sp_layout[sp].keys(): + load_address = sp_layout[sp]["physical-load-address"] + else: + load_address = get_load_address(sp_layout, sp, args) + + if load_address is not None: + f.write( +f'''\ +{sp} {{ + uuid = "{uuid}"; + load-address = <{load_address}>; + owner = "{owner}"; +}}; + +''') + else: + print("Warning: No load-address was found in the SP manifest.") + + return args + +def init_sp_actions(sys): + # Initialize arguments for the SP actions framework + args = {} + args["sp_gen_mk"] = os.path.abspath(sys.argv[1]) + sp_layout_file = os.path.abspath(sys.argv[2]) + args["sp_layout_dir"] = os.path.dirname(sp_layout_file) + args["out_dir"] = os.path.abspath(sys.argv[3]) + args["dualroot"] = sys.argv[4] == "dualroot" + args["fconf_fragment"] = os.path.abspath(sys.argv[5]) + + + with open(sp_layout_file) as json_file: + sp_layout = json.load(json_file) + #Clear content of file "sp_gen.mk". + with open(args["sp_gen_mk"], "w"): + None + #Clear content of file "fconf_fragment". + with open(args["fconf_fragment"], "w"): + None + + return args, sp_layout + +if __name__ == "__main__": + args, sp_layout = init_sp_actions(sys) + SpSetupActions.run_actions(sp_layout, args) diff --git a/atf-20250711/tools/sptool/spactions.py b/atf-20250711/tools/sptool/spactions.py new file mode 100644 index 000000000..ff28ebbcd --- /dev/null +++ b/atf-20250711/tools/sptool/spactions.py @@ -0,0 +1,155 @@ +#!/usr/bin/python3 +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +''' +This is a python module for defining and executing SP setup actions, targeting +a system deploying an SPM implementation. +Each action consists of a function, that processes the SP layout json file and +other provided arguments. +At the core of this is the SpSetupActions which provides a means to register +the functions into a table of actions, and execute them all when invoking +SpSetupActions.run_actions. +Registering the function is done by using the decorator '@SpSetupActions.sp_action' +at function definition. + +Functions can be called: +- once only, or per SP defined in the SP layout file; +- following an order, from lowest to highest of their execution order. +More information in the doc comments below. +''' +import bisect + +DEFAULT_ACTION_ORDER = 100 + +class _ConfiguredAction: + """ + Wraps action function with its configuration. + """ + def __init__(self, action, exec_order=DEFAULT_ACTION_ORDER, global_action=True, log_calls = False): + self.exec_order = exec_order + self.__name__ = action.__name__ + def logged_action(action): + def inner_logged_action(sp_layout, sp, args :dict): + print(f"Calling {action.__name__} -> {sp}") + return action(sp_layout, sp, args) + return inner_logged_action + self.action = logged_action(action) if log_calls is True else action + self.global_action = global_action + + def __lt__(self, other): + """ + To allow for ordered inserts in a list of actions. + """ + return self.exec_order < other.exec_order + + def __call__(self, sp_layout, sp, args :dict): + """ + Calls action function. + """ + return self.action(sp_layout, sp, args) + + def __repr__(self) -> str: + """ + Pretty format to show debug information about the action. + """ + return f"func: {self.__name__}; global:{self.global_action}; exec_order: {self.exec_order}" + +class SpSetupActions: + actions = [] + + def sp_action(in_action = None, global_action = False, log_calls=False, exec_order=DEFAULT_ACTION_ORDER): + """ + Function decorator that registers and configures action. + + :param in_action - function to register + :param global_action - make the function global, i.e. make it be + only called once. + :param log_calls - at every call to action, a useful log will be printed. + :param exec_order - action's calling order. + """ + def append_action(action): + action = _ConfiguredAction(action, exec_order, global_action, log_calls) + bisect.insort(SpSetupActions.actions, action) + return action + if in_action is not None: + return append_action(in_action) + return append_action + + def run_actions(sp_layout: dict, args: dict, verbose=False): + """ + Executes all actions in accordance to their registering configuration: + - If set as "global" it will be called once. + - Actions are called respecting the order established by their "exec_order" field. + + :param sp_layout - dictionary containing the SP layout information. + :param args - arguments to be propagated through the call of actions. + :param verbose - prints actions information in order of execution. + """ + args["called"] = [] # for debug purposes + def append_called(action, sp, args :dict): + args["called"].append(f"{action.__name__} -> {sp}") + return args + + for action in SpSetupActions.actions: + if verbose: + print(f"Calling {action}") + if action.global_action: + scope = "global" + args = action(sp_layout, scope, args) + args = append_called(action, scope, args) + else: + # Functions that are not global called for each SP defined in + # the SP layout. + for sp in sp_layout.keys(): + args = action(sp_layout, sp, args) + args = append_called(action, sp, args) + +if __name__ == "__main__": + # Executing this module will have the following test code/playground executed + sp_layout = { + "partition1" : { + "boot-info": True, + "image": { + "file": "partition.bin", + "offset":"0x2000" + }, + "pm": { + "file": "cactus.dts", + "offset":"0x1000" + }, + "owner": "SiP" + }, + "partition2" : { + "image": "partition.bin", + "pm": "cactus-secondary.dts", + "owner": "Plat" + }, + "partition3" : { + "image": "partition.bin", + "pm": "cactus-tertiary.dts", + "owner": "Plat" + }, + "partition4" : { + "image": "ivy.bin", + "pm": "ivy.dts", + "owner": "Plat" + } + } + + #Example of how to use this module + @SpSetupActions.sp_action(global_action=True) + def my_action1(sp_layout, _, args :dict): + print(f"inside function my_action1{sp_layout}\n\n args:{args})") + return args # Always return args in action function. + @SpSetupActions.sp_action(exec_order=1) + def my_action2(sp_layout, sp_name, args :dict): + print(f"inside function my_action2; SP: {sp_name} {sp_layout} args:{args}") + return args + + # Example arguments to be propagated through the functions. + # 'args' can be extended in the action functions. + args = dict() + args["arg1"] = 0xEEE + args["arg2"] = 0xFF + SpSetupActions.run_actions(sp_layout, args) diff --git a/atf-20250711/tools/sptool/sptool.py b/atf-20250711/tools/sptool/sptool.py new file mode 100755 index 000000000..ae7df92c1 --- /dev/null +++ b/atf-20250711/tools/sptool/sptool.py @@ -0,0 +1,145 @@ +#!/usr/bin/python3 +# Copyright (c) 2022, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# +# Copyright 2022 The Hafnium Authors. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://opensource.org/licenses/BSD-3-Clause. + +""" +Script which generates a Secure Partition package. +https://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager.html#secure-partition-packages +""" + +import argparse +from collections import namedtuple +import sys +from shutil import copyfileobj +import os + +HF_PAGE_SIZE = 0x1000 # bytes +HEADER_ELEMENT_BYTES = 4 # bytes +MANIFEST_IMAGE_SPLITTER=':' +PM_OFFSET_DEFAULT = "0x1000" +IMG_OFFSET_DEFAULT = "0x4000" + +def split_dtb_bin(i : str): + return i.split(MANIFEST_IMAGE_SPLITTER) + +def align_to_page(n): + return HF_PAGE_SIZE * \ + (round(n / HF_PAGE_SIZE) + \ + (1 if n % HF_PAGE_SIZE else 0)) + +def to_bytes(value): + return int(value).to_bytes(HEADER_ELEMENT_BYTES, 'little') + +class SpPkg: + def __init__(self, pm_path : str, img_path : str, pm_offset: int, + img_offset: int): + if not os.path.isfile(pm_path) or not os.path.isfile(img_path): + raise Exception(f"Parameters should be path. \ + manifest: {pm_path}; img: {img_path}") + self.pm_path = pm_path + self.img_path = img_path + self._SpPkgHeader = namedtuple("SpPkgHeader", + ("magic", "version", + "pm_offset", "pm_size", + "img_offset", "img_size")) + + if pm_offset >= img_offset: + raise ValueError("pm_offset must be smaller than img_offset") + + is_hfpage_aligned = lambda val : val % HF_PAGE_SIZE == 0 + if not is_hfpage_aligned(pm_offset) or not is_hfpage_aligned(img_offset): + raise ValueError(f"Offsets provided need to be page aligned: pm-{pm_offset}, img-{img_offset}") + + if img_offset - pm_offset < self.pm_size: + raise ValueError(f"pm_offset and img_offset do not fit the specified file:{pm_path})") + + self.pm_offset = pm_offset + self.img_offset = img_offset + + def __str__(self): + return \ + f'''--SP package Info-- + header:{self.header} + pm: {self.pm_path} + img: {self.img_path} + ''' + + @property + def magic(self): + return "SPKG".encode() + + @property + def version(self): + return 0x2 + + @property + def pm_size(self): + return os.path.getsize(self.pm_path) + + @property + def img_size(self): + return os.path.getsize(self.img_path) + + @property + def header(self): + return self._SpPkgHeader( + self.magic, + self.version, + self.pm_offset, + self.pm_size, + self.img_offset, + self.img_size) + + @property + def header_size(self): + return len(self._SpPkgHeader._fields) + + def generate(self, f_out : str): + with open(f_out, "wb+") as output: + for h in self.header: + to_write = h if type(h) is bytes else to_bytes(h) + output.write(to_write) + output.seek(self.pm_offset) + with open(self.pm_path, "rb") as pm: + copyfileobj(pm, output) + output.seek(self.img_offset) + with open(self.img_path, "rb") as img: + copyfileobj(img, output) + +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument("-i", required=True, + help="path to partition's image and manifest separated by a colon.") + parser.add_argument("--pm-offset", required=False, default=PM_OFFSET_DEFAULT, + help="set partitition manifest offset.") + parser.add_argument("--img-offset", required=False, default=IMG_OFFSET_DEFAULT, + help="set partition image offset.") + parser.add_argument("-o", required=True, help="set output file path.") + parser.add_argument("-v", required=False, action="store_true", + help="print package information.") + args = parser.parse_args() + + if not os.path.exists(os.path.dirname(args.o)): + raise Exception("Provide a valid output file path!\n") + + image_path, manifest_path = split_dtb_bin(args.i) + pm_offset = int(args.pm_offset, 0) + img_offset = int(args.img_offset, 0) + pkg = SpPkg(manifest_path, image_path, pm_offset, img_offset) + pkg.generate(args.o) + + if args.v is True: + print(pkg) + + return 0 + +if __name__ == "__main__": + sys.exit(Main()) diff --git a/atf-20250711/tools/stm32image/Makefile b/atf-20250711/tools/stm32image/Makefile new file mode 100644 index 000000000..64b6ccfab --- /dev/null +++ b/atf-20250711/tools/stm32image/Makefile @@ -0,0 +1,41 @@ +# +# Copyright (c) 2017-2025, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +MAKE_HELPERS_DIRECTORY := ../../make_helpers/ +include ${MAKE_HELPERS_DIRECTORY}build_macros.mk +include ${MAKE_HELPERS_DIRECTORY}common.mk +include ${MAKE_HELPERS_DIRECTORY}toolchain.mk + +PROJECT := stm32image$(.exe) +OBJECTS := stm32image.o + +HOSTCCFLAGS := -Wall -Werror -pedantic -std=c99 -D_GNU_SOURCE + +ifeq (${DEBUG},1) + HOSTCCFLAGS += -g -O0 -DDEBUG +else + HOSTCCFLAGS += -O2 +endif + +.PHONY: all clean distclean + +all: ${PROJECT} + +${PROJECT}: ${OBJECTS} Makefile + $(s)echo " HOSTLD $@" + $(q)$(host-cc) ${OBJECTS} -o $@ + $(s)echo + $(s)echo "Built $@ successfully" + $(s)echo + +%.o: %.c Makefile + $(s)echo " HOSTCC $<" + $(q)$(host-cc) -c ${HOSTCCFLAGS} $< -o $@ + +clean: + $(q)rm -rf $(PROJECT) $(OBJECTS) + +distclean: clean diff --git a/atf-20250711/tools/stm32image/stm32image.c b/atf-20250711/tools/stm32image/stm32image.c new file mode 100644 index 000000000..bd4720c5c --- /dev/null +++ b/atf-20250711/tools/stm32image/stm32image.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Magic = 'S' 'T' 'M' 0x32 */ +#define HEADER_MAGIC __be32_to_cpu(0x53544D32) +#define VER_MAJOR 2 +#define VER_MINOR 1 +#define VER_VARIANT 0 +#define HEADER_VERSION_V1 0x1 +#define HEADER_VERSION_V2 0x2 +#define PADDING_HEADER_MAGIC __be32_to_cpu(0x5354FFFF) +#define PADDING_HEADER_FLAG (1 << 31) +#define PADDING_HEADER_LENGTH 0x180 + +struct stm32_header_v1 { + uint32_t magic_number; + uint8_t image_signature[64]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t option_flags; + uint32_t ecdsa_algorithm; + uint8_t ecdsa_public_key[64]; + uint8_t padding[83]; + uint8_t binary_type; +}; + +struct stm32_header_v2 { + uint32_t magic_number; + uint8_t image_signature[64]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t extension_flags; + uint32_t extension_headers_length; + uint32_t binary_type; + uint8_t padding[16]; + uint32_t extension_header_type; + uint32_t extension_header_length; + uint8_t extension_padding[376]; +}; + +static void stm32image_default_header(void *ptr) +{ + struct stm32_header_v1 *header = (struct stm32_header_v1 *)ptr; + + if (!header) { + return; + } + + header->magic_number = HEADER_MAGIC; + header->version_number = __cpu_to_le32(0); +} + +static uint32_t stm32image_checksum(void *start, uint32_t len, + uint32_t header_size) +{ + uint32_t csum = 0; + uint8_t *p; + + if (len < header_size) { + return 0; + } + + p = (unsigned char *)start + header_size; + len -= header_size; + + while (len > 0) { + csum += *p; + p++; + len--; + } + + return csum; +} + +static void stm32image_print_header(const void *ptr) +{ + struct stm32_header_v1 *stm32hdr = (struct stm32_header_v1 *)ptr; + struct stm32_header_v2 *stm32hdr_v2 = (struct stm32_header_v2 *)ptr; + + printf("Image Type : ST Microelectronics STM32 V%d.%d\n", + stm32hdr->header_version[VER_MAJOR], + stm32hdr->header_version[VER_MINOR]); + printf("Image Size : %lu bytes\n", + (unsigned long)__le32_to_cpu(stm32hdr->image_length)); + printf("Image Load : 0x%08x\n", + __le32_to_cpu(stm32hdr->load_address)); + printf("Entry Point : 0x%08x\n", + __le32_to_cpu(stm32hdr->image_entry_point)); + printf("Checksum : 0x%08x\n", + __le32_to_cpu(stm32hdr->image_checksum)); + + switch (stm32hdr->header_version[VER_MAJOR]) { + case HEADER_VERSION_V1: + printf("Option : 0x%08x\n", + __le32_to_cpu(stm32hdr->option_flags)); + break; + + case HEADER_VERSION_V2: + printf("Extension : 0x%08x\n", + __le32_to_cpu(stm32hdr_v2->extension_flags)); + break; + + default: + printf("Incorrect header version\n"); + } + + printf("Version : 0x%08x\n", + __le32_to_cpu(stm32hdr->version_number)); +} + +static int stm32image_set_header(void *ptr, struct stat *sbuf, int ifd, + uint32_t loadaddr, uint32_t ep, uint32_t ver, + uint32_t major, uint32_t minor, + uint32_t binary_type, uint32_t header_size) +{ + struct stm32_header_v1 *stm32hdr = (struct stm32_header_v1 *)ptr; + struct stm32_header_v2 *stm32hdr_v2 = (struct stm32_header_v2 *)ptr; + uint32_t ext_size = 0U; + uint32_t ext_flags = 0U; + + stm32image_default_header(ptr); + + stm32hdr->header_version[VER_MAJOR] = major; + stm32hdr->header_version[VER_MINOR] = minor; + stm32hdr->load_address = __cpu_to_le32(loadaddr); + stm32hdr->image_entry_point = __cpu_to_le32(ep); + stm32hdr->image_length = __cpu_to_le32((uint32_t)sbuf->st_size - + header_size); + stm32hdr->image_checksum = + __cpu_to_le32(stm32image_checksum(ptr, sbuf->st_size, + header_size)); + + switch (stm32hdr->header_version[VER_MAJOR]) { + case HEADER_VERSION_V1: + /* Default option for header v1 : bit0 => no signature */ + stm32hdr->option_flags = __cpu_to_le32(0x00000001); + stm32hdr->ecdsa_algorithm = __cpu_to_le32(1); + stm32hdr->binary_type = (uint8_t)binary_type; + break; + + case HEADER_VERSION_V2: + stm32hdr_v2->binary_type = binary_type; + ext_size += PADDING_HEADER_LENGTH; + ext_flags |= PADDING_HEADER_FLAG; + stm32hdr_v2->extension_flags = + __cpu_to_le32(ext_flags); + stm32hdr_v2->extension_headers_length = + __cpu_to_le32(ext_size); + stm32hdr_v2->extension_header_type = PADDING_HEADER_MAGIC; + stm32hdr_v2->extension_header_length = + __cpu_to_le32(PADDING_HEADER_LENGTH); + break; + + default: + return -1; + } + + stm32hdr->version_number = __cpu_to_le32(ver); + + return 0; +} + +static int stm32image_create_header_file(char *srcname, char *destname, + uint32_t loadaddr, uint32_t entry, + uint32_t version, uint32_t major, + uint32_t minor, uint32_t binary_type) +{ + int src_fd, dest_fd, header_size; + struct stat sbuf; + unsigned char *ptr; + void *stm32image_header; + + dest_fd = open(destname, O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0666); + if (dest_fd == -1) { + fprintf(stderr, "Can't open %s: %s\n", destname, + strerror(errno)); + return -1; + } + + src_fd = open(srcname, O_RDONLY); + if (src_fd == -1) { + fprintf(stderr, "Can't open %s: %s\n", srcname, + strerror(errno)); + return -1; + } + + if (fstat(src_fd, &sbuf) < 0) { + return -1; + } + + ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, src_fd, 0); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't read %s\n", srcname); + return -1; + } + + switch (major) { + case HEADER_VERSION_V1: + stm32image_header = malloc(sizeof(struct stm32_header_v1)); + header_size = sizeof(struct stm32_header_v1); + break; + + case HEADER_VERSION_V2: + stm32image_header = malloc(sizeof(struct stm32_header_v2)); + header_size = sizeof(struct stm32_header_v2); + break; + + default: + return -1; + } + + memset(stm32image_header, 0, header_size); + if (write(dest_fd, stm32image_header, header_size) != + header_size) { + fprintf(stderr, "Write error %s: %s\n", destname, + strerror(errno)); + free(stm32image_header); + return -1; + } + + free(stm32image_header); + + if (write(dest_fd, ptr, sbuf.st_size) != sbuf.st_size) { + fprintf(stderr, "Write error on %s: %s\n", destname, + strerror(errno)); + return -1; + } + + munmap((void *)ptr, sbuf.st_size); + close(src_fd); + + if (fstat(dest_fd, &sbuf) < 0) { + return -1; + } + + ptr = mmap(0, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + dest_fd, 0); + + if (ptr == MAP_FAILED) { + fprintf(stderr, "Can't write %s\n", destname); + return -1; + } + + if (stm32image_set_header(ptr, &sbuf, dest_fd, loadaddr, + entry, version, major, minor, + binary_type, header_size) != 0) { + return -1; + } + + stm32image_print_header(ptr); + + munmap((void *)ptr, sbuf.st_size); + close(dest_fd); + return 0; +} + +int main(int argc, char *argv[]) +{ + int opt; + int loadaddr = -1; + int entry = -1; + int err = 0; + int version = 0; + int binary_type = -1; + int major = HEADER_VERSION_V2; + int minor = 0; + char *dest = NULL; + char *src = NULL; + + while ((opt = getopt(argc, argv, ":b:s:d:l:e:v:m:n:")) != -1) { + switch (opt) { + case 'b': + binary_type = strtol(optarg, NULL, 0); + break; + case 's': + src = optarg; + break; + case 'd': + dest = optarg; + break; + case 'l': + loadaddr = strtol(optarg, NULL, 0); + break; + case 'e': + entry = strtol(optarg, NULL, 0); + break; + case 'v': + version = strtol(optarg, NULL, 0); + break; + case 'm': + major = strtol(optarg, NULL, 0); + break; + case 'n': + minor = strtol(optarg, NULL, 0); + break; + default: + fprintf(stderr, + "Usage : %s [-s srcfile] [-d destfile] [-l loadaddr] [-e entry_point] [-m major] [-n minor] [-b binary_type]\n", + argv[0]); + return -1; + } + } + + if (!src) { + fprintf(stderr, "Missing -s option\n"); + return -1; + } + + if (!dest) { + fprintf(stderr, "Missing -d option\n"); + return -1; + } + + if (loadaddr == -1) { + fprintf(stderr, "Missing -l option\n"); + return -1; + } + + if (entry == -1) { + fprintf(stderr, "Missing -e option\n"); + return -1; + } + + if (binary_type == -1) { + fprintf(stderr, "Missing -b option\n"); + return -1; + } + + err = stm32image_create_header_file(src, dest, loadaddr, + entry, version, major, minor, + binary_type); + + return err; +} diff --git a/atf-20250711/tools/tlc/.gitignore b/atf-20250711/tools/tlc/.gitignore new file mode 100644 index 000000000..ad4a1f17f --- /dev/null +++ b/atf-20250711/tools/tlc/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/atf-20250711/tools/tlc/assets/images/coverage.svg b/atf-20250711/tools/tlc/assets/images/coverage.svg new file mode 100644 index 000000000..b6c4e361f --- /dev/null +++ b/atf-20250711/tools/tlc/assets/images/coverage.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + coverage + coverage + 95% + 95% + + diff --git a/atf-20250711/tools/tlc/poetry.lock b/atf-20250711/tools/tlc/poetry.lock new file mode 100644 index 000000000..3562de99d --- /dev/null +++ b/atf-20250711/tools/tlc/poetry.lock @@ -0,0 +1,1560 @@ +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. + +[[package]] +name = "astroid" +version = "2.15.8" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.7.2" +groups = ["dev"] +files = [ + {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, + {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, +] + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} +wrapt = [ + {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, +] + +[[package]] +name = "bandit" +version = "1.7.10" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "bandit-1.7.10-py3-none-any.whl", hash = "sha256:665721d7bebbb4485a339c55161ac0eedde27d51e638000d91c8c2d68343ad02"}, + {file = "bandit-1.7.10.tar.gz", hash = "sha256:59ed5caf5d92b6ada4bf65bc6437feea4a9da1093384445fed4d472acc6cff7b"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""] +yaml = ["PyYAML"] + +[[package]] +name = "bandit" +version = "1.8.3" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version >= \"3.11\"" +files = [ + {file = "bandit-1.8.3-py3-none-any.whl", hash = "sha256:28f04dc0d258e1dd0f99dee8eefa13d1cb5e3fde1a5ab0c523971f97b289bcd8"}, + {file = "bandit-1.8.3.tar.gz", hash = "sha256:f5847beb654d309422985c36644649924e0ea4425c76dec2e89110b87506193a"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""] +yaml = ["PyYAML"] + +[[package]] +name = "black" +version = "24.8.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.5.2" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, + {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +groups = ["dev"] +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +optional = false +python-versions = "*" +groups = ["main", "dev"] +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "coverage" +version = "6.5.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "coverage-badge" +version = "1.1.2" +description = "Generate coverage badges for Coverage.py." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "coverage_badge-1.1.2-py2.py3-none-any.whl", hash = "sha256:d8413ce51c91043a1692b943616b450868cbeeb0ea6a0c54a32f8318c9c96ff7"}, + {file = "coverage_badge-1.1.2.tar.gz", hash = "sha256:fe7ed58a3b72dad85a553b64a99e963dea3847dcd0b8ddd2b38a00333618642c"}, +] + +[package.dependencies] +coverage = "*" +setuptools = "*" + +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +optional = false +python-versions = ">=3.6,<4.0" +groups = ["dev"] +files = [ + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +groups = ["main", "dev"] +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "dparse" +version = "0.6.3" +description = "A parser for Python dependency files" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, + {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, +] + +[package.dependencies] +packaging = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv (<=2022.12.19)"] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] + +[[package]] +name = "identify" +version = "2.6.1" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +groups = ["dev"] +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.dependencies] +colorama = {version = ">=0.4.6", optional = true, markers = "extra == \"colors\""} + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.10.0" +description = "A fast and thorough lazy object proxy." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy" +version = "0.910" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.5" +groups = ["dev"] +files = [ + {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, + {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, + {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, + {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, + {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, + {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, + {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, + {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, + {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, + {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, + {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, + {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, + {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, + {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, + {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, + {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, + {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, + {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, + {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, + {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, + {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, + {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, + {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3,<0.5.0" +toml = "*" +typing-extensions = ">=3.7.4" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.4" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +optional = false +python-versions = ">=2.7" +groups = ["dev"] +files = [ + {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pbr" +version = "6.1.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +groups = ["dev"] +files = [ + {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, + {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "2.17.7" +description = "python code static checker" +optional = false +python-versions = ">=3.7.2" +groups = ["dev"] +files = [ + {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, + {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, +] + +[package.dependencies] +astroid = ">=2.15.8,<=2.17.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyproject-api" +version = "1.8.0" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, + {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, +] + +[package.dependencies] +packaging = ">=24.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] + +[[package]] +name = "pytest" +version = "8.3.5" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-html" +version = "4.1.1" +description = "pytest plugin for generating HTML reports" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytest_html-4.1.1-py3-none-any.whl", hash = "sha256:c8152cea03bd4e9bee6d525573b67bbc6622967b72b9628dda0ea3e2a0b5dd71"}, + {file = "pytest_html-4.1.1.tar.gz", hash = "sha256:70a01e8ae5800f4a074b56a4cb1025c8f4f9b038bba5fe31e3c98eb996686f07"}, +] + +[package.dependencies] +jinja2 = ">=3.0.0" +pytest = ">=7.0.0" +pytest-metadata = ">=2.0.0" + +[package.extras] +docs = ["pip-tools (>=6.13.0)"] +test = ["assertpy (>=1.1)", "beautifulsoup4 (>=4.11.1)", "black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "pytest-mock (>=3.7.0)", "pytest-rerunfailures (>=11.1.2)", "pytest-xdist (>=2.4.0)", "selenium (>=4.3.0)", "tox (>=3.24.5)"] + +[[package]] +name = "pytest-metadata" +version = "3.1.1" +description = "pytest plugin for test session metadata" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, + {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>=3.24.5)"] + +[[package]] +name = "pyupgrade" +version = "2.38.4" +description = "A tool to automatically upgrade syntax for newer versions." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "pyupgrade-2.38.4-py2.py3-none-any.whl", hash = "sha256:944ff993c396ddc2b9012eb3de4cda138eb4c149b22c6c560d4c8bfd0e180982"}, + {file = "pyupgrade-2.38.4.tar.gz", hash = "sha256:1eb43a49f416752929741ba4d706bf3f33593d3cac9bdc217fc1ef55c047c1f4"}, +] + +[package.dependencies] +tokenize-rt = "<5" + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "10.16.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.6.2,<4.0.0" +groups = ["main", "dev"] +files = [ + {file = "rich-10.16.2-py3-none-any.whl", hash = "sha256:c59d73bd804c90f747c8d7b1d023b88f2a9ac2454224a4aeaf959b21eeb42d03"}, + {file = "rich-10.16.2.tar.gz", hash = "sha256:720974689960e06c2efdb54327f8bf0cdbdf4eae4ad73b6c94213cad405c371b"}, +] + +[package.dependencies] +colorama = ">=0.4.0,<0.5.0" +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + +[[package]] +name = "ruamel-yaml" +version = "0.18.6" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\"" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + +[[package]] +name = "safety" +version = "2.3.4" +description = "Checks installed dependencies for known vulnerabilities and licenses." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "safety-2.3.4-py3-none-any.whl", hash = "sha256:6224dcd9b20986a2b2c5e7acfdfba6bca42bb11b2783b24ed04f32317e5167ea"}, + {file = "safety-2.3.4.tar.gz", hash = "sha256:b9e74e794e82f54d11f4091c5d820c4d2d81de9f953bf0b4f33ac8bc402ae72c"}, +] + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = ">=19.3" + +[package.extras] +github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "setuptools" +version = "75.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, + {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.5.2) ; sys_platform != \"cygwin\""] +core = ["importlib-metadata (>=6) ; python_version < \"3.10\"", "importlib-resources (>=5.10.2) ; python_version < \"3.9\"", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.11.*)", "pytest-mypy"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "stevedore" +version = "5.3.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "stevedore-5.3.0-py3-none-any.whl", hash = "sha256:1efd34ca08f474dad08d9b19e934a22c68bb6fe416926479ba29e5013bcc8f78"}, + {file = "stevedore-5.3.0.tar.gz", hash = "sha256:9a64265f4060312828151c204efbe9b7a9852a0d9228756344dbc7e4023e375a"}, +] + +[package.dependencies] +pbr = ">=2.0.0" + +[[package]] +name = "tokenize-rt" +version = "4.2.1" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +optional = false +python-versions = ">=3.6.1" +groups = ["dev"] +files = [ + {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, + {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, +] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, +] + +[[package]] +name = "tox" +version = "4.24.2" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "tox-4.24.2-py3-none-any.whl", hash = "sha256:92e8290e76ad4e15748860a205865696409a2d014eedeb796a34a0f3b5e7336e"}, + {file = "tox-4.24.2.tar.gz", hash = "sha256:d5948b350f76fae436d6545a5e87c2b676ab7a0d7d88c1308651245eadbe8aea"}, +] + +[package.dependencies] +cachetools = ">=5.5.1" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.16.1" +packaging = ">=24.2" +platformdirs = ">=4.3.6" +pluggy = ">=1.5" +pyproject-api = ">=1.8" +tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} +virtualenv = ">=20.29.1" + +[package.extras] +test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.4)", "pytest-mock (>=3.14)"] + +[[package]] +name = "typer" +version = "0.4.2" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "typer-0.4.2-py3-none-any.whl", hash = "sha256:023bae00d1baf358a6cc7cea45851639360bb716de687b42b0a4641cd99173f1"}, + {file = "typer-0.4.2.tar.gz", hash = "sha256:b8261c6c0152dd73478b5ba96ba677e5d6948c715c310f7c91079f311f62ec03"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +colorama = {version = ">=0.4.3,<0.5.0", optional = true, markers = "extra == \"all\""} +shellingham = {version = ">=1.3.0,<2.0.0", optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] +markers = {main = "python_version < \"3.11\""} + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.29.3" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "virtualenv-20.29.3-py3-none-any.whl", hash = "sha256:3e3d00f5807e83b234dfb6122bf37cfadf4be216c53a49ac059d02414f819170"}, + {file = "virtualenv-20.29.3.tar.gz", hash = "sha256:95e39403fcf3940ac45bc717597dba16110b74506131845d9b687d5e73d947ac"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] + +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[metadata] +lock-version = "2.1" +python-versions = "^3.8" +content-hash = "a4b9c3bababadba14f49a8de0ccee1f5a141b6cea23d02a19ab8bf4f8c45533f" diff --git a/atf-20250711/tools/tlc/pyproject.toml b/atf-20250711/tools/tlc/pyproject.toml new file mode 100644 index 000000000..e9ff26f61 --- /dev/null +++ b/atf-20250711/tools/tlc/pyproject.toml @@ -0,0 +1,151 @@ +# Poetry pyproject.toml: https://python-poetry.org/docs/pyproject/ +[build-system] +requires = ["poetry_core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "tlc" +version = "0.9.0" +description = "Transfer List Compiler (TLC) is a Python-based CLI for efficiently handling transfer lists." +authors = ["Arm Ltd "] +license = "BSD-3" +repository = "https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/" +homepage = "https://trustedfirmware-a.readthedocs.io/en/latest/index.html" + +# Keywords description https://python-poetry.org/docs/pyproject/#keywords +keywords = [] #! Update me + +# Pypi classifiers: https://pypi.org/classifiers/ +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", +] + +[tool.poetry.scripts] +# Entry points for the package https://python-poetry.org/docs/pyproject/#scripts +"tlc" = "tlc.__main__:cli" + +[tool.poetry.dependencies] +python = "^3.8" + +typer = {extras = ["all"], version = "^0.4.0"} +rich = "^10.14.0" +click = "^8.1.7" +pyyaml = "^6.0.1" +tox = "^4.18.0" +jinja2 = "^3.1.5" + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +bandit = "^1.7.1" +tox = "^4.18.0" +darglint = "^1.8.1" +black = "^24.4.2" +isort = {extras = ["colors"], version = "^5.10.1"} +mypy = "^0.910" +mypy-extensions = "^0.4.3" +pre-commit = "^2.15.0" +pydocstyle = "^6.1.1" +pylint = "^2.11.1" +pytest = "^8.0.0" +pyupgrade = "^2.29.1" +safety = "^2.2.0" +coverage = "^6.1.2" +coverage-badge = "^1.1.0" +pytest-html = "^4.1.1" +pytest-cov = "5.0.0" + +[tool.black] +# https://github.com/psf/black +target-version = ["py38"] +line-length = 88 +color = true + +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | env + | venv +)/ +''' + +[tool.isort] +# https://github.com/timothycrosley/isort/ +py_version = 38 +line_length = 88 + +known_typing = ["typing", "types", "typing_extensions", "mypy", "mypy_extensions"] +sections = ["FUTURE", "TYPING", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] +include_trailing_comma = true +profile = "black" +multi_line_output = 3 +indent = 4 +color_output = true + +[tool.mypy] +# https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file +python_version = 3.8 +pretty = true +show_traceback = true +color_output = true + +allow_redefinition = false +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +ignore_missing_imports = true +implicit_reexport = false +no_implicit_optional = true +show_column_numbers = true +show_error_codes = true +show_error_context = true +strict_equality = true +strict_optional = true +warn_no_return = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + + +[tool.pytest.ini_options] +# https://docs.pytest.org/en/6.2.x/customize.html#pyproject-toml +# Directories that are not visited by pytest collector: +norecursedirs =["hooks", "*.egg", ".eggs", "dist", "build", "docs", ".tox", ".git", "__pycache__"] +doctest_optionflags = ["NUMBER", "NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"] + +# Extra options: +addopts = [ + "--strict-markers", + "--tb=short", + "--doctest-modules", + "--doctest-continue-on-failure", +] + +[tool.coverage.run] +source = ["tests"] +branch = true + +[tool.coverage.paths] +source = ["tlc"] + +[tool.coverage.report] +fail_under = 50 +show_missing = true diff --git a/atf-20250711/tools/tlc/setup.cfg b/atf-20250711/tools/tlc/setup.cfg new file mode 100644 index 000000000..3c46a08cc --- /dev/null +++ b/atf-20250711/tools/tlc/setup.cfg @@ -0,0 +1,4 @@ +[darglint] +# https://github.com/terrencepreilly/darglint +strictness = long +docstring_style = google diff --git a/atf-20250711/tools/tlc/tests/conftest.py b/atf-20250711/tools/tlc/tests/conftest.py new file mode 100644 index 000000000..93e44a9f5 --- /dev/null +++ b/atf-20250711/tools/tlc/tests/conftest.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +""" Common configurations and fixtures for test environment.""" + +from random import randint + +import pytest +import yaml +from click.testing import CliRunner + +from tlc.cli import cli + + +def generate_random_bytes(n): + return bytes([randint(0, 255) for _ in range(n)]) + + +@pytest.fixture +def tmptlstr(tmpdir): + return tmpdir.join("tl.bin").strpath + + +@pytest.fixture +def tmpyamlconfig(tmpdir): + return tmpdir.join("config.yaml").strpath + + +@pytest.fixture +def tmpfdt(tmpdir): + fdt = tmpdir.join("fdt.dtb") + fdt.write_binary(b"\x00" * 100) + return fdt + + +@pytest.fixture(params=[1, 2, 3, 4, 5, 0x100, 0x101, 0x102, 0x104]) +def non_empty_tag_id(request): + return request.param + + +@pytest.fixture +def tmpyamlconfig_blob_file(tmpdir, tmpfdt, non_empty_tag_id): + config_path = tmpdir.join("config.yaml") + + config = { + "has_checksum": True, + "max_size": 0x1000, + "entries": [ + { + "tag_id": non_empty_tag_id, + "blob_file_path": tmpfdt.strpath, + }, + ], + } + + with open(config_path, "w") as f: + yaml.safe_dump(config, f) + + return config_path + + +@pytest.fixture +def tlcrunner(tmptlstr): + runner = CliRunner() + with runner.isolated_filesystem(): + runner.invoke(cli, ["create", "--size", 0x1F000, tmptlstr]) + return runner + + +@pytest.fixture +def tlc_entries(tmpfdt): + return [(0, "/dev/null"), (1, tmpfdt.strpath), (0x102, tmpfdt.strpath)] + + +@pytest.fixture +def random_entry(): + def _random_entry(max_size): + return randint(0, 0xFFFFFF), generate_random_bytes(randint(0, max_size)) + + return _random_entry + + +@pytest.fixture +def random_entries(random_entry): + def _random_entries(n=5, max_size=0x100): + for _ in range(n): + yield random_entry(max_size) + + return _random_entries diff --git a/atf-20250711/tools/tlc/tests/test_cli.py b/atf-20250711/tools/tlc/tests/test_cli.py new file mode 100644 index 000000000..ebe1f6a84 --- /dev/null +++ b/atf-20250711/tools/tlc/tests/test_cli.py @@ -0,0 +1,507 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Contains unit tests for the CLI functionality.""" + +from math import ceil, log2 +from pathlib import Path +from re import findall, search +from unittest import mock + +import pytest +import yaml +from click.testing import CliRunner +from conftest import generate_random_bytes + +from tlc.cli import cli +from tlc.te import TransferEntry +from tlc.tl import TransferList + + +def test_create_empty_tl(tmpdir): + runner = CliRunner() + test_file = tmpdir.join("tl.bin") + + result = runner.invoke(cli, ["create", test_file.strpath]) + assert result.exit_code == 0 + assert TransferList.fromfile(test_file) is not None + + +@pytest.mark.parametrize("align", [4, 6, 12, 13]) +def test_create_with_align(align, tlcrunner, tmpdir): + tl_file = tmpdir.join("tl.bin").strpath + tlcrunner.invoke(cli, ["create", "-s", "10000", "-a", align, tl_file]) + + blob = tmpdir.join("blob.bin") + + blob.write_binary(generate_random_bytes(0x200)) + tlcrunner.invoke(cli, ["add", "--entry", 1, blob.strpath, tl_file]) + + tl = TransferList.fromfile(tl_file) + te = tl.entries[-1] + assert tl.alignment == align + assert (te.offset + te.hdr_size) % (1 << align) == 0 + + +def test_create_with_fdt(tmpdir): + runner = CliRunner() + fdt = tmpdir.join("fdt.dtb") + fdt.write_binary(b"\x00" * 100) + + result = runner.invoke( + cli, + [ + "create", + "--fdt", + fdt.strpath, + "--size", + "1000", + tmpdir.join("tl.bin").strpath, + ], + ) + assert result.exit_code == 0 + + +def test_add_single_entry(tlcrunner, tmptlstr): + tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr]) + + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + assert tl.entries[0].id == 0 + + +def test_add_multiple_entries(tlcrunner, tlc_entries, tmptlstr): + for id, path in tlc_entries: + tlcrunner.invoke(cli, ["add", "--entry", id, path, tmptlstr]) + + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == len(tlc_entries) + + +@pytest.mark.parametrize("align", [4, 6, 12, 13]) +def test_cli_add_entry_with_align(align, tlcrunner, tmpdir, tmptlstr): + blob = tmpdir.join("blob.bin") + blob.write_binary(bytes(0x100)) + + tlcrunner.invoke(cli, ["add", "--align", align, "--entry", 1, blob, tmptlstr]) + tl = TransferList.fromfile(tmptlstr) + te = tl.entries[-1] + + print(tl, *(te for te in tl.entries), sep="\n---------------\n") + assert (te.offset + te.hdr_size) % (1 << align) == 0 + assert tl.alignment == align + + +def test_info(tlcrunner, tmptlstr, tmpfdt): + tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr]) + tlcrunner.invoke(cli, ["add", "--fdt", tmpfdt.strpath, tmptlstr]) + + result = tlcrunner.invoke(cli, ["info", tmptlstr]) + assert result.exit_code == 0 + assert "signature" in result.stdout + assert "id" in result.stdout + + result = tlcrunner.invoke(cli, ["info", "--header", tmptlstr]) + assert result.exit_code == 0 + assert "signature" in result.stdout + assert "id" not in result.stdout + + result = tlcrunner.invoke(cli, ["info", "--entries", tmptlstr]) + assert result.exit_code == 0 + assert "signature" not in result.stdout + assert "id" in result.stdout + + +def test_raises_max_size_error(tmptlstr, tmpfdt): + tmpfdt.write_binary(bytes(6000)) + + runner = CliRunner() + result = runner.invoke(cli, ["create", "--fdt", tmpfdt, tmptlstr]) + + assert result.exception + assert isinstance(result.exception, MemoryError) + assert "TL max size exceeded, consider increasing with the option -s" in str( + result.exception + ) + assert "TL size has exceeded the maximum allocation" in str( + result.exception.__cause__ + ) + + +def test_info_get_fdt_offset(tmptlstr, tmpfdt): + runner = CliRunner() + with runner.isolated_filesystem(): + runner.invoke(cli, ["create", "--size", "1000", tmptlstr]) + runner.invoke(cli, ["add", "--entry", "1", tmpfdt.strpath, tmptlstr]) + result = runner.invoke(cli, ["info", "--fdt-offset", tmptlstr]) + + assert result.exit_code == 0 + assert result.output.strip("\n").isdigit() + + +def test_remove_tag(tlcrunner, tmptlstr): + tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr]) + result = tlcrunner.invoke(cli, ["info", tmptlstr]) + + assert result.exit_code == 0 + assert "signature" in result.stdout + + tlcrunner.invoke(cli, ["remove", "--tags", "0", tmptlstr]) + tl = TransferList.fromfile(tmptlstr) + + assert result.exit_code == 0 + assert len(tl.entries) == 0 + + +def test_unpack_tl(tlcrunner, tmptlstr, tmpfdt, tmpdir): + with tlcrunner.isolated_filesystem(temp_dir=tmpdir): + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["unpack", tmptlstr]) + assert Path("te_0_1.bin").exists() + + +def test_unpack_multiple_tes(tlcrunner, tlc_entries, tmptlstr, tmpdir): + with tlcrunner.isolated_filesystem(temp_dir=tmpdir): + for id, path in tlc_entries: + tlcrunner.invoke(cli, ["add", "--entry", id, path, tmptlstr]) + + assert all( + filter( + lambda te: (Path(tmpdir.strpath) / f"te_{te[0]}.bin").exists(), tlc_entries + ) + ) + + +def test_unpack_into_dir(tlcrunner, tmpdir, tmptlstr, tmpfdt): + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["unpack", "-C", tmpdir.strpath, tmptlstr]) + + assert (Path(tmpdir.strpath) / "te_0_1.bin").exists() + + +def test_unpack_into_dir_with_conflicting_tags(tlcrunner, tmpdir, tmptlstr, tmpfdt): + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr]) + tlcrunner.invoke(cli, ["unpack", "-C", tmpdir.strpath, tmptlstr]) + + assert (Path(tmpdir.strpath) / "te_0_1.bin").exists() + assert (Path(tmpdir.strpath) / "te_1_1.bin").exists() + + +def test_validate_invalid_signature(tmptlstr, tlcrunner, monkeypatch): + tl = TransferList() + tl.signature = 0xDEADBEEF + + mock_open = lambda tmptlstr, mode: mock.mock_open(read_data=tl.header_to_bytes())() + monkeypatch.setattr("builtins.open", mock_open) + + result = tlcrunner.invoke(cli, ["validate", tmptlstr]) + assert result.exit_code != 0 + + +def test_validate_misaligned_entries(tmptlstr, tlcrunner, monkeypatch): + """Base address of a TE must be 8-byte aligned.""" + mock_open = lambda tmptlstr, mode: mock.mock_open( + read_data=TransferList().header_to_bytes() + + bytes(5) + + TransferEntry(0, 0, bytes(0)).header_to_bytes + )() + monkeypatch.setattr("builtins.open", mock_open) + + result = tlcrunner.invoke(cli, ["validate", tmptlstr]) + + assert result.exit_code == 1 + + +@pytest.mark.parametrize( + "version", [0, TransferList.version, TransferList.version + 1, 1 << 8] +) +def test_validate_unsupported_version(version, tmptlstr, tlcrunner, monkeypatch): + tl = TransferList() + tl.version = version + + mock_open = lambda tmptlstr, mode: mock.mock_open(read_data=tl.header_to_bytes())() + monkeypatch.setattr("builtins.open", mock_open) + + result = tlcrunner.invoke(cli, ["validate", tmptlstr]) + + if version >= TransferList.version and version <= 0xFF: + assert result.exit_code == 0 + else: + assert result.exit_code == 1 + + +def test_create_entry_from_yaml_and_blob_file( + tlcrunner, tmpyamlconfig_blob_file, tmptlstr, non_empty_tag_id +): + tlcrunner.invoke( + cli, + [ + "create", + "--from-yaml", + tmpyamlconfig_blob_file.strpath, + tmptlstr, + ], + ) + + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + assert tl.entries[0].id == non_empty_tag_id + + +@pytest.mark.parametrize( + "entry", + [ + {"tag_id": 0}, + { + "tag_id": 0x104, + "addr": 0x0400100000000010, + "size": 0x0003300000000000, + }, + { + "tag_id": 0x100, + "pp_addr": 100, + }, + { + "tag_id": "optee_pageable_part", + "pp_addr": 100, + }, + ], +) +def test_create_from_yaml_check_sum_bytes(tlcrunner, tmpyamlconfig, tmptlstr, entry): + """Test creating a TL from a yaml file, but only check that the sum of the + data in the yaml file matches the sum of the data in the TL. This means + you don't have to type the exact sequence of expected bytes. All the data + in the yaml file must be integers (except for the tag IDs, which can be + strings). + """ + # create yaml config file + config = { + "has_checksum": True, + "max_size": 0x1000, + "entries": [entry], + } + with open(tmpyamlconfig, "w") as f: + yaml.safe_dump(config, f) + + # invoke TLC + tlcrunner.invoke( + cli, + [ + "create", + "--from-yaml", + tmpyamlconfig, + tmptlstr, + ], + ) + + # open created TL, and check + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + + # Check that the sum of all the data in the transfer entry in the yaml file + # is the same as the sum of all the data in the transfer list. Don't count + # the tag id or the TE headers. + + # every item in the entry dict must be an integer + yaml_total = 0 + for key, data in iter_nested_dict(entry): + if key != "tag_id": + num_bytes = ceil(log2(data + 1) / 8) + yaml_total += sum(data.to_bytes(num_bytes, "little")) + + tl_total = sum(tl.entries[0].data) + + assert tl_total == yaml_total + + +@pytest.mark.parametrize( + "entry,expected", + [ + ( + { + "tag_id": 0x102, + "ep_info": { + "h": { + "type": 0x01, + "version": 0x02, + "attr": 8, + }, + "pc": 67239936, + "spsr": 965, + "args": [67112976, 67112960, 0, 0, 0, 0, 0, 0], + }, + }, + ( + "0x00580201 0x00000008 0x04020000 0x00000000 " + "0x000003C5 0x00000000 0x04001010 0x00000000 " + "0x04001000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000" + ), + ), + ( + { + "tag_id": 0x102, + "ep_info": { + "h": { + "type": 0x01, + "version": 0x02, + "attr": "EP_NON_SECURE | EP_ST_ENABLE", + }, + "pc": 67239936, + "spsr": 965, + "args": [67112976, 67112960, 0, 0, 0, 0, 0, 0], + }, + }, + ( + "0x00580201 0x00000005 0x04020000 0x00000000 " + "0x000003C5 0x00000000 0x04001010 0x00000000 " + "0x04001000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000 0x00000000 0x00000000 " + "0x00000000 0x00000000" + ), + ), + ], +) +def test_create_from_yaml_check_exact_data( + tlcrunner, tmpyamlconfig, tmptlstr, entry, expected +): + """Test creating a TL from a yaml file, checking the exact sequence of + bytes. This is useful for checking that the alignment is correct. You can + get the expected sequence of bytes by copying it from the ArmDS debugger. + """ + # create yaml config file + config = { + "has_checksum": True, + "max_size": 0x1000, + "entries": [entry], + } + with open(tmpyamlconfig, "w") as f: + yaml.safe_dump(config, f) + + # invoke TLC + tlcrunner.invoke( + cli, + [ + "create", + "--from-yaml", + tmpyamlconfig, + tmptlstr, + ], + ) + + # open TL and check + tl = TransferList.fromfile(tmptlstr) + assert tl is not None + assert len(tl.entries) == 1 + + # check expected and actual data + actual = tl.entries[0].data + actual = bytes_to_hex(actual) + + assert actual == expected + + +@pytest.mark.parametrize("option", ["-O", "--output"]) +def test_gen_tl_header_with_output_name(tlcrunner, tmptlstr, option, filename="test.h"): + with tlcrunner.isolated_filesystem(): + result = tlcrunner.invoke( + cli, + [ + "gen-header", + option, + filename, + tmptlstr, + ], + ) + + assert result.exit_code == 0 + assert Path(filename).exists() + + +def test_gen_tl_with_fdt_header(tmptlstr, tmpfdt): + tlcrunner = CliRunner() + + with tlcrunner.isolated_filesystem(): + tlcrunner.invoke(cli, ["create", "--size", 1000, "--fdt", tmpfdt, tmptlstr]) + + result = tlcrunner.invoke( + cli, + [ + "gen-header", + tmptlstr, + ], + ) + + assert result.exit_code == 0 + assert Path("header.h").exists() + + with open("header.h", "r") as f: + dtb_match = search(r"DTB_OFFSET\s+(\d+)", "".join(f.readlines())) + assert dtb_match and dtb_match[1].isnumeric() + + +def test_gen_empty_tl_c_header(tlcrunner, tmptlstr): + with tlcrunner.isolated_filesystem(): + result = tlcrunner.invoke( + cli, + [ + "gen-header", + tmptlstr, + ], + ) + + assert result.exit_code == 0 + assert Path("header.h").exists() + + with open("header.h", "r") as f: + lines = "".join(f.readlines()) + + assert TransferList.hdr_size == int( + findall(r"SIZE\s+(0x[0-9a-fA-F]+|\d+)", lines)[0], 16 + ) + assert TransferList.version == int( + findall(r"VERSION.+(0x[0-9a-fA-F]+|\d+)", lines)[0] + ) + + +def bytes_to_hex(data: bytes) -> str: + """Convert bytes to a hex string in the same format as the debugger in + ArmDS + + You can copy data from the debugger in Arm Development Studio and put it + into a unit test. You can then run this function on the output from tlc, + and compare it to the data you copied. + + The format is groups of 4 bytes with 0x prefixes separated by spaces. + Little endian is used. + """ + words_hex = [] + for i in range(0, len(data), 4): + word = data[i : i + 4] + word_int = int.from_bytes(word, "little") + word_hex = "0x" + f"{word_int:0>8x}".upper() + words_hex.append(word_hex) + + return " ".join(words_hex) + + +def iter_nested_dict(dictionary: dict): + for key, value in dictionary.items(): + if isinstance(value, dict): + yield from iter_nested_dict(value) + else: + yield key, value diff --git a/atf-20250711/tools/tlc/tests/test_transfer_list.py b/atf-20250711/tools/tlc/tests/test_transfer_list.py new file mode 100644 index 000000000..6900b4144 --- /dev/null +++ b/atf-20250711/tools/tlc/tests/test_transfer_list.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Contains unit tests for the types TransferEntry and TransferList.""" + +import math +from random import randint + +import pytest + +from tlc.te import TransferEntry +from tlc.tl import TransferList + +large_data = 0xDEADBEEF.to_bytes(4, "big") +small_data = 0x1234.to_bytes(3, "big") +test_entries = [ + (0, b""), + (1, small_data), + (1, large_data), + (0xFFFFFF, small_data), + (0xFFFFFF, large_data), +] + + +@pytest.mark.parametrize( + "size,csum", + [ + (-1, None), + (0x18, 0x9E), + (0x1000, 0xA6), + (0x2000, 0x96), + (0x4000, 0x76), + ], +) +def test_make_transfer_list(size, csum): + if size < 8: + with pytest.raises(AssertionError): + tl = TransferList(size) + else: + tl = TransferList(size) + + assert tl.signature == 0x4A0FB10B + assert not tl.entries + assert tl.sum_of_bytes() == 0 + assert tl.checksum == csum + + +def test_add_transfer_entry(random_entries): + tl = TransferList(0x1000) + + # Add a single entry and check it's in the list of entries + te = tl.add_transfer_entry(1, bytes(100)) + assert te in tl.entries + assert tl.size % 8 == 0 + + # Add a range of tag id's + for id, data in random_entries(50, 1): + te = tl.add_transfer_entry(id, data) + assert te in tl.entries + assert tl.size % 8 == 0 + + +@pytest.mark.parametrize("align", [4, 6, 12, 13]) +def test_add_transfer_entry_with_align(align, random_entries, random_entry): + tl = TransferList(0xF00000) + id, data = random_entry(4) + + tl.add_transfer_entry(id, data) + + # Add an entry with a larger alignment requirement + _, data = random_entry(4) + te = tl.add_transfer_entry(1, data, data_align=align) + assert (te.offset + te.hdr_size) % (1 << align) == 0 + assert tl.alignment == align + + # Add some more entries and ensure the alignment is preserved + for id, data in random_entries(5, 0x200): + te = tl.add_transfer_entry(id, data, data_align=align) + assert (te.offset + te.hdr_size) % (1 << align) == 0 + assert tl.alignment == align + + +@pytest.mark.parametrize( + ("tag_id", "data"), + [ + (-1, None), # tag out of range + (1, None), # no data provided + (1, bytes(8000)), # very large data > total size + (0x100000, b"0dd0edfe"), # tag out of range + ], +) +def test_add_out_of_range_transfer_entry(tag_id, data): + tl = TransferList() + + with pytest.raises(Exception): + tl.add_transfer_entry(tag_id, data) + + +@pytest.mark.parametrize(("tag_id", "data"), test_entries) +def test_calculate_te_sum_of_bytes(tag_id, data): + te = TransferEntry(tag_id, len(data), data) + csum = ( + sum(data) + + sum(len(data).to_bytes(4, "big")) + + te.hdr_size + + sum(tag_id.to_bytes(4, "big")) + ) % 256 + assert te.sum_of_bytes == csum + + +def test_calc_tl_checksum(tmpdir, random_entries): + tl_file = tmpdir.join("tl.bin") + + tl = TransferList(0x1000) + + for id, data in random_entries(10): + tl.add_transfer_entry(id, data) + + assert sum(tl.to_bytes()) % 256 == 0 + + # Write the transfer list to a file and check that the sum of bytes is 0 + tl.write_to_file(tl_file) + assert sum(tl_file.read_binary()) % 256 == 0 + + +def test_empty_transfer_list_blob(tmpdir): + """Check that we can correctly create a transfer list header.""" + test_file = tmpdir.join("test_tl_blob.bin") + tl = TransferList() + tl.write_to_file(test_file) + + with open(test_file, "rb") as f: + assert f.read(tl.hdr_size) == tl.header_to_bytes() + + +@pytest.mark.parametrize(("tag_id", "data"), test_entries) +def test_single_te_transfer_list(tag_id, data, tmpdir): + """Check that we can create a complete TL with a single TE.""" + test_file = tmpdir.join("test_tl_blob.bin") + tl = TransferList(0x1000) + + tl.add_transfer_entry(tag_id, data) + tl.write_to_file(test_file) + + te = tl.entries[0] + + with open(test_file, "rb") as f: + assert f.read(tl.hdr_size) == tl.header_to_bytes() + assert int.from_bytes(f.read(3), "little") == te.id + assert int.from_bytes(f.read(1), "little") == te.hdr_size + assert int.from_bytes(f.read(4), "little") == te.data_size + assert f.read(te.data_size) == te.data + + +def test_write_multiple_tes_to_file(tmpdir, random_entries, random_entry): + """Check that we can create a TL with multiple TE's.""" + test_file = tmpdir.join("test_tl_blob.bin") + tl = TransferList(0x4000) + _test_entries = list(random_entries()) + + for tag_id, data in _test_entries: + tl.add_transfer_entry(tag_id, data) + + # Add a few entries with special alignment requirements + blob_id, blob = random_entry(0x200) + tl.add_transfer_entry(blob_id, blob, data_align=12) + + tl.write_to_file(test_file) + + with open(test_file, "rb") as f: + assert f.read(tl.hdr_size) == tl.header_to_bytes() + # Ensure that TE's have the correct alignment + for tag_id, data in _test_entries: + f.seek(int(math.ceil(f.tell() / 8) * 8)) + + assert int.from_bytes(f.read(3), "little") == tag_id + assert int.from_bytes(f.read(1), "little") == TransferEntry.hdr_size + # Make sure the data in the TE matches the data in the original case + data_size = int.from_bytes(f.read(4), "little") + assert f.read(data_size) == data + + f.seek(int(math.ceil(f.tell() / (1 << 12)) * (1 << 12)) - 8) + assert int.from_bytes(f.read(3), "little") == blob_id + assert int.from_bytes(f.read(1), "little") == TransferEntry.hdr_size + # Make sure the data in the TE matches the data in the original case + data_size = int.from_bytes(f.read(4), "little") + assert f.read(data_size) == blob + + # padding is added to align TE's, make sure padding is added to the size of + # the TL by checking we don't overflow. + assert f.tell() <= tl.size + + +def test_read_empty_transfer_list_from_file(tmpdir): + test_file = tmpdir.join("test_tl_blob.bin") + original_tl = TransferList(0x1000) + original_tl.write_to_file(test_file) + + # Read the contents of the file we just wrote + tl = TransferList.fromfile(test_file) + assert tl.header_to_bytes() == original_tl.header_to_bytes() + assert tl.sum_of_bytes() == 0 + + +def test_read_single_transfer_list_from_file(tmpdir): + test_file = tmpdir.join("test_tl_blob.bin") + original_tl = TransferList(0x1000) + + original_tl.add_transfer_entry(test_entries[0][0], test_entries[0][1]) + original_tl.write_to_file(test_file) + + # Read the contents of the file we just wrote + tl = TransferList.fromfile(test_file) + assert tl.entries + + te = tl.entries[0] + assert te.id == test_entries[0][0] + assert te.data == test_entries[0][1] + assert tl.sum_of_bytes() == 0 + + +def test_read_multiple_transfer_list_from_file(tmpdir): + test_file = tmpdir.join("test_tl_blob.bin") + original_tl = TransferList(0x1000) + + for tag_id, data in test_entries: + original_tl.add_transfer_entry(tag_id, data) + + original_tl.write_to_file(test_file) + + # Read the contents of the file we just wrote + tl = TransferList.fromfile(test_file) + + # The TE we derive from the file might have a an associated offset, compare + # the TE's based on the header in bytes, which doesn't account for this. + for te0, te1 in zip(tl.entries, original_tl.entries): + assert te0.header_to_bytes() == te1.header_to_bytes() + + assert tl.sum_of_bytes() == 0 + + +def test_remove_tag(random_entry): + """Adds a transfer entry and remove it, size == transfer list header.""" + tl = TransferList(0x100) + id, data = random_entry(tl.total_size // 2) + + te = tl.add_transfer_entry(id, data) + assert te in tl.entries + + tl.remove_tag(id) + assert not tl.get_entry(id) and te not in tl.entries + assert tl.size == tl.hdr_size + + +def test_get_fdt_offset(tmpdir): + tl = TransferList(0x1000) + tl.add_transfer_entry(1, 0xEDFE0DD0.to_bytes(4, "big")) + f = tmpdir.join("blob.bin") + + tl.write_to_file(f) + + blob_tl = TransferList.fromfile(f) + + assert blob_tl.hdr_size + TransferEntry.hdr_size == blob_tl.get_entry_data_offset(1) + + +def test_get_missing_fdt_offset(tmpdir): + tl = TransferList(0x1000) + f = tmpdir.join("blob.bin") + + tl.write_to_file(f) + blob_tl = TransferList.fromfile(f) + + with pytest.raises(ValueError): + blob_tl.get_entry_data_offset(1) diff --git a/atf-20250711/tools/tlc/tlc/__init__.py b/atf-20250711/tools/tlc/tlc/__init__.py new file mode 100644 index 000000000..82f5f5bf4 --- /dev/null +++ b/atf-20250711/tools/tlc/tlc/__init__.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Transfer List Compiler (TLC) is a Python-based CLI for efficiently handling transfer lists.""" + +import sys + +if sys.version_info >= (3, 8): + from importlib import metadata as importlib_metadata +else: + import importlib_metadata + + +def get_version() -> str: + try: + return importlib_metadata.version(__name__) + except importlib_metadata.PackageNotFoundError: # pragma: no cover + return "unknown" + + +version: str = get_version() diff --git a/atf-20250711/tools/tlc/tlc/__main__.py b/atf-20250711/tools/tlc/tlc/__main__.py new file mode 100644 index 000000000..03ffa0e72 --- /dev/null +++ b/atf-20250711/tools/tlc/tlc/__main__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +from tlc.cli import cli + +if __name__ == "__main__": + cli() diff --git a/atf-20250711/tools/tlc/tlc/cli.py b/atf-20250711/tools/tlc/tlc/cli.py new file mode 100644 index 000000000..431af0465 --- /dev/null +++ b/atf-20250711/tools/tlc/tlc/cli.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +# type: ignore[attr-defined] + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Module defining the Transfer List Compiler (TLC) command line interface.""" + +from pathlib import Path + +import click +import jinja2 +import yaml + +from tlc.tl import * + + +@click.group() +@click.version_option() +def cli(): + pass + + +@cli.command() +@click.argument("filename", type=click.Path(dir_okay=False)) +@click.option( + "-a", + "--align", + type=int, + default=3, + show_default=True, + help="Set alignment in powers of 2 (e.g., -a 3 for 8 byte alignment).", +) +@click.option( + "-s", "--size", default=0x1000, type=int, help="Maximum size of the Transfer List" +) +@click.option( + "--fdt", + type=click.Path(exists=True), + help="Path to flattened device tree (FDT).", +) +@click.option( + "--entry", + type=(int, click.Path(exists=True)), + multiple=True, + help="A tag ID and the corresponding path to a binary blob in the form .", +) +@click.option( + "--flags", + default=TRANSFER_LIST_ENABLE_CHECKSUM, + show_default=True, + help="Settings for the TL's properties.", +) +@click.option( + "--from-yaml", + type=click.Path(exists=True), + help="Create the transfer list from a YAML config file.", +) +def create(filename, align, size, fdt, entry, flags, from_yaml): + """Create a new Transfer List.""" + try: + if from_yaml: + with open(from_yaml, "r") as f: + config = yaml.safe_load(f) + + tl = TransferList.from_dict(config) + else: + tl = TransferList(size, flags=flags, alignment=align) + + entry = (*entry, (1, fdt)) if fdt else entry + + for id, path in entry: + tl.add_transfer_entry_from_file(id, path, data_align=align) + except MemoryError as mem_excp: + raise MemoryError( + "TL max size exceeded, consider increasing with the option -s" + ) from mem_excp + + tl.write_to_file(filename) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--fdt-offset", + is_flag=True, + help="Returns the offset of FDT in the TL if it is present.", +) +@click.option( + "--header", + is_flag=True, + help="Print the Transfer List header.", +) +@click.option( + "--entries", + is_flag=True, + help="Print the Transfer List entries.", +) +def info(filename, fdt_offset, header, entries): + """Print the contents of an existing Transfer List. + + This command allows you to extract the data stored in a binary blob + representing a transfer list (TL). The transfer list must comply with the + version of the firmware handoff specification supported by this tool. + """ + tl = TransferList.fromfile(filename) + + if fdt_offset: + return print(tl.get_entry_data_offset(1)) + + if header and entries or not (header or entries): + print(tl, sep="") + if tl.entries: + print("----", tl.get_transfer_entries_str(), sep="\n") + elif entries: + print(tl.get_transfer_entries_str()) + elif header: + print(tl) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--tags", + type=int, + multiple=True, + help="Tags to be removed from TL.", +) +def remove(filename, tags): + """Remove Transfer Entries with given tags. + + Remove Transfer Entries with given tags from a Transfer List.""" + tl = TransferList.fromfile(filename) + + for tag in tags: + tl.remove_tag(tag) + tl.write_to_file(filename) + + +@cli.command() +@click.option( + "-a", + "--align", + type=int, + help="Set alignment in powers of 2 (e.g., -a 3 for 8 byte alignment).", +) +@click.option( + "--entry", + type=(int, click.Path(exists=True)), + multiple=True, + help="A tag ID and the corresponding path to a binary blob in the form .", +) +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +def add(align, entry, filename): + """Update an existing Transfer List with given images.""" + tl = TransferList.fromfile(filename) + for id, path in entry: + tl.add_transfer_entry_from_file(id, path, data_align=align) + + tl.write_to_file(filename) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "-C", type=click.Path(exists=True), help="Output directory for extracted images." +) +def unpack(filename, c): + """Unpack images from a Transfer List.""" + tl = TransferList.fromfile(filename) + pwd = Path(".") if not c else Path(c) + + for i, te in enumerate(tl.entries): + with open(pwd / f"te_{i}_{te.id}.bin", "wb") as f: + f.write(te.data) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +@click.option( + "--output", + "-O", + type=click.Path(exists=False), + help="Output filename for the header", + default=Path("header.h"), +) +def gen_header(filename, output): + """Generate a header with common definitions.""" + tl = TransferList.fromfile(filename) + tmp_keys = tl.__dict__ + tmp_keys["header_guard"] = Path(output).name.replace(".", "_").upper() + + dtb_te = tl.get_entry(1) + + if dtb_te: + tmp_keys["dtb_offset"] = dtb_te.offset + dtb_te.hdr_size + + env = jinja2.Environment( + loader=jinja2.PackageLoader("tlc", "templates"), + ) + template = env.get_template("header.h.j2") + with open(output, "w") as f: + f.write(template.render(tmp_keys)) + + +@cli.command() +@click.argument("filename", type=click.Path(exists=True, dir_okay=False)) +def validate(filename): + """Validate the contents of an existing Transfer List.""" + TransferList.fromfile(filename) + print("Valid TL!") diff --git a/atf-20250711/tools/tlc/tlc/te.py b/atf-20250711/tools/tlc/tlc/te.py new file mode 100644 index 000000000..0b6b53269 --- /dev/null +++ b/atf-20250711/tools/tlc/tlc/te.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Module containing definitions pertaining to the 'Transfer Entry' (TE) type.""" + +from typing import ClassVar + +import struct +from dataclasses import dataclass + + +@dataclass +class TransferEntry: + """Class representing a Transfer Entry.""" + + id: int + data_size: int + data: bytes + hdr_size: int = 8 + offset: int = 0 + # Header encoding, with little-endian byte order. + encoding: ClassVar[str] = " 0xFFFFFF: + raise ValueError( + f"Out of bounds tag ID: {self.id:x}.\n" + f"Valid range is from 0 to 0xFFFFFF. Please ensure the tag ID is within this range." + ) + + def __str__(self) -> str: + return "\n".join( + [ + f"{k:<10} {hex(v)}" + for k, v in vars(self).items() + if not isinstance(v, bytes) + ] + ) + + @property + def size(self) -> int: + return self.hdr_size + len(self.data) + + @property + def sum_of_bytes(self) -> int: + return sum(self.to_bytes()) % 256 + + def to_bytes(self) -> bytes: + return self.header_to_bytes() + self.data + + def header_to_bytes(self) -> bytes: + return self.id.to_bytes(3, "little") + struct.pack( + self.encoding, self.hdr_size, self.data_size + ) diff --git a/atf-20250711/tools/tlc/tlc/templates/header.h.j2 b/atf-20250711/tools/tlc/tlc/templates/header.h.j2 new file mode 100644 index 000000000..87707cec2 --- /dev/null +++ b/atf-20250711/tools/tlc/tlc/templates/header.h.j2 @@ -0,0 +1,16 @@ +/* + * Auto-generated by TLC, this file includes declarations and macros + * derived from a Transfer List input. + */ + +#ifndef {{ header_guard }} +#define {{ header_guard }} + +{% if dtb_offset -%} +#define TRANSFER_LIST_DTB_OFFSET {{ "0x%x" % dtb_offset }} +{%- endif %} +#define TRANSFER_LIST_CONVENTION_VERSION {{ version }} +#define TRANSFER_LIST_HEADER_SIZE {{ "0x%x" % hdr_size }} +#define TRANSFER_LIST_SIZE {{ "0x%x" % size }} + +#endif /* {{ header_guard }} */ diff --git a/atf-20250711/tools/tlc/tlc/tl.py b/atf-20250711/tools/tlc/tlc/tl.py new file mode 100644 index 000000000..dfbea9fc5 --- /dev/null +++ b/atf-20250711/tools/tlc/tlc/tl.py @@ -0,0 +1,388 @@ +#!/usr/bin/env python3 + +# +# Copyright (c) 2024, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +"""Module containing definitions pertaining to the 'Transfer List' (TL) type.""" + +from typing import Any, Dict, List, Optional + +import math +import struct +from dataclasses import dataclass +from functools import reduce +from pathlib import Path + +from tlc.te import TransferEntry + +TRANSFER_LIST_ENABLE_CHECKSUM = 0b1 + +# Description of each TE type. For each TE, there is a tag ID, a format (to be +# used in struct.pack to encode the TE), and a list of field names that can +# appear in the yaml file for that TE. Some fields are missing, if that TE has +# to be processed differently, or if it can only be added with a blob file. +transfer_entry_formats: Dict[int, Any] = { + 0: { + "tag_name": "empty", + "format": "4x", + "fields": [], + }, + 1: { + "tag_name": "fdt", + }, + 2: { + "tag_name": "hob_block", + }, + 3: { + "tag_name": "hob_list", + }, + 4: { + "tag_name": "acpi_table_aggregate", + }, + 5: { + "tag_name": "tpm_event_log_table", + "fields": ["event_log", "flags"], + }, + 6: { + "tag_name": "tpm_crb_base_address_table", + "format": "QI", + "fields": ["crb_base_address", "crb_size"], + }, + 0x100: { + "tag_name": "optee_pageable_part", + "format": "Q", + "fields": ["pp_addr"], + }, + 0x101: { + "tag_name": "dt_spmc_manifest", + }, + 0x102: { + "tag_name": "exec_ep_info", + "format": "2BHIQI4x8Q", + "fields": ["ep_info"], + }, + 0x104: { + "tag_name": "sram_layout", + "format": "2Q", + "fields": ["addr", "size"], + }, +} +tag_name_to_tag_id = { + te["tag_name"]: tag_id for tag_id, te in transfer_entry_formats.items() +} + + +class TransferList: + """Class representing a Transfer List based on version 1.0 of the Firmware Handoff specification.""" + + # Header encoding, with little-endian byte order. + encoding = " None: + assert max_size >= self.hdr_size + self.checksum: int = 0 + self.alignment: int = alignment + self.size = self.hdr_size + self.total_size = max_size + self.flags = flags + self.entries: List[TransferEntry] = [] + self.update_checksum() + + def __str__(self) -> str: + return "\n".join( + [ + f"{k:<10} {hex(v)}" + for k, v in vars(self).items() + if not isinstance(v, list) + ] + ) + + def get_transfer_entries_str(self): + return "\n----\n".join([str(te) for _, te in enumerate(self.entries)]) + + @classmethod + def fromfile(cls, filepath: Path) -> "TransferList": + tl = cls() + + with open(filepath, "rb") as f: + ( + tl.signature, + tl.checksum, + tl.version, + tl.hdr_size, + tl.alignment, + used_size, + tl.total_size, + tl.flags, + _, + ) = struct.unpack( + cls.encoding, + f.read(tl.hdr_size), + ) + + if tl.signature != TransferList.signature: + raise ValueError(f"Invalid TL signature 0x{tl.signature:x}!") + elif tl.version == 0 or tl.version > 0xFF: + raise ValueError(f"Invalid TL version 0x{tl.version:x}!") + else: + while tl.size < used_size: + # We add an extra padding byte into the header so we can extract + # the 3-byte wide ID as a 4-byte uint, shift out this padding + # once we have the id. + te_base = f.tell() + (id, _, data_size) = struct.unpack( + TransferEntry.encoding[0] + "I" + TransferEntry.encoding[1:], + b"\x00" + f.read(TransferEntry.hdr_size), + ) + + id >>= 8 + te = tl.add_transfer_entry(id, f.read(data_size)) + te.offset = te_base + f.seek(align(f.tell(), tl.granule)) + + return tl + + @classmethod + def from_dict(cls, config: Dict[str, Any]) -> "TransferList": + """Create a TL from data in a dictionary + + The dictionary should have the same format as the yaml config files. + See the readme for more detail. + + :param config: Dictionary containing the data described above. + """ + # get settings from config and set defaults + max_size = config.get("max_size", 0x1000) + has_checksum = config.get("has_checksum", True) + align = config.get("alignment", None) + + flags = TRANSFER_LIST_ENABLE_CHECKSUM if has_checksum else 0 + + if align: + tl = cls(max_size, flags, alignment=align) + else: + tl = cls(max_size, flags) + + for entry in config["entries"]: + tl.add_transfer_entry_from_dict(entry) + + return tl + + def header_to_bytes(self) -> bytes: + return struct.pack( + self.encoding, + self.signature, + self.checksum, + self.version, + self.hdr_size, + self.alignment, + self.size, + self.total_size, + self.flags, + 0, + ) + + def update_checksum(self) -> None: + """Calculates the checksum based on the sum of bytes.""" + self.checksum = (256 - (self.sum_of_bytes() - self.checksum)) % 256 + assert self.checksum <= 0xFF + + def to_bytes(self) -> bytes: + return self.header_to_bytes() + b"".join([te.to_bytes() for te in self.entries]) + + def sum_of_bytes(self) -> int: + """Sum of all bytes between the base address and the end of that last TE (modulo 0xff).""" + return (sum(self.to_bytes())) % 256 + + def get_entry(self, tag_id: int) -> Optional[TransferEntry]: + for te in self.entries: + if te.id == tag_id: + return te + + return None + + def get_entry_data_offset(self, tag_id: int) -> int: + """Returns offset of data of a TE from the base of the TL.""" + te = self.get_entry(tag_id) + + if not te: + raise ValueError(f"Tag {tag_id} not found in TL!") + + return te.offset + te.hdr_size + + def add_transfer_entry( + self, tag_id: int, data: bytes, data_align: int = 0 + ) -> TransferEntry: + """Appends a TransferEntry into the internal list of TE's.""" + data_offset = TransferEntry.hdr_size + self.size + data_align = self.alignment if not data_align else data_align + + aligned_data_offset = align(data_offset, 1 << data_align) + + if tag_id != 0 and data_offset != aligned_data_offset: + void_len = aligned_data_offset - data_offset - TransferEntry.hdr_size + self.add_transfer_entry(0, bytes(void_len)) + + assert align(self.size, self.granule) + + if not (self.total_size >= self.size + TransferEntry.hdr_size + len(data)): + raise MemoryError( + f"TL size has exceeded the maximum allocation {self.total_size}." + ) + + te = TransferEntry(tag_id, len(data), data, offset=self.size) + self.entries.append(te) + + self.size += align(te.size, self.granule) + if data_align > self.alignment: + self.alignment = data_align + + self.update_checksum() + return te + + def add_transfer_entry_from_struct_format( + self, tag_id: int, struct_format: str, *args: Any + ) -> TransferEntry: + struct_format = "<" + struct_format + data = struct.pack(struct_format, *args) + return self.add_transfer_entry(tag_id, data) + + def add_entry_point_info_transfer_entry( + self, entry: Dict[str, Any] + ) -> TransferEntry: + """Add entry_point_info transfer entry + + :param entry: Dictionary of the transfer entry, in the same format as + the YAML file. + """ + ep_info = entry["ep_info"] + header = ep_info["h"] + + # size of the entry_point_info struct + entry_point_size = 88 + + attr = header["attr"] + if type(attr) is str: + # convert string of flags names to an integer + + # bit number | 0 | 1 | + # ------------|-----------------------|----------------------| + # 0 | secure | non-secure | + # 1 | little endian | big-endian | + # 2 | disable secure timer | enable secure timer | + # 3 | executable | non-executable | + # 4 | first exe | not first exe | + # + # Bit 5 and bit 0 are used to determine the security state. + + flag_names = { + "EP_SECURE": 0x0, + "EP_NON_SECURE": 0x1, + "EP_REALM": 0x21, + "EP_EE_LITTLE": 0x0, + "EP_EE_BIG": 0x2, + "EP_ST_DISABLE": 0x0, + "EP_ST_ENABLE": 0x4, + "EP_NON_EXECUTABLE": 0x0, + "EP_EXECUTABLE": 0x8, + "EP_FIRST_EXE": 0x10, + } + + # create list of integer flags, then bitwise-or them together + flags = [flag_names[f.strip()] for f in attr.split("|")] + attr = reduce(lambda x, y: x | y, flags) + + return self.add_transfer_entry_from_struct_format( + 0x102, + transfer_entry_formats[0x102]["format"], + header["type"], + header["version"], + entry_point_size, + attr, + ep_info["pc"], + ep_info["spsr"], + *ep_info["args"], + ) + + def add_transfer_entry_from_dict( + self, + entry: Dict[str, Any], + ) -> TransferEntry: + """Add a transfer entry from data in a dictionary + + The dictionary should have the same format as the entries in the yaml + config files. See the readme for more detail. + + :param entry: Dictionary containing the data described above. + """ + # Tag_id is either a tag name or a tag id. Use it to get the TE format. + tag_id = entry["tag_id"] + if tag_id in tag_name_to_tag_id: + tag_id = tag_name_to_tag_id[tag_id] + + align = entry.get("alignment", None) + + if "blob_file_path" in entry: + return self.add_transfer_entry_from_file( + tag_id, entry["blob_file_path"], data_align=align + ) + else: + te_format = transfer_entry_formats[tag_id] + tag_name = te_format["tag_name"] + + if tag_name == "tpm_event_log_table": + with open(entry["event_log"], "rb") as f: + event_log_data = f.read() + + flags_bytes = entry["flags"].to_bytes(4, "little") + data = flags_bytes + event_log_data + + return self.add_transfer_entry(tag_id, data, data_align=align) + elif tag_name == "exec_ep_info": + return self.add_entry_point_info_transfer_entry(entry) + elif "format" in te_format and "fields" in te_format: + fields = [entry[field] for field in te_format["fields"]] + return self.add_transfer_entry_from_struct_format( + tag_id, te_format["format"], *fields + ) + else: + raise ValueError(f"Invalid transfer entry {entry}.") + + def add_transfer_entry_from_file( + self, tag_id: int, path: Path, data_align: int = 0 + ) -> TransferEntry: + with open(path, "rb") as f: + return self.add_transfer_entry(tag_id, f.read(), data_align=data_align) + + def write_to_file(self, file: Path) -> None: + """Write the contents of the TL to a file.""" + with open(file, "wb") as f: + f.write(self.header_to_bytes()) + for te in self.entries: + assert f.tell() + te.hdr_size + te.data_size < self.total_size + + f.write(te.header_to_bytes()) + f.write(te.data) + # Ensure the next TE is at an 8-byte aligned address + f.write(bytes((align(f.tell(), self.granule) - f.tell()))) + + def remove_tag(self, tag: int) -> None: + self.entries = list(filter(lambda te: te.id != tag, self.entries)) + self.size = self.hdr_size + sum(map(lambda te: te.size, self.entries)) + self.update_checksum() + + +def align(n, alignment): + return int(math.ceil(n / alignment) * alignment) diff --git a/atf-20250711/tools/tlc/tox.ini b/atf-20250711/tools/tlc/tox.ini new file mode 100644 index 000000000..4fd141f1c --- /dev/null +++ b/atf-20250711/tools/tlc/tox.ini @@ -0,0 +1,26 @@ +[tox] +envlist = py38, py39, py310, py311, py312, lint + +[testenv] +allowlist_externals = poetry +commands = + poetry install -v --with dev + poetry run pytest + +[testenv:format] +description = Run linters and type checks +skip_install = true +allowlist_externals = poetry +commands = + poetry run black . + poetry run isort . + +[testenv:lint] +description = Run linters and type checks +skip_install = true +allowlist_externals = poetry +commands = + poetry run black --check . + poetry run isort --check-only . + poetry run mypy . + poetry run darglint tlc tests