1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "trampoline.h"
6
7 #include <inttypes.h>
8 #include <libzbi/zbi.h>
9 #include <string.h>
10 #include <zircon/boot/e820.h>
11
12 #define BOOT_LOADER_NAME_ENV "multiboot.boot_loader_name="
13
zbi_size(const zbi_header_t * zbi)14 static size_t zbi_size(const zbi_header_t* zbi) {
15 return sizeof(*zbi) + zbi->length;
16 }
17
18 // Convert the multiboot memory information to ZBI_TYPE_E820_TABLE format.
add_memory_info(void * zbi,size_t capacity,const multiboot_info_t * info)19 static void add_memory_info(void* zbi, size_t capacity,
20 const multiboot_info_t* info) {
21 if ((info->flags & MB_INFO_MMAP) &&
22 info->mmap_addr != 0 &&
23 info->mmap_length >= sizeof(memory_map_t)) {
24 size_t nranges = 0;
25 for (memory_map_t* mmap = (void*)info->mmap_addr;
26 (uintptr_t)mmap < info->mmap_addr + info->mmap_length;
27 mmap = (void*)((uintptr_t)mmap + 4 + mmap->size)) {
28 ++nranges;
29 }
30 e820entry_t* ranges;
31 void* payload;
32 zbi_result_t result = zbi_create_section(
33 zbi, capacity, nranges * sizeof(ranges[0]),
34 ZBI_TYPE_E820_TABLE, 0, 0, &payload);
35 if (result != ZBI_RESULT_OK) {
36 panic("zbi_create_section(%p, %#"PRIxPTR", %#zx) failed: %d",
37 zbi, capacity, nranges * sizeof(ranges[0]), (int)result);
38 }
39 ranges = payload;
40 for (memory_map_t* mmap = (void*)info->mmap_addr;
41 (uintptr_t)mmap < info->mmap_addr + info->mmap_length;
42 mmap = (void*)((uintptr_t)mmap + 4 + mmap->size)) {
43 *ranges++ = (e820entry_t){
44 .addr = (((uint64_t)mmap->base_addr_high << 32) |
45 mmap->base_addr_low),
46 .size = (((uint64_t)mmap->length_high << 32) |
47 mmap->length_low),
48 // MB_MMAP_TYPE_* matches E820_* values.
49 .type = mmap->type,
50 };
51 }
52 } else {
53 const e820entry_t ranges[] = {
54 {
55 .addr = 0,
56 .size = info->mem_lower << 10,
57 .type = E820_RAM,
58 },
59 {
60 .addr = 1 << 20,
61 .size = ((uint64_t)info->mem_upper << 10) - (1 << 20),
62 .type = E820_RAM,
63 },
64 };
65 zbi_result_t result = zbi_append_section(
66 zbi, capacity, sizeof(ranges), ZBI_TYPE_E820_TABLE, 0, 0, ranges);
67 if (result != ZBI_RESULT_OK) {
68 panic("zbi_append_section(%p, %#"PRIxPTR", %#zx) failed: %d",
69 zbi, capacity, sizeof(ranges), (int)result);
70 }
71 }
72 }
73
add_cmdline(void * zbi,size_t capacity,const multiboot_info_t * info)74 static void add_cmdline(void* zbi, size_t capacity,
75 const multiboot_info_t* info) {
76 // Boot loader command line.
77 if (info->flags & MB_INFO_CMD_LINE) {
78 const char* cmdline = (void*)info->cmdline;
79 size_t len = strlen(cmdline) + 1;
80 zbi_result_t result = zbi_append_section(
81 zbi, capacity, len, ZBI_TYPE_CMDLINE, 0, 0, cmdline);
82 if (result != ZBI_RESULT_OK) {
83 panic("zbi_append_section(%p, %#"PRIxPTR", %zu) failed: %d",
84 zbi, capacity, len, (int)result);
85 }
86 }
87
88 // Boot loader name.
89 if (info->flags & MB_INFO_BOOT_LOADER) {
90 const char* name = (void*)info->boot_loader_name;
91 size_t len = strlen(name) + 1;
92 void *payload;
93 zbi_result_t result = zbi_create_section(
94 zbi, capacity, sizeof(BOOT_LOADER_NAME_ENV) - 1 + len,
95 ZBI_TYPE_CMDLINE, 0, 0, &payload);
96 if (result != ZBI_RESULT_OK) {
97 panic("zbi_create_section(%p, %#"PRIxPTR", %zu) failed: %d",
98 zbi, capacity, sizeof(BOOT_LOADER_NAME_ENV) - 1 + len,
99 (int)result);
100 }
101 for (char *p = (memcpy(payload, BOOT_LOADER_NAME_ENV,
102 sizeof(BOOT_LOADER_NAME_ENV) - 1) +
103 sizeof(BOOT_LOADER_NAME_ENV) - 1);
104 len > 0;
105 ++p, ++name, --len) {
106 *p = (*name == ' ' || *name == '\t' ||
107 *name == '\n' || *name == '\r') ? '+' : *name;
108 }
109 }
110 }
111
add_zbi_items(void * zbi,size_t capacity,const multiboot_info_t * info)112 static void add_zbi_items(void* zbi, size_t capacity,
113 const multiboot_info_t* info) {
114 add_memory_info(zbi, capacity, info);
115 add_cmdline(zbi, capacity, info);
116 }
117
find_kernel_item(zbi_header_t * hdr,void * payload,void * cookie)118 static zbi_result_t find_kernel_item(zbi_header_t* hdr, void* payload,
119 void* cookie) {
120 if (hdr->type == ZBI_TYPE_KERNEL_X64) {
121 *(const zbi_header_t**)cookie = hdr;
122 return ZBI_RESULT_INCOMPLETE_KERNEL;
123 }
124 return ZBI_RESULT_OK;
125 }
126
multiboot_main(uint32_t magic,multiboot_info_t * info)127 noreturn void multiboot_main(uint32_t magic, multiboot_info_t* info) {
128 if (magic != MULTIBOOT_BOOTLOADER_MAGIC) {
129 panic("bad multiboot magic from bootloader %#"PRIx32" != %#"PRIx32"\n",
130 magic, (uint32_t)MULTIBOOT_BOOTLOADER_MAGIC);
131 }
132
133 uintptr_t upper_memory_limit = 0;
134 if (info->flags & MB_INFO_MEM_SIZE) {
135 upper_memory_limit = info->mem_upper << 10;
136 if (info->mem_upper > (UINT32_MAX >> 10)) {
137 upper_memory_limit = -4096u;
138 }
139 } else if ((info->flags & MB_INFO_MMAP) &&
140 info->mmap_addr != 0 &&
141 info->mmap_length >= sizeof(memory_map_t)) {
142 for (memory_map_t* mmap = (void*)info->mmap_addr;
143 (uintptr_t)mmap < info->mmap_addr + info->mmap_length;
144 mmap = (void*)((uintptr_t)mmap + 4 + mmap->size)) {
145 if (mmap->type == MB_MMAP_TYPE_AVAILABLE) {
146 const uint64_t addr = (((uint64_t)mmap->base_addr_high << 32) |
147 mmap->base_addr_low);
148 const uint64_t len = (((uint64_t)mmap->length_high << 32) |
149 mmap->length_low);
150 const uint64_t end = addr + len;
151 if (addr <= (uint64_t)(uintptr_t)PHYS_LOAD_ADDRESS &&
152 end > (uint64_t)(uintptr_t)PHYS_LOAD_ADDRESS) {
153 if (end > UINT32_MAX) {
154 upper_memory_limit = -4096u;
155 } else {
156 upper_memory_limit = end;
157 }
158 break;
159 }
160 }
161 }
162 if (upper_memory_limit == 0) {
163 panic("multiboot memory map doesn't cover %#" PRIxPTR,
164 (uintptr_t)PHYS_LOAD_ADDRESS);
165 }
166 } else {
167 panic("multiboot memory information missing");
168 }
169
170 if (!(info->flags & MB_INFO_MODS)) {
171 panic("missing multiboot modules");
172 }
173 if (info->mods_count != 1) {
174 panic("cannot handle multiboot mods_count %"PRIu32" != 1\n",
175 info->mods_count);
176 }
177
178 module_t* const mod = (void*)info->mods_addr;
179 zbi_header_t* zbi = (void*)mod->mod_start;
180 size_t zbi_len = mod->mod_end - mod->mod_start;
181
182 if (zbi == NULL || zbi_len < sizeof(*zbi)) {
183 panic("insufficient multiboot module [%#"PRIx32",%#"PRIx32")"
184 " for ZBI header", mod->mod_start, mod->mod_end);
185 }
186
187 // TODO(mcgrathr): Sanity check disabled for now because Depthcharge as of
188 // https://chromium.googlesource.com/chromiumos/platform/depthcharge/+/firmware-eve-9584.B
189 // prepends items and adjusts the ZBI container header, but fails to update
190 // the Multiboot module_t header to match.
191 if (zbi_len < sizeof(*zbi) + zbi->length && 0) {
192 panic("insufficient multiboot module [%#"PRIx32",%#"PRIx32")"
193 " for ZBI length %#"PRIx32,
194 mod->mod_start, mod->mod_end, sizeof(*zbi) + zbi->length);
195 }
196
197 // Depthcharge prepends items to the ZBI, so the kernel is not necessarily
198 // first in the image seen here even though that is a requirement of the
199 // protocol with actual ZBI bootloaders. Hence this can't use
200 // zbi_check_complete.
201 zbi_header_t* bad_hdr;
202 zbi_result_t result = zbi_check(zbi, &bad_hdr);
203 if (result != ZBI_RESULT_OK) {
204 panic("ZBI failed check: %d at offset %#zx",
205 (int)result, (size_t)((uint8_t*)bad_hdr - (uint8_t*)zbi));
206 }
207
208 // Find the kernel item.
209 const zbi_header_t* kernel_item_header = NULL;
210 result = zbi_for_each(zbi, &find_kernel_item, &kernel_item_header);
211 if (result != ZBI_RESULT_INCOMPLETE_KERNEL) {
212 panic("ZBI missing kernel");
213 }
214
215 // This is the kernel item's payload, but it expects the whole
216 // zircon_kernel_t (i.e. starting with the container header) to be loaded
217 // at PHYS_LOAD_ADDRESS.
218 const zbi_kernel_t* kernel_header = (const void*)(kernel_item_header + 1);
219
220 // The kernel will sit at PHYS_LOAD_ADDRESS, where the code now
221 // running sits. The space until kernel_memory_end is reserved
222 // and can't be used for anything else.
223 const size_t kernel_load_size =
224 offsetof(zircon_kernel_t, data_kernel) + kernel_item_header->length;
225 uint8_t* const kernel_load_end = PHYS_LOAD_ADDRESS + kernel_load_size;
226 uint8_t* const kernel_memory_end =
227 kernel_load_end + ZBI_ALIGN(kernel_header->reserve_memory_size);
228
229 if (upper_memory_limit < (uintptr_t)kernel_memory_end) {
230 panic("upper memory limit %#"PRIxPTR" < kernel end %p",
231 upper_memory_limit, kernel_memory_end);
232 }
233
234 // Now we can append other items to the ZBI.
235 const size_t capacity = upper_memory_limit - (uintptr_t)zbi;
236 add_zbi_items(zbi, capacity, info);
237
238 // Use discarded ZBI space to hold the trampoline.
239 void* trampoline;
240 result = zbi_create_section(zbi, capacity, sizeof(struct trampoline),
241 ZBI_TYPE_DISCARD, 0, 0, &trampoline);
242 if (result != ZBI_RESULT_OK) {
243 panic("zbi_create_section(%p, %#"PRIxPTR", %#zx) failed: %d",
244 zbi, capacity, sizeof(struct trampoline), (int)result);
245 }
246
247 uint8_t* const zbi_end = (uint8_t*)zbi + zbi_size(zbi);
248 uintptr_t free_memory = (uintptr_t)kernel_memory_end;
249 if ((uint8_t*)zbi < kernel_memory_end) {
250 // The ZBI overlaps where the kernel needs to sit. Copy it further up.
251 zbi_header_t* new_zbi = (void*)zbi_end;
252 if ((uint8_t*)zbi_end < kernel_memory_end) {
253 new_zbi = (void*)kernel_memory_end;
254 }
255 // It needs to be page-aligned.
256 new_zbi = (void*)(((uintptr_t)new_zbi + 4096 - 1) & -4096);
257 memmove(new_zbi, zbi, zbi_size(zbi));
258 free_memory = (uintptr_t)new_zbi + zbi_size(zbi);
259 zbi = new_zbi;
260 }
261
262 // Set up page tables in free memory.
263 enable_64bit_paging(free_memory, upper_memory_limit);
264
265 // Copy the kernel into place and enter its code in 64-bit mode.
266 boot_zbi(zbi, kernel_item_header, trampoline);
267 }
268