1 /*
2  * Copyright 2018 The Hafnium Authors.
3  *
4  * 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.
7  */
8 
9 #include "hf/arch/init.h"
10 
11 #include <stdalign.h>
12 #include <stddef.h>
13 
14 #include "hf/arch/other_world.h"
15 #include "hf/arch/plat/ffa.h"
16 
17 #include "hf/api.h"
18 #include "hf/boot_flow.h"
19 #include "hf/boot_params.h"
20 #include "hf/cpio.h"
21 #include "hf/cpu.h"
22 #include "hf/dlog.h"
23 #include "hf/fdt_handler.h"
24 #include "hf/load.h"
25 #include "hf/manifest.h"
26 #include "hf/mm.h"
27 #include "hf/mpool.h"
28 #include "hf/panic.h"
29 #include "hf/plat/boot_flow.h"
30 #include "hf/plat/console.h"
31 #include "hf/plat/interrupts.h"
32 #include "hf/plat/iommu.h"
33 #include "hf/std.h"
34 #include "hf/vm.h"
35 
36 #include "vmapi/hf/call.h"
37 
38 alignas(MM_PPOOL_ENTRY_SIZE) char ptable_buf[MM_PPOOL_ENTRY_SIZE * HEAP_PAGES];
39 
40 static struct mpool ppool;
41 
42 /**
43  * Performs one-time initialisation of memory management for the hypervisor.
44  *
45  * This is the only C code entry point called with MMU and caching disabled. The
46  * page table returned is used to set up the MMU and caches for all subsequent
47  * code.
48  */
one_time_init_mm(void)49 void one_time_init_mm(void)
50 {
51 	/* Make sure the console is initialised before calling dlog. */
52 	plat_console_init();
53 
54 	plat_ffa_log_init();
55 
56 	mpool_init(&ppool, MM_PPOOL_ENTRY_SIZE);
57 	mpool_add_chunk(&ppool, ptable_buf, sizeof(ptable_buf));
58 
59 	if (!mm_init(&ppool)) {
60 		panic("mm_init failed");
61 	}
62 }
63 
64 /**
65  * Performs one-time initialisation of the hypervisor.
66  */
one_time_init(void)67 void one_time_init(void)
68 {
69 	struct string manifest_fname = STRING_INIT("manifest.dtb");
70 	struct fdt fdt;
71 	enum manifest_return_code manifest_ret;
72 	struct boot_params *params;
73 	struct boot_params_update update;
74 	struct memiter cpio;
75 	struct memiter manifest_it;
76 	void *initrd;
77 	size_t i;
78 	struct mm_stage1_locked mm_stage1_locked;
79 	struct manifest *manifest;
80 
81 	arch_one_time_init();
82 
83 	/* Enable locks now that mm is initialised. */
84 	dlog_enable_lock();
85 	mpool_enable_locks();
86 
87 	mm_stage1_locked = mm_lock_stage1();
88 
89 	if (!fdt_map(&fdt, mm_stage1_locked, plat_boot_flow_get_fdt_addr(),
90 		     &ppool)) {
91 		panic("Unable to map FDT.");
92 	}
93 
94 	static_assert(sizeof(struct boot_params) <= MM_PPOOL_ENTRY_SIZE,
95 		      "The sizeof boot params must fit an entry of the mpool.");
96 	params = (struct boot_params *)mpool_alloc(&ppool);
97 
98 	if (params == NULL) {
99 		panic("Could not use memory pool to allocate boot params.");
100 	}
101 
102 	if (!boot_flow_get_params(params, &fdt)) {
103 		panic("Could not parse boot params.");
104 	}
105 
106 	for (i = 0; i < params->mem_ranges_count; ++i) {
107 		dlog_info("Memory range:  %#lx - %#lx\n",
108 			  pa_addr(params->mem_ranges[i].begin),
109 			  pa_addr(params->mem_ranges[i].end) - 1);
110 	}
111 
112 	/*
113 	 * Hafnium manifest is either gathered from the ramdisk or passed
114 	 * directly to Hafnium entry point by the earlier bootloader stage.
115 	 * If the ramdisk start address is non-zero it hints the manifest
116 	 * shall be looked up from the ramdisk. If zero, assume the address
117 	 * passed to Hafnium entry point is the manifest address.
118 	 */
119 	if (pa_addr(params->initrd_begin)) {
120 		dlog_info("Ramdisk range: %#lx - %#lx\n",
121 			  pa_addr(params->initrd_begin),
122 			  pa_addr(params->initrd_end) - 1);
123 
124 		/* Map initrd in, and initialise cpio parser. */
125 		initrd = mm_identity_map(mm_stage1_locked, params->initrd_begin,
126 					 params->initrd_end, MM_MODE_R, &ppool);
127 		if (!initrd) {
128 			panic("Unable to map initrd.");
129 		}
130 
131 		memiter_init(&cpio, initrd,
132 			     pa_difference(params->initrd_begin,
133 					   params->initrd_end));
134 
135 		if (!cpio_get_file(&cpio, &manifest_fname, &manifest_it)) {
136 			panic("Could not find manifest in initrd.");
137 		}
138 	} else {
139 		manifest_it = fdt.buf;
140 	}
141 
142 	dlog_verbose("Manifest range: %p - %p (%ld bytes)\n",
143 		     (void *)manifest_it.next, (void *)manifest_it.limit,
144 		     manifest_it.limit - manifest_it.next);
145 	if (!is_aligned(manifest_it.next, 4)) {
146 		panic("Manifest not aligned.");
147 	}
148 
149 	manifest_ret = manifest_init(mm_stage1_locked, &manifest, &manifest_it,
150 				     params, &ppool);
151 
152 	if (manifest_ret != MANIFEST_SUCCESS) {
153 		panic("Could not parse manifest: %s.",
154 		      manifest_strerror(manifest_ret));
155 	}
156 
157 	plat_ffa_set_tee_enabled(manifest->ffa_tee_enabled);
158 
159 	if (!plat_iommu_init(&fdt, mm_stage1_locked, &ppool)) {
160 		panic("Could not initialize IOMMUs.");
161 	}
162 
163 	cpu_module_init(params->cpu_ids, params->cpu_count);
164 
165 	if (!plat_interrupts_controller_driver_init(&fdt, mm_stage1_locked,
166 						    &ppool)) {
167 		panic("Could not initialize Interrupt Controller driver.");
168 	}
169 
170 	if (!fdt_unmap(&fdt, mm_stage1_locked, &ppool)) {
171 		panic("Unable to unmap FDT.");
172 	}
173 
174 	/* Load all VMs. */
175 	update.reserved_ranges_count = 0;
176 	if (!load_vms(mm_stage1_locked, manifest, &cpio, params, &update,
177 		      &ppool)) {
178 		panic("Unable to load VMs.");
179 	}
180 
181 	if (!boot_flow_update(mm_stage1_locked, manifest, &update, &cpio,
182 			      &ppool)) {
183 		panic("Unable to update boot flow.");
184 	}
185 
186 	/* Free space allocated for the boot parameters. */
187 	mpool_free(&ppool, params);
188 
189 	/* Now manifest parsing has completed free the resourses used. */
190 	manifest_deinit(&ppool);
191 
192 	mm_unlock_stage1(&mm_stage1_locked);
193 
194 	/* Enable TLB invalidation for VM page table updates. */
195 	mm_vm_enable_invalidation();
196 
197 	/* Perform platform specfic FF-A initialization. */
198 	plat_ffa_init(&ppool);
199 
200 	/* Initialise the API page pool. ppool will be empty from now on. */
201 	api_init(&ppool);
202 
203 	dlog_info("Hafnium initialisation completed\n");
204 }
205