blob: 4d0496f21411157630df15374776af5b0f702cab [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
Andrew Scull18834872018-10-12 11:48:09 +01007 */
8
Andrew Scull18c78fc2018-08-20 12:57:41 +01009#include "hf/fdt_handler.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010010
David Brazdil7a462ec2019-08-15 12:27:47 +010011#include "hf/check.h"
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000012#include "hf/cpu.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010013#include "hf/dlog.h"
14#include "hf/fdt.h"
15#include "hf/mm.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010016#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010017
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010018/**
Fuad Tabba50469e02020-06-30 15:14:28 +010019 * Initializes the FDT struct with the pointer to the FDT data (header) in
20 * fdt_ptr.
21 */
22bool fdt_struct_from_ptr(const void *fdt_ptr, struct fdt *fdt)
23{
24 size_t fdt_size;
25
26 if (!fdt_ptr || !fdt) {
27 return false;
28 }
29
30 return fdt_size_from_header(fdt_ptr, &fdt_size) &&
31 fdt_init_from_ptr(fdt, fdt_ptr, fdt_size);
32}
33
34/**
David Brazdil0dbb41f2019-09-09 18:03:35 +010035 * Finds the memory region where initrd is stored.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010036 */
David Brazdilb856be62020-03-25 10:14:55 +000037bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010038{
David Brazdilb856be62020-03-25 10:14:55 +000039 struct fdt_node n;
Andrew Scullb401ba32018-11-09 10:30:54 +000040 uint64_t initrd_begin;
41 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +010042
David Brazdilb856be62020-03-25 10:14:55 +000043 if (!fdt_find_node(fdt, "/chosen", &n)) {
44 dlog_error("Unable to find '/chosen'\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010045 return false;
46 }
47
David Brazdilb856be62020-03-25 10:14:55 +000048 if (!fdt_read_number(&n, FDT_PROP_INITRD_START, &initrd_begin)) {
49 dlog_error("Unable to read " FDT_PROP_INITRD_START "\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010050 return false;
51 }
52
David Brazdilb856be62020-03-25 10:14:55 +000053 if (!fdt_read_number(&n, FDT_PROP_INITRD_END, &initrd_end)) {
54 dlog_error("Unable to read " FDT_PROP_INITRD_END "\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010055 return false;
56 }
57
Andrew Scullb401ba32018-11-09 10:30:54 +000058 *begin = pa_init(initrd_begin);
59 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +010060
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010061 return true;
62}
63
David Brazdilb856be62020-03-25 10:14:55 +000064bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count)
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000065{
David Brazdilb856be62020-03-25 10:14:55 +000066 static const struct string str_cpu = STRING_INIT("cpu");
67 struct fdt_node n;
68 size_t addr_size;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000069
70 *cpu_count = 0;
71
David Brazdilb856be62020-03-25 10:14:55 +000072 if (!fdt_find_node(fdt, "/cpus", &n)) {
73 dlog_error("Unable to find '/cpus'\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +010074 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000075 }
76
David Brazdilb856be62020-03-25 10:14:55 +000077 if (!fdt_address_size(&n, &addr_size)) {
78 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000079 }
80
David Brazdilb856be62020-03-25 10:14:55 +000081 if (!fdt_first_child(&n)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +010082 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000083 }
84
85 do {
David Brazdilb856be62020-03-25 10:14:55 +000086 struct memiter data;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000087
David Brazdilb856be62020-03-25 10:14:55 +000088 if (!fdt_read_property(&n, "device_type", &data) ||
89 !string_eq(&str_cpu, &data) ||
90 !fdt_read_property(&n, "reg", &data)) {
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000091 continue;
92 }
93
94 /* Get all entries for this CPU. */
David Brazdilb856be62020-03-25 10:14:55 +000095 while (memiter_size(&data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +010096 uint64_t value;
97
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000098 if (*cpu_count >= MAX_CPUS) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000099 dlog_error("Found more than %d CPUs\n",
100 MAX_CPUS);
David Brazdil0dbb41f2019-09-09 18:03:35 +0100101 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000102 }
103
David Brazdilb856be62020-03-25 10:14:55 +0000104 if (!fdt_parse_number(&data, addr_size, &value)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000105 dlog_error("Could not parse CPU id\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +0100106 return false;
David Brazdil7a462ec2019-08-15 12:27:47 +0100107 }
108 cpu_ids[(*cpu_count)++] = value;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000109 }
David Brazdilb856be62020-03-25 10:14:55 +0000110 } while (fdt_next_sibling(&n));
David Brazdil0dbb41f2019-09-09 18:03:35 +0100111
112 return true;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000113}
114
Fuad Tabba50469e02020-06-30 15:14:28 +0100115bool fdt_find_memory_ranges(const struct fdt *fdt,
116 const struct string *device_type,
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000117 struct mem_range *mem_ranges,
118 size_t *mem_ranges_count, size_t mem_range_limit)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100119{
David Brazdilb856be62020-03-25 10:14:55 +0000120 struct fdt_node n;
121 size_t addr_size;
122 size_t size_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100123 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100124
David Brazdilb856be62020-03-25 10:14:55 +0000125 if (!fdt_find_node(fdt, "/", &n) || !fdt_address_size(&n, &addr_size) ||
126 !fdt_size_size(&n, &size_size)) {
127 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100128 }
129
David Brazdilb856be62020-03-25 10:14:55 +0000130 /* Look for nodes with the device_type set to `device_type`. */
131 if (!fdt_first_child(&n)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100132 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100133 }
134
135 do {
David Brazdilb856be62020-03-25 10:14:55 +0000136 struct memiter data;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000137
David Brazdilb856be62020-03-25 10:14:55 +0000138 if (!fdt_read_property(&n, "device_type", &data) ||
139 !string_eq(device_type, &data) ||
140 !fdt_read_property(&n, "reg", &data)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100141 continue;
142 }
143
144 /* Traverse all memory ranges within this node. */
David Brazdilb856be62020-03-25 10:14:55 +0000145 while (memiter_size(&data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100146 uintpaddr_t addr;
147 size_t len;
148
David Brazdilb856be62020-03-25 10:14:55 +0000149 CHECK(fdt_parse_number(&data, addr_size, &addr));
150 CHECK(fdt_parse_number(&data, size_size, &len));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100151
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000152 if (mem_range_index < mem_range_limit) {
153 mem_ranges[mem_range_index].begin =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100154 pa_init(addr);
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000155 mem_ranges[mem_range_index].end =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100156 pa_init(addr + len);
157 ++mem_range_index;
158 } else {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000159 dlog_error(
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000160 "Found %s range %u in FDT but only %u "
161 "supported, ignoring additional range "
162 "of size %u.\n",
163 string_data(device_type),
164 mem_range_index, mem_range_limit, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100165 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100166 }
David Brazdilb856be62020-03-25 10:14:55 +0000167 } while (fdt_next_sibling(&n));
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000168 *mem_ranges_count = mem_range_index;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100169
170 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100171}
172
David Brazdilb856be62020-03-25 10:14:55 +0000173bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
174 paddr_t fdt_addr, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100175{
David Brazdilb856be62020-03-25 10:14:55 +0000176 const void *fdt_ptr;
177 size_t fdt_len;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100178
179 /* Map the fdt header in. */
David Brazdilb856be62020-03-25 10:14:55 +0000180 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
181 pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
182 MM_MODE_R, ppool);
183 if (!fdt_ptr) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000184 dlog_error("Unable to map FDT header.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000185 return NULL;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100186 }
187
David Brazdilb856be62020-03-25 10:14:55 +0000188 if (!fdt_size_from_header(fdt_ptr, &fdt_len)) {
189 dlog_error("FDT failed header validation.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000190 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100191 }
192
193 /* Map the rest of the fdt in. */
David Brazdilb856be62020-03-25 10:14:55 +0000194 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
195 pa_add(fdt_addr, fdt_len), MM_MODE_R, ppool);
196 if (!fdt_ptr) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000197 dlog_error("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000198 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100199 }
200
David Brazdilb856be62020-03-25 10:14:55 +0000201 if (!fdt_init_from_ptr(fdt, fdt_ptr, fdt_len)) {
202 dlog_error("FDT failed validation.\n");
203 goto fail_full;
204 }
205
206 return true;
207
208fail_full:
209 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_len), ppool);
210 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100211
Andrew Scullb401ba32018-11-09 10:30:54 +0000212fail:
David Brazdilb856be62020-03-25 10:14:55 +0000213 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100214 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100215 return false;
216}
David Brazdilb856be62020-03-25 10:14:55 +0000217
218bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
219 struct mpool *ppool)
220{
221 paddr_t begin = pa_from_va(va_from_ptr(fdt_base(fdt)));
222 paddr_t end = pa_add(begin, fdt_size(fdt));
223
224 if (!mm_unmap(stage1_locked, begin, end, ppool)) {
225 return false;
226 }
227
228 /* Invalidate pointer to the buffer. */
229 fdt_fini(fdt);
230 return true;
231}
Fuad Tabba50469e02020-06-30 15:14:28 +0100232
233/**
234 * Gets the size of the first memory range from the FDT into size.
235 *
236 * The test framework expects the address space to be contiguous, therefore
237 * gets the size of the first memory range, if there is more than one range.
238 */
239bool fdt_get_memory_size(const struct fdt *fdt, size_t *size)
240{
241 const struct string memory_device_type = STRING_INIT("memory");
242 struct mem_range mem_range;
243 size_t mem_ranges_count;
244
245 if (!fdt || !size ||
246 !fdt_find_memory_ranges(fdt, &memory_device_type, &mem_range,
247 &mem_ranges_count, 1)) {
248 return false;
249 }
250
251 if (mem_ranges_count < 1) {
252 return false;
253 }
254
255 *size = pa_difference(mem_range.begin, mem_range.end);
256
257 return true;
258}