mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-07 21:33:54 +00:00
Merge changes from topic "hm/handoff" into integration
* changes: fix(arm): move HW_CONFIG relocation into BL31 feat: add option to input attr as string of flag names feat: add option to input text instead of tag id number feat: add creating transfer lists from yaml files
This commit is contained in:
commit
9bfad24c3b
13 changed files with 622 additions and 69 deletions
|
@ -59,6 +59,12 @@ through the ``--entry`` option.
|
|||
provided tag ID. It only checks that the tags provided as input are within
|
||||
range and that there is sufficient memory to include their TE's.
|
||||
|
||||
You can also create a TL from a YAML config file.
|
||||
|
||||
.. code ::
|
||||
|
||||
tlc create --from-yaml config.yaml tl.bin
|
||||
|
||||
Printing the contents of a TL
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -186,9 +192,120 @@ performs the following checks:
|
|||
#. Ensures that the specified version is greater than or equal to the tool’s current version.
|
||||
#. Verifies alignment criteria for all TE’s.
|
||||
|
||||
YAML Config File Format
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Example YAML config file:
|
||||
|
||||
.. code::
|
||||
|
||||
execution_state: aarch32
|
||||
has_checksum: true
|
||||
max_size: 4096
|
||||
entries:
|
||||
- tag_id: 258 # entry point info
|
||||
ep_info:
|
||||
args:
|
||||
- 67112968
|
||||
- 67112960
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
h:
|
||||
attr: 8
|
||||
type: 1
|
||||
version: 2
|
||||
pc: 67239936
|
||||
spsr: 467
|
||||
- tag_id: 3 # memory layout
|
||||
addr: 8
|
||||
size: 8
|
||||
- tag_id: 1, # fdt
|
||||
blob_file_path: "fdt.bin",
|
||||
|
||||
`max_size` defaults to `0x1000`, `execution_state` defaults to `aarch64`, and `has_checksum`
|
||||
defaults to `true`.
|
||||
|
||||
The fields of the YAML file should match the fields in the specification for the transfer list. You
|
||||
don't need to give the hdr_size or data_size fields. For example, a memory layout entry would have
|
||||
an entry like:
|
||||
|
||||
.. code::
|
||||
|
||||
tag_id: 3
|
||||
addr: 8
|
||||
size: 8
|
||||
|
||||
You can input blob files by giving paths to the current working directory. You can do this for any
|
||||
TE type. For example, an FDT layout would have an entry like:
|
||||
|
||||
.. code::
|
||||
|
||||
tag_id: 1,
|
||||
blob_file_path: "fdt.bin",
|
||||
|
||||
You can input C-types by giving its fields. For example, an entry point
|
||||
info entry would have an entry like:
|
||||
|
||||
.. code::
|
||||
|
||||
tag_id: 258
|
||||
ep_info:
|
||||
args:
|
||||
- 67112968
|
||||
- 67112960
|
||||
- 0
|
||||
- 0
|
||||
h:
|
||||
attr: 8
|
||||
type: 1
|
||||
version: 2
|
||||
lr_svc: 0
|
||||
pc: 67239936
|
||||
spsr: 467
|
||||
|
||||
You can give the name of the tag instead of the tag id number. The valid tag names are in the
|
||||
`transfer_entry_formats` dict in `tools/tlc/tlc/tl.py`_. Some examples are:
|
||||
|
||||
* empty
|
||||
* fdt
|
||||
* hob_block
|
||||
* hob_list
|
||||
|
||||
You can input the attr field of entry_point_info as a string of flag
|
||||
names separated by `|`. The names are taken from ep_info_exp.h in TF-A.
|
||||
For example:
|
||||
|
||||
.. code::
|
||||
|
||||
has_checksum: true
|
||||
max_size: 4096
|
||||
entries:
|
||||
- tag_id: 0x102
|
||||
ep_info:
|
||||
args:
|
||||
- 67112976
|
||||
- 67112960
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
h:
|
||||
attr: EP_NON_SECURE | EP_ST_ENABLE
|
||||
type: 1
|
||||
version: 2
|
||||
pc: 67239936
|
||||
spsr: 965
|
||||
|
||||
--------------
|
||||
|
||||
*Copyright (c) 2024, Arm Limited. All rights reserved.*
|
||||
|
||||
.. _Firmware Handoff specification: https://github.com/FirmwareHandoff/firmware_handoff/
|
||||
.. _tools/tlc/pyproject.toml: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/master/tools/tlc/pyproject.toml
|
||||
.. _tools/tlc/tlc/tl.py: https://review.trustedfirmware.org/plugins/gitiles/TF-A/trusted-firmware-a/+/refs/heads/master/tools/tlc/tlc/tl.py
|
||||
|
|
|
@ -284,10 +284,7 @@ void arm_bl31_plat_arch_setup(void);
|
|||
/* Firmware Handoff utility functions */
|
||||
void arm_transfer_list_dyn_cfg_init(struct transfer_list_header *secure_tl);
|
||||
void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node,
|
||||
struct transfer_list_header *secure_tl,
|
||||
struct transfer_list_header *ns_tl);
|
||||
void arm_transfer_list_copy_hw_config(struct transfer_list_header *secure_tl,
|
||||
struct transfer_list_header *ns_tl);
|
||||
struct transfer_list_header *secure_tl);
|
||||
|
||||
/* TSP utility functions */
|
||||
void arm_tsp_early_platform_setup(void);
|
||||
|
|
|
@ -162,16 +162,6 @@ void arm_bl2_platform_setup(void)
|
|||
#if defined(PLAT_ARM_MEM_PROT_ADDR)
|
||||
arm_nor_psci_do_static_mem_protect();
|
||||
#endif
|
||||
|
||||
#if TRANSFER_LIST
|
||||
ns_tl = transfer_list_init((void *)FW_NS_HANDOFF_BASE,
|
||||
PLAT_ARM_FW_HANDOFF_SIZE);
|
||||
|
||||
if (ns_tl == NULL) {
|
||||
ERROR("Non-secure transfer list initialisation failed!");
|
||||
panic();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void bl2_platform_setup(void)
|
||||
|
@ -326,7 +316,8 @@ int arm_bl2_plat_handle_post_image_load(unsigned int image_id)
|
|||
|
||||
#if TRANSFER_LIST
|
||||
if (image_id == HW_CONFIG_ID) {
|
||||
arm_transfer_list_copy_hw_config(secure_tl, ns_tl);
|
||||
/* Refresh the now stale checksum following loading of HW_CONFIG into the TL. */
|
||||
transfer_list_update_checksum(secure_tl);
|
||||
}
|
||||
#endif /* TRANSFER_LIST */
|
||||
|
||||
|
@ -340,5 +331,5 @@ void arm_bl2_setup_next_ep_info(bl_mem_params_node_t *next_param_node)
|
|||
&next_param_node->ep_info);
|
||||
assert(ep != NULL);
|
||||
|
||||
arm_transfer_list_populate_ep_info(next_param_node, secure_tl, ns_tl);
|
||||
arm_transfer_list_populate_ep_info(next_param_node, secure_tl);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <platform_def.h>
|
||||
|
||||
static struct transfer_list_header *secure_tl __unused;
|
||||
static struct transfer_list_header *ns_tl __unused;
|
||||
|
||||
/*
|
||||
* Placeholder variables for copying the arguments that have been passed to
|
||||
* BL31 from BL2.
|
||||
|
@ -95,7 +97,12 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type)
|
|||
|
||||
assert(sec_state_is_valid(type));
|
||||
if (type == NON_SECURE) {
|
||||
#if TRANSFER_LIST && !RESET_TO_BL31
|
||||
next_image_info = transfer_list_set_handoff_args(
|
||||
ns_tl, &bl33_image_ep_info);
|
||||
#else
|
||||
next_image_info = &bl33_image_ep_info;
|
||||
#endif
|
||||
}
|
||||
#if ENABLE_RME
|
||||
else if (type == REALM) {
|
||||
|
@ -357,6 +364,28 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
|
|||
******************************************************************************/
|
||||
void arm_bl31_platform_setup(void)
|
||||
{
|
||||
struct transfer_list_entry *te __unused;
|
||||
|
||||
#if TRANSFER_LIST && !RESET_TO_BL31
|
||||
/* Initialise the non-secure world tl, BL31 may modify the HW_CONFIG so defer
|
||||
* copying it until later.
|
||||
*/
|
||||
ns_tl = transfer_list_init((void *)FW_NS_HANDOFF_BASE,
|
||||
PLAT_ARM_FW_HANDOFF_SIZE);
|
||||
|
||||
if (ns_tl == NULL) {
|
||||
ERROR("Non-secure transfer list initialisation failed!");
|
||||
panic();
|
||||
}
|
||||
|
||||
#if !RESET_TO_BL2
|
||||
te = transfer_list_find(secure_tl, TL_TAG_FDT);
|
||||
assert(te != NULL);
|
||||
|
||||
fconf_populate("HW_CONFIG", (uintptr_t)transfer_list_entry_data(te));
|
||||
#endif /* !(RESET_TO_BL2 && RESET_TO_BL31) */
|
||||
#endif /* TRANSFER_LIST */
|
||||
|
||||
/* Initialize the GIC driver, cpu and distributor interfaces */
|
||||
plat_arm_gic_driver_init();
|
||||
plat_arm_gic_init();
|
||||
|
@ -399,9 +428,26 @@ void arm_bl31_platform_setup(void)
|
|||
******************************************************************************/
|
||||
void arm_bl31_plat_runtime_setup(void)
|
||||
{
|
||||
struct transfer_list_entry *te __unused;
|
||||
/* Initialize the runtime console */
|
||||
arm_console_runtime_init();
|
||||
|
||||
#if TRANSFER_LIST && !RESET_TO_BL31
|
||||
te = transfer_list_find(secure_tl, TL_TAG_FDT);
|
||||
assert(te != NULL);
|
||||
|
||||
te = transfer_list_add(ns_tl, TL_TAG_FDT, te->data_size,
|
||||
transfer_list_entry_data(te));
|
||||
assert(te != NULL);
|
||||
|
||||
/*
|
||||
* We assume BL31 has added all TE's required by BL33 at this stage, ensure
|
||||
* that data is visible to all observers by performing a flush operation, so
|
||||
* they can access the updated data even if caching is not enabled.
|
||||
*/
|
||||
flush_dcache_range((uintptr_t)ns_tl, ns_tl->size);
|
||||
#endif /* TRANSFER_LIST && !(RESET_TO_BL2 || RESET_TO_BL31) */
|
||||
|
||||
#if RECLAIM_INIT_CODE
|
||||
arm_free_init_memory();
|
||||
#endif
|
||||
|
@ -516,15 +562,5 @@ void __init arm_bl31_plat_arch_setup(void)
|
|||
|
||||
void __init bl31_plat_arch_setup(void)
|
||||
{
|
||||
struct transfer_list_entry *te __unused;
|
||||
|
||||
arm_bl31_plat_arch_setup();
|
||||
|
||||
#if TRANSFER_LIST && !(RESET_TO_BL2 || RESET_TO_BL31)
|
||||
te = transfer_list_find(secure_tl, TL_TAG_FDT);
|
||||
assert(te != NULL);
|
||||
|
||||
/* Populate HW_CONFIG device tree with the mapped address */
|
||||
fconf_populate("HW_CONFIG", (uintptr_t)transfer_list_entry_data(te));
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ void arm_transfer_list_dyn_cfg_init(struct transfer_list_header *secure_tl)
|
|||
}
|
||||
|
||||
void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node,
|
||||
struct transfer_list_header *secure_tl,
|
||||
struct transfer_list_header *ns_tl)
|
||||
struct transfer_list_header *secure_tl)
|
||||
{
|
||||
uint32_t next_exe_img_id;
|
||||
entry_point_info_t *ep;
|
||||
|
@ -53,10 +52,7 @@ void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node,
|
|||
|
||||
ep = transfer_list_entry_data(te);
|
||||
|
||||
if (next_exe_img_id == BL33_IMAGE_ID) {
|
||||
ep = transfer_list_set_handoff_args(ns_tl, ep);
|
||||
assert(ep != NULL);
|
||||
} else if ((next_exe_img_id == BL32_IMAGE_ID) && SPMC_AT_EL3) {
|
||||
if ((next_exe_img_id == BL32_IMAGE_ID) && SPMC_AT_EL3) {
|
||||
/*
|
||||
* Populate the BL32 image base, size and max limit in
|
||||
* the entry point information, since there is no
|
||||
|
@ -78,19 +74,3 @@ void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node,
|
|||
|
||||
flush_dcache_range((uintptr_t)secure_tl, secure_tl->size);
|
||||
}
|
||||
|
||||
void arm_transfer_list_copy_hw_config(struct transfer_list_header *secure_tl,
|
||||
struct transfer_list_header *ns_tl)
|
||||
{
|
||||
struct transfer_list_entry *te =
|
||||
transfer_list_find(secure_tl, TL_TAG_FDT);
|
||||
assert(te != NULL);
|
||||
|
||||
/* Refresh the now stale checksum following loading of HW_CONFIG into the TL. */
|
||||
transfer_list_update_checksum(secure_tl);
|
||||
|
||||
/* Copy the hardware configuration to the non-secure TL. */
|
||||
te = transfer_list_add(ns_tl, TL_TAG_FDT, te->data_size,
|
||||
transfer_list_entry_data(te));
|
||||
assert(te != NULL);
|
||||
}
|
||||
|
|
1
poetry.lock
generated
1
poetry.lock
generated
|
@ -893,6 +893,7 @@ develop = false
|
|||
|
||||
[package.dependencies]
|
||||
click = "^8.1.7"
|
||||
pyyaml = "^6.0.1"
|
||||
rich = "^10.14.0"
|
||||
typer = {version = "^0.4.0", extras = ["all"]}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
|
||||
<text x="31.5" y="14">coverage</text>
|
||||
<text x="80" y="15" fill="#010101" fill-opacity=".3">97%</text>
|
||||
<text x="80" y="14">97%</text>
|
||||
<text x="80" y="15" fill="#010101" fill-opacity=".3">95%</text>
|
||||
<text x="80" y="14">95%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 901 B After Width: | Height: | Size: 901 B |
25
tools/tlc/poetry.lock
generated
25
tools/tlc/poetry.lock
generated
|
@ -386,13 +386,13 @@ pipenv = ["pipenv (<=2022.12.19)"]
|
|||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
|
||||
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
|
||||
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
||||
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
@ -1107,18 +1107,19 @@ gitlab = ["python-gitlab (>=1.3.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "70.2.0"
|
||||
version = "72.1.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"},
|
||||
{file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"},
|
||||
{file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"},
|
||||
{file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||
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"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
|
@ -1191,13 +1192,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.12.5"
|
||||
version = "0.13.0"
|
||||
description = "Style preserving TOML library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"},
|
||||
{file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"},
|
||||
{file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"},
|
||||
{file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1352,4 +1353,4 @@ files = [
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "60bdb4a8b67815f01b4e7089d9f7664afcb9041fa8adf5aa92d977f4e2d5b4b2"
|
||||
content-hash = "cfcb196cda412f6139302937640455aa8154d7979c69017fe45ddd528e4a1bf2"
|
||||
|
|
|
@ -37,6 +37,7 @@ python = "^3.8"
|
|||
typer = {extras = ["all"], version = "^0.4.0"}
|
||||
rich = "^10.14.0"
|
||||
click = "^8.1.7"
|
||||
pyyaml = "^6.0.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
bandit = "^1.7.1"
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
""" Common configurations and fixtures for test environment."""
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from click.testing import CliRunner
|
||||
|
||||
from tlc.cli import cli
|
||||
|
@ -20,6 +21,11 @@ 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")
|
||||
|
@ -27,6 +33,32 @@ def tmpfdt(tmpdir):
|
|||
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()
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
from math import log2, ceil
|
||||
|
||||
import pytest
|
||||
import pytest
|
||||
import yaml
|
||||
from click.testing import CliRunner
|
||||
|
||||
from tlc.cli import cli
|
||||
|
@ -203,3 +206,208 @@ def test_validate_unsupported_version(version, tmptlstr, tlcrunner, monkeypatch)
|
|||
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
|
||||
|
||||
|
||||
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
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import yaml
|
||||
|
||||
from tlc.tl import *
|
||||
|
||||
|
@ -44,15 +45,26 @@ def cli():
|
|||
show_default=True,
|
||||
help="Settings for the TL's properties.",
|
||||
)
|
||||
def create(filename, size, fdt, entry, flags):
|
||||
@click.option(
|
||||
"--from-yaml",
|
||||
type=click.Path(exists=True),
|
||||
help="Create the transfer list from a YAML config file.",
|
||||
)
|
||||
def create(filename, size, fdt, entry, flags, from_yaml):
|
||||
"""Create a new Transfer List."""
|
||||
tl = TransferList(size)
|
||||
|
||||
entry = (*entry, (1, fdt)) if fdt else entry
|
||||
|
||||
try:
|
||||
for id, path in entry:
|
||||
tl.add_transfer_entry_from_file(id, path)
|
||||
if from_yaml:
|
||||
with open(from_yaml, "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
tl = TransferList.from_dict(config)
|
||||
else:
|
||||
tl = TransferList(size)
|
||||
|
||||
entry = (*entry, (1, fdt)) if fdt else entry
|
||||
|
||||
for id, path in entry:
|
||||
tl.add_transfer_entry_from_file(id, path)
|
||||
except MemoryError as mem_excp:
|
||||
raise MemoryError(
|
||||
"TL max size exceeded, consider increasing with the option -s"
|
||||
|
|
|
@ -13,12 +13,67 @@ import typing
|
|||
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 = {
|
||||
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."""
|
||||
|
@ -96,6 +151,28 @@ class TransferList:
|
|||
|
||||
return tl
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, config: dict):
|
||||
"""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)
|
||||
|
||||
flags = TRANSFER_LIST_ENABLE_CHECKSUM if has_checksum else 0
|
||||
|
||||
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,
|
||||
|
@ -141,6 +218,106 @@ class TransferList:
|
|||
self.update_checksum()
|
||||
return te
|
||||
|
||||
def add_transfer_entry_from_struct_format(
|
||||
self, tag_id: int, struct_format: str, *args
|
||||
):
|
||||
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) -> "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,
|
||||
) -> "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]
|
||||
te_format = transfer_entry_formats[tag_id]
|
||||
tag_name = te_format["tag_name"]
|
||||
|
||||
if "blob_file_path" in entry:
|
||||
return self.add_transfer_entry_from_file(tag_id, entry["blob_file_path"])
|
||||
elif 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)
|
||||
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) -> "TransferEntry":
|
||||
with open(path, "rb") as f:
|
||||
return self.add_transfer_entry(tag_id, f.read())
|
||||
|
|
Loading…
Add table
Reference in a new issue