blob: fc62c5c9d086565c8098e2da2e99f482915ba739 [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/**
David Brazdil0dbb41f2019-09-09 18:03:35 +010019 * Finds the memory region where initrd is stored.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010020 */
David Brazdilb856be62020-03-25 10:14:55 +000021bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010022{
David Brazdilb856be62020-03-25 10:14:55 +000023 struct fdt_node n;
Andrew Scullb401ba32018-11-09 10:30:54 +000024 uint64_t initrd_begin;
25 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +010026
David Brazdilb856be62020-03-25 10:14:55 +000027 if (!fdt_find_node(fdt, "/chosen", &n)) {
28 dlog_error("Unable to find '/chosen'\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010029 return false;
30 }
31
David Brazdilb856be62020-03-25 10:14:55 +000032 if (!fdt_read_number(&n, FDT_PROP_INITRD_START, &initrd_begin)) {
33 dlog_error("Unable to read " FDT_PROP_INITRD_START "\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010034 return false;
35 }
36
David Brazdilb856be62020-03-25 10:14:55 +000037 if (!fdt_read_number(&n, FDT_PROP_INITRD_END, &initrd_end)) {
38 dlog_error("Unable to read " FDT_PROP_INITRD_END "\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010039 return false;
40 }
41
Andrew Scullb401ba32018-11-09 10:30:54 +000042 *begin = pa_init(initrd_begin);
43 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +010044
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010045 return true;
46}
47
David Brazdilb856be62020-03-25 10:14:55 +000048bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count)
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000049{
David Brazdilb856be62020-03-25 10:14:55 +000050 static const struct string str_cpu = STRING_INIT("cpu");
51 struct fdt_node n;
52 size_t addr_size;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000053
54 *cpu_count = 0;
55
David Brazdilb856be62020-03-25 10:14:55 +000056 if (!fdt_find_node(fdt, "/cpus", &n)) {
57 dlog_error("Unable to find '/cpus'\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +010058 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000059 }
60
David Brazdilb856be62020-03-25 10:14:55 +000061 if (!fdt_address_size(&n, &addr_size)) {
62 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000063 }
64
David Brazdilb856be62020-03-25 10:14:55 +000065 if (!fdt_first_child(&n)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +010066 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000067 }
68
69 do {
David Brazdilb856be62020-03-25 10:14:55 +000070 struct memiter data;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000071
David Brazdilb856be62020-03-25 10:14:55 +000072 if (!fdt_read_property(&n, "device_type", &data) ||
73 !string_eq(&str_cpu, &data) ||
74 !fdt_read_property(&n, "reg", &data)) {
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000075 continue;
76 }
77
78 /* Get all entries for this CPU. */
David Brazdilb856be62020-03-25 10:14:55 +000079 while (memiter_size(&data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +010080 uint64_t value;
81
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000082 if (*cpu_count >= MAX_CPUS) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000083 dlog_error("Found more than %d CPUs\n",
84 MAX_CPUS);
David Brazdil0dbb41f2019-09-09 18:03:35 +010085 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000086 }
87
David Brazdilb856be62020-03-25 10:14:55 +000088 if (!fdt_parse_number(&data, addr_size, &value)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000089 dlog_error("Could not parse CPU id\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +010090 return false;
David Brazdil7a462ec2019-08-15 12:27:47 +010091 }
92 cpu_ids[(*cpu_count)++] = value;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000093 }
David Brazdilb856be62020-03-25 10:14:55 +000094 } while (fdt_next_sibling(&n));
David Brazdil0dbb41f2019-09-09 18:03:35 +010095
96 return true;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000097}
98
David Brazdilb856be62020-03-25 10:14:55 +000099bool fdt_find_memory_ranges(const struct fdt *fdt, struct string *device_type,
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000100 struct mem_range *mem_ranges,
101 size_t *mem_ranges_count, size_t mem_range_limit)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100102{
David Brazdilb856be62020-03-25 10:14:55 +0000103 struct fdt_node n;
104 size_t addr_size;
105 size_t size_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100106 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100107
David Brazdilb856be62020-03-25 10:14:55 +0000108 if (!fdt_find_node(fdt, "/", &n) || !fdt_address_size(&n, &addr_size) ||
109 !fdt_size_size(&n, &size_size)) {
110 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100111 }
112
David Brazdilb856be62020-03-25 10:14:55 +0000113 /* Look for nodes with the device_type set to `device_type`. */
114 if (!fdt_first_child(&n)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100115 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100116 }
117
118 do {
David Brazdilb856be62020-03-25 10:14:55 +0000119 struct memiter data;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000120
David Brazdilb856be62020-03-25 10:14:55 +0000121 if (!fdt_read_property(&n, "device_type", &data) ||
122 !string_eq(device_type, &data) ||
123 !fdt_read_property(&n, "reg", &data)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100124 continue;
125 }
126
127 /* Traverse all memory ranges within this node. */
David Brazdilb856be62020-03-25 10:14:55 +0000128 while (memiter_size(&data)) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100129 uintpaddr_t addr;
130 size_t len;
131
David Brazdilb856be62020-03-25 10:14:55 +0000132 CHECK(fdt_parse_number(&data, addr_size, &addr));
133 CHECK(fdt_parse_number(&data, size_size, &len));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100134
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000135 if (mem_range_index < mem_range_limit) {
136 mem_ranges[mem_range_index].begin =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100137 pa_init(addr);
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000138 mem_ranges[mem_range_index].end =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100139 pa_init(addr + len);
140 ++mem_range_index;
141 } else {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000142 dlog_error(
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000143 "Found %s range %u in FDT but only %u "
144 "supported, ignoring additional range "
145 "of size %u.\n",
146 string_data(device_type),
147 mem_range_index, mem_range_limit, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100148 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100149 }
David Brazdilb856be62020-03-25 10:14:55 +0000150 } while (fdt_next_sibling(&n));
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000151 *mem_ranges_count = mem_range_index;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100152
153 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100154}
155
David Brazdilb856be62020-03-25 10:14:55 +0000156bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
157 paddr_t fdt_addr, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100158{
David Brazdilb856be62020-03-25 10:14:55 +0000159 const void *fdt_ptr;
160 size_t fdt_len;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100161
162 /* Map the fdt header in. */
David Brazdilb856be62020-03-25 10:14:55 +0000163 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
164 pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
165 MM_MODE_R, ppool);
166 if (!fdt_ptr) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000167 dlog_error("Unable to map FDT header.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000168 return NULL;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100169 }
170
David Brazdilb856be62020-03-25 10:14:55 +0000171 if (!fdt_size_from_header(fdt_ptr, &fdt_len)) {
172 dlog_error("FDT failed header validation.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000173 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100174 }
175
176 /* Map the rest of the fdt in. */
David Brazdilb856be62020-03-25 10:14:55 +0000177 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
178 pa_add(fdt_addr, fdt_len), MM_MODE_R, ppool);
179 if (!fdt_ptr) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000180 dlog_error("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000181 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100182 }
183
David Brazdilb856be62020-03-25 10:14:55 +0000184 if (!fdt_init_from_ptr(fdt, fdt_ptr, fdt_len)) {
185 dlog_error("FDT failed validation.\n");
186 goto fail_full;
187 }
188
189 return true;
190
191fail_full:
192 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_len), ppool);
193 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100194
Andrew Scullb401ba32018-11-09 10:30:54 +0000195fail:
David Brazdilb856be62020-03-25 10:14:55 +0000196 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100197 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100198 return false;
199}
David Brazdilb856be62020-03-25 10:14:55 +0000200
201bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
202 struct mpool *ppool)
203{
204 paddr_t begin = pa_from_va(va_from_ptr(fdt_base(fdt)));
205 paddr_t end = pa_add(begin, fdt_size(fdt));
206
207 if (!mm_unmap(stage1_locked, begin, end, ppool)) {
208 return false;
209 }
210
211 /* Invalidate pointer to the buffer. */
212 fdt_fini(fdt);
213 return true;
214}