summaryrefslogtreecommitdiffstats
path: root/plat/common/plat_spmd_manifest.c
blob: 5f7d1428dd2babe9dda6c6057e438f0e2499f6c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * Copyright (c) 2020-2023, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <libfdt.h>
#include <stdint.h>
#include <string.h>

#include <common/bl_common.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <platform_def.h>
#include <services/spm_core_manifest.h>

#define ATTRIBUTE_ROOT_NODE_STR "attribute"

/*******************************************************************************
 * SPMC attribute node parser
 ******************************************************************************/
static int manifest_parse_attribute(spmc_manifest_attribute_t *attr,
				    const void *fdt,
				    int node)
{
	uint32_t val32;
	int rc;

	assert((attr != NULL) && (fdt != NULL));

	rc = fdt_read_uint32(fdt, node, "maj_ver", &attr->major_version);
	if (rc != 0) {
		ERROR("Missing FFA %s version in SPM Core manifest.\n",
			"major");
		return rc;
	}

	rc = fdt_read_uint32(fdt, node, "min_ver", &attr->minor_version);
	if (rc != 0) {
		ERROR("Missing FFA %s version in SPM Core manifest.\n",
			"minor");
		return rc;
	}

	rc = fdt_read_uint32(fdt, node, "spmc_id", &val32);
	if (rc != 0) {
		ERROR("Missing SPMC ID in manifest.\n");
		return rc;
	}

	attr->spmc_id = val32 & 0xffff;

	rc = fdt_read_uint32(fdt, node, "exec_state", &attr->exec_state);
	if (rc != 0) {
		NOTICE("%s not specified in SPM Core manifest.\n",
			"Execution state");
	}

	rc = fdt_read_uint32(fdt, node, "binary_size", &attr->binary_size);
	if (rc != 0) {
		NOTICE("%s not specified in SPM Core manifest.\n",
			"Binary size");
	}

	rc = fdt_read_uint64(fdt, node, "load_address", &attr->load_address);
	if (rc != 0) {
		NOTICE("%s not specified in SPM Core manifest.\n",
			"Load address");
	}

	rc = fdt_read_uint64(fdt, node, "entrypoint", &attr->entrypoint);
	if (rc != 0) {
		NOTICE("%s not specified in SPM Core manifest.\n",
			"Entry point");
	}

	VERBOSE("SPM Core manifest attribute section:\n");
	VERBOSE("  version: %u.%u\n", attr->major_version, attr->minor_version);
	VERBOSE("  spmc_id: 0x%x\n", attr->spmc_id);
	VERBOSE("  binary_size: 0x%x\n", attr->binary_size);
	VERBOSE("  load_address: 0x%" PRIx64 "\n", attr->load_address);
	VERBOSE("  entrypoint: 0x%" PRIx64 "\n", attr->entrypoint);

	return 0;
}

/*******************************************************************************
 * Root node handler
 ******************************************************************************/
static int manifest_parse_root(spmc_manifest_attribute_t *manifest,
			       const void *fdt,
			       int root)
{
	int node;

	assert(manifest != NULL);

	node = fdt_subnode_offset_namelen(fdt, root, ATTRIBUTE_ROOT_NODE_STR,
		sizeof(ATTRIBUTE_ROOT_NODE_STR) - 1);
	if (node < 0) {
		ERROR("Root node doesn't contain subnode '%s'\n",
			ATTRIBUTE_ROOT_NODE_STR);
		return node;
	}

	return manifest_parse_attribute(manifest, fdt, node);
}

/*******************************************************************************
 * Platform handler to parse a SPM Core manifest.
 ******************************************************************************/
int plat_spm_core_manifest_load(spmc_manifest_attribute_t *manifest,
				const void *pm_addr)
{
	int rc, unmap_ret;
	uintptr_t pm_base, pm_base_align;
	size_t mapped_size;

	assert(manifest != NULL);
	assert(pm_addr != NULL);

	/*
	 * Assume TOS_FW_CONFIG is not necessarily aligned to a page
	 * boundary, thus calculate the remaining space between SPMC
	 * manifest start address and upper page limit.
	 *
	 */
	pm_base = (uintptr_t)pm_addr;
	pm_base_align = page_align(pm_base, UP);

	if (pm_base == pm_base_align) {
		/* Page aligned */
		mapped_size = PAGE_SIZE;
	} else {
		mapped_size = pm_base_align - pm_base;
	}

	/* Check space within the page at least maps the FDT header */
	if (mapped_size < sizeof(struct fdt_header)) {
		ERROR("Error while mapping SPM Core manifest.\n");
		return -EINVAL;
	}

	/* Map first SPMC manifest page in the SPMD translation regime */
	pm_base_align = page_align(pm_base, DOWN);
	rc = mmap_add_dynamic_region((unsigned long long)pm_base_align,
				     pm_base_align,
				     PAGE_SIZE,
				     MT_RO_DATA | EL3_PAS);
	if (rc != 0) {
		ERROR("Error while mapping SPM Core manifest (%d).\n", rc);
		return rc;
	}

	rc = fdt_check_header(pm_addr);
	if (rc != 0) {
		ERROR("Wrong format for SPM Core manifest (%d).\n", rc);
		goto exit_unmap;
	}

	/* Check SPMC manifest fits within the upper mapped page boundary */
	if (mapped_size < fdt_totalsize(pm_addr)) {
		ERROR("SPM Core manifest too large.\n");
		rc = -EINVAL;
		goto exit_unmap;
	}

	VERBOSE("Reading SPM Core manifest at address %p\n", pm_addr);

	rc = fdt_node_offset_by_compatible(pm_addr, -1,
				"arm,ffa-core-manifest-1.0");
	if (rc < 0) {
		ERROR("Unrecognized SPM Core manifest\n");
		goto exit_unmap;
	}

	rc = manifest_parse_root(manifest, pm_addr, rc);

exit_unmap:
	unmap_ret = mmap_remove_dynamic_region(pm_base_align, PAGE_SIZE);
	if (unmap_ret != 0) {
		ERROR("Error while unmapping SPM Core manifest (%d).\n",
			unmap_ret);
		if (rc == 0) {
			rc = unmap_ret;
		}
	}

	return rc;
}