blob: 75051ee39e47ef63dd22bd48d59f1b750499e866 [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 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/fdt_handler.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010018
David Brazdil7a462ec2019-08-15 12:27:47 +010019#include "hf/check.h"
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000020#include "hf/cpu.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010021#include "hf/dlog.h"
22#include "hf/fdt.h"
23#include "hf/mm.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010024#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010025
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010026/**
David Brazdil0dbb41f2019-09-09 18:03:35 +010027 * Finds the memory region where initrd is stored.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010028 */
David Brazdilb856be62020-03-25 10:14:55 +000029bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010030{
David Brazdilb856be62020-03-25 10:14:55 +000031 struct fdt_node n;
Andrew Scullb401ba32018-11-09 10:30:54 +000032 uint64_t initrd_begin;
33 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +010034
David Brazdilb856be62020-03-25 10:14:55 +000035 if (!fdt_find_node(fdt, "/chosen", &n)) {
36 dlog_error("Unable to find '/chosen'\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010037 return false;
38 }
39
David Brazdilb856be62020-03-25 10:14:55 +000040 if (!fdt_read_number(&n, FDT_PROP_INITRD_START, &initrd_begin)) {
41 dlog_error("Unable to read " FDT_PROP_INITRD_START "\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010042 return false;
43 }
44
David Brazdilb856be62020-03-25 10:14:55 +000045 if (!fdt_read_number(&n, FDT_PROP_INITRD_END, &initrd_end)) {
46 dlog_error("Unable to read " FDT_PROP_INITRD_END "\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010047 return false;
48 }
49
Andrew Scullb401ba32018-11-09 10:30:54 +000050 *begin = pa_init(initrd_begin);
51 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +010052
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010053 return true;
54}
55
David Brazdilb856be62020-03-25 10:14:55 +000056bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count)
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000057{
David Brazdilb856be62020-03-25 10:14:55 +000058 static const struct string str_cpu = STRING_INIT("cpu");
59 struct fdt_node n;
60 size_t addr_size;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000061
62 *cpu_count = 0;
63
David Brazdilb856be62020-03-25 10:14:55 +000064 if (!fdt_find_node(fdt, "/cpus", &n)) {
65 dlog_error("Unable to find '/cpus'\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +010066 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000067 }
68
David Brazdilb856be62020-03-25 10:14:55 +000069 if (!fdt_address_size(&n, &addr_size)) {
70 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000071 }
72
David Brazdilb856be62020-03-25 10:14:55 +000073 if (!fdt_first_child(&n)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +010074 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000075 }
76
77 do {
David Brazdilb856be62020-03-25 10:14:55 +000078 struct memiter data;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000079
David Brazdilb856be62020-03-25 10:14:55 +000080 if (!fdt_read_property(&n, "device_type", &data) ||
81 !string_eq(&str_cpu, &data) ||
82 !fdt_read_property(&n, "reg", &data)) {
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000083 continue;
84 }
85
86 /* Get all entries for this CPU. */
David Brazdilb856be62020-03-25 10:14:55 +000087 while (memiter_size(&data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +010088 uint64_t value;
89
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000090 if (*cpu_count >= MAX_CPUS) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000091 dlog_error("Found more than %d CPUs\n",
92 MAX_CPUS);
David Brazdil0dbb41f2019-09-09 18:03:35 +010093 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000094 }
95
David Brazdilb856be62020-03-25 10:14:55 +000096 if (!fdt_parse_number(&data, addr_size, &value)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000097 dlog_error("Could not parse CPU id\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +010098 return false;
David Brazdil7a462ec2019-08-15 12:27:47 +010099 }
100 cpu_ids[(*cpu_count)++] = value;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000101 }
David Brazdilb856be62020-03-25 10:14:55 +0000102 } while (fdt_next_sibling(&n));
David Brazdil0dbb41f2019-09-09 18:03:35 +0100103
104 return true;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000105}
106
David Brazdilb856be62020-03-25 10:14:55 +0000107bool fdt_find_memory_ranges(const struct fdt *fdt, struct string *device_type,
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000108 struct mem_range *mem_ranges,
109 size_t *mem_ranges_count, size_t mem_range_limit)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100110{
David Brazdilb856be62020-03-25 10:14:55 +0000111 struct fdt_node n;
112 size_t addr_size;
113 size_t size_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100114 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100115
David Brazdilb856be62020-03-25 10:14:55 +0000116 if (!fdt_find_node(fdt, "/", &n) || !fdt_address_size(&n, &addr_size) ||
117 !fdt_size_size(&n, &size_size)) {
118 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100119 }
120
David Brazdilb856be62020-03-25 10:14:55 +0000121 /* Look for nodes with the device_type set to `device_type`. */
122 if (!fdt_first_child(&n)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100123 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100124 }
125
126 do {
David Brazdilb856be62020-03-25 10:14:55 +0000127 struct memiter data;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000128
David Brazdilb856be62020-03-25 10:14:55 +0000129 if (!fdt_read_property(&n, "device_type", &data) ||
130 !string_eq(device_type, &data) ||
131 !fdt_read_property(&n, "reg", &data)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100132 continue;
133 }
134
135 /* Traverse all memory ranges within this node. */
David Brazdilb856be62020-03-25 10:14:55 +0000136 while (memiter_size(&data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100137 uintpaddr_t addr;
138 size_t len;
139
David Brazdilb856be62020-03-25 10:14:55 +0000140 CHECK(fdt_parse_number(&data, addr_size, &addr));
141 CHECK(fdt_parse_number(&data, size_size, &len));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100142
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000143 if (mem_range_index < mem_range_limit) {
144 mem_ranges[mem_range_index].begin =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100145 pa_init(addr);
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000146 mem_ranges[mem_range_index].end =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100147 pa_init(addr + len);
148 ++mem_range_index;
149 } else {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000150 dlog_error(
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000151 "Found %s range %u in FDT but only %u "
152 "supported, ignoring additional range "
153 "of size %u.\n",
154 string_data(device_type),
155 mem_range_index, mem_range_limit, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100156 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100157 }
David Brazdilb856be62020-03-25 10:14:55 +0000158 } while (fdt_next_sibling(&n));
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000159 *mem_ranges_count = mem_range_index;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100160
161 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100162}
163
David Brazdilb856be62020-03-25 10:14:55 +0000164bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
165 paddr_t fdt_addr, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100166{
David Brazdilb856be62020-03-25 10:14:55 +0000167 const void *fdt_ptr;
168 size_t fdt_len;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100169
170 /* Map the fdt header in. */
David Brazdilb856be62020-03-25 10:14:55 +0000171 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
172 pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
173 MM_MODE_R, ppool);
174 if (!fdt_ptr) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000175 dlog_error("Unable to map FDT header.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000176 return NULL;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100177 }
178
David Brazdilb856be62020-03-25 10:14:55 +0000179 if (!fdt_size_from_header(fdt_ptr, &fdt_len)) {
180 dlog_error("FDT failed header validation.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000181 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100182 }
183
184 /* Map the rest of the fdt in. */
David Brazdilb856be62020-03-25 10:14:55 +0000185 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
186 pa_add(fdt_addr, fdt_len), MM_MODE_R, ppool);
187 if (!fdt_ptr) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000188 dlog_error("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000189 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100190 }
191
David Brazdilb856be62020-03-25 10:14:55 +0000192 if (!fdt_init_from_ptr(fdt, fdt_ptr, fdt_len)) {
193 dlog_error("FDT failed validation.\n");
194 goto fail_full;
195 }
196
197 return true;
198
199fail_full:
200 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_len), ppool);
201 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100202
Andrew Scullb401ba32018-11-09 10:30:54 +0000203fail:
David Brazdilb856be62020-03-25 10:14:55 +0000204 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100205 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100206 return false;
207}
David Brazdilb856be62020-03-25 10:14:55 +0000208
209bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
210 struct mpool *ppool)
211{
212 paddr_t begin = pa_from_va(va_from_ptr(fdt_base(fdt)));
213 paddr_t end = pa_add(begin, fdt_size(fdt));
214
215 if (!mm_unmap(stage1_locked, begin, end, ppool)) {
216 return false;
217 }
218
219 /* Invalidate pointer to the buffer. */
220 fdt_fini(fdt);
221 return true;
222}