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