1 // Copyright 2017 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 "osboot.h"
6
7 #include <efi/protocol/graphics-output.h>
8 #include <efi/runtime-services.h>
9 #include <efi/zircon.h>
10
11 #include <cmdline.h>
12 #include <inttypes.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <xefi.h>
16
17 #include <zircon/boot/image.h>
18 #include <zircon/pixelformat.h>
19
20
21 static efi_guid zircon_guid = ZIRCON_VENDOR_GUID;
22 static char16_t crashlog_name[] = ZIRCON_CRASHLOG_EFIVAR;
23
get_last_crashlog(efi_system_table * sys,void * ptr,size_t max)24 static size_t get_last_crashlog(efi_system_table* sys, void* ptr, size_t max) {
25 efi_runtime_services* rs = sys->RuntimeServices;
26
27 uint32_t attr = ZIRCON_CRASHLOG_EFIATTR;
28 size_t sz = max;
29 efi_status r = rs->GetVariable(crashlog_name, &zircon_guid, &attr, &sz, ptr);
30 if (r == EFI_SUCCESS) {
31 // Erase it
32 rs->SetVariable(crashlog_name, &zircon_guid, ZIRCON_CRASHLOG_EFIATTR, 0, NULL);
33 } else {
34 sz = 0;
35 }
36 return sz;
37 }
38
39 static unsigned char scratch[32768];
40
start_zircon(uint64_t entry,void * bootdata)41 static void start_zircon(uint64_t entry, void* bootdata) {
42 #if __x86_64__
43 // ebx = 0, ebp = 0, edi = 0, esi = bootdata
44 __asm__ __volatile__(
45 "movl $0, %%ebp \n"
46 "cli \n"
47 "jmp *%[entry] \n" ::[entry] "a"(entry),
48 [bootdata] "S"(bootdata),
49 "b"(0), "D"(0));
50 #else
51 #warning "add code for other arches here"
52 #endif
53 for (;;)
54 ;
55 }
56
add_bootdata(void ** ptr,size_t * avail,zbi_header_t * bd,void * data)57 static int add_bootdata(void** ptr, size_t* avail,
58 zbi_header_t* bd, void* data) {
59 size_t len = ZBI_ALIGN(bd->length);
60 if ((sizeof(zbi_header_t) + len) > *avail) {
61 printf("boot: no room for bootdata type=%08x size=%08x\n",
62 bd->type, bd->length);
63 return -1;
64 }
65 bd->flags |= ZBI_FLAG_VERSION;
66 bd->reserved0 = 0;
67 bd->reserved1 = 0;
68 bd->magic = ZBI_ITEM_MAGIC;
69 bd->crc32 = ZBI_ITEM_NO_CRC32;
70
71 memcpy(*ptr, bd, sizeof(zbi_header_t));
72 memcpy((*ptr) + sizeof(zbi_header_t), data, len);
73 len += sizeof(zbi_header_t);
74 (*ptr) += len;
75 (*avail) -= len;
76
77 return 0;
78 }
79
image_getsize(void * image,size_t sz)80 size_t image_getsize(void* image, size_t sz) {
81 if (sz < sizeof(zircon_kernel_t)) {
82 return 0;
83 }
84 zircon_kernel_t* kernel = image;
85 if ((kernel->hdr_file.type != ZBI_TYPE_CONTAINER) ||
86 (kernel->hdr_file.magic != ZBI_ITEM_MAGIC) ||
87 (kernel->hdr_kernel.type != ZBI_TYPE_KERNEL_X64) ||
88 (kernel->hdr_kernel.magic != ZBI_ITEM_MAGIC)) {
89 return 0;
90 }
91 return ZBI_ALIGN(kernel->hdr_file.length) + sizeof(zbi_header_t);
92 }
93
header_check(void * image,size_t sz,uint64_t * _entry,size_t * _flen,size_t * _klen)94 static int header_check(void* image, size_t sz, uint64_t* _entry,
95 size_t* _flen, size_t* _klen) {
96 zbi_header_t* bd = image;
97 size_t flen, klen;
98 uint64_t entry;
99
100 if (!(bd->flags & ZBI_FLAG_VERSION)) {
101 printf("boot: v1 bootdata kernel no longer supported\n");
102 return -1;
103 }
104 zircon_kernel_t* kernel = image;
105 if ((sz < sizeof(zircon_kernel_t)) ||
106 (kernel->hdr_kernel.type != ZBI_TYPE_KERNEL_X64) ||
107 ((kernel->hdr_kernel.flags & ZBI_FLAG_VERSION) == 0)) {
108 printf("boot: invalid zircon kernel header\n");
109 return -1;
110 }
111 flen = ZBI_ALIGN(kernel->hdr_file.length);
112 klen = ZBI_ALIGN(kernel->hdr_kernel.length);
113 entry = kernel->data_kernel.entry;
114 if (flen > (sz - sizeof(zbi_header_t))) {
115 printf("boot: invalid zircon kernel header (bad flen)\n");
116 return -1;
117 }
118
119 if (klen > (sz - (sizeof(zbi_header_t) * 2))) {
120 printf("boot: invalid zircon kernel header (bad klen)\n");
121 return -1;
122 }
123 if (_entry) {
124 *_entry = entry;
125 }
126 if (_flen) {
127 *_flen = flen;
128 *_klen = klen;
129 }
130
131 return 0;
132 }
133
item_check(zbi_header_t * bd,size_t sz)134 static int item_check(zbi_header_t* bd, size_t sz) {
135 if (sz > 0x7FFFFFFF) {
136 // disallow 2GB+ items to avoid wrap on align issues
137 return -1;
138 }
139 if ((bd->magic != ZBI_ITEM_MAGIC) ||
140 ((bd->flags & ZBI_FLAG_VERSION) == 0) ||
141 (ZBI_ALIGN(bd->length) > sz)) {
142 return -1;
143 } else {
144 return 0;
145 }
146 }
147
148 //TODO: verify crc32 when present
identify_image(void * image,size_t sz)149 unsigned identify_image(void* image, size_t sz) {
150 if (sz == 0) {
151 return IMAGE_EMPTY;
152 }
153 if (sz < sizeof(zbi_header_t)) {
154 printf("image is too small\n");
155 return IMAGE_INVALID;
156 }
157 zbi_header_t* bd = image;
158 sz -= sizeof(zbi_header_t);
159 if ((bd->type != ZBI_TYPE_CONTAINER) ||
160 item_check(bd, sz)) {
161 printf("image has invalid header\n");
162 return IMAGE_INVALID;
163 }
164 image += sizeof(zbi_header_t);
165 unsigned n = 0;
166 unsigned r = 0;
167 while (sz > sizeof(zbi_header_t)) {
168 bd = image;
169 sz -= sizeof(zbi_header_t);
170 if (item_check(image, sz)) {
171 printf("image has invalid bootitem\n");
172 return IMAGE_INVALID;
173 }
174 if (ZBI_IS_KERNEL_BOOTITEM(bd->type)) {
175 if (n != 0) {
176 printf("image has kernel in middle\n");
177 return IMAGE_INVALID;
178 } else {
179 r = IMAGE_KERNEL;
180 }
181 }
182 if (bd->type == ZBI_TYPE_STORAGE_BOOTFS) {
183 if ((r == IMAGE_KERNEL) || (r == IMAGE_COMBO)) {
184 r = IMAGE_COMBO;
185 } else {
186 r = IMAGE_RAMDISK;
187 }
188 }
189 image += ZBI_ALIGN(bd->length) + sizeof(zbi_header_t);
190 sz -= ZBI_ALIGN(bd->length);
191 n++;
192 }
193
194 return r;
195 }
196
boot_zircon(efi_handle img,efi_system_table * sys,void * image,size_t isz,void * ramdisk,size_t rsz,void * cmdline,size_t csz)197 int boot_zircon(efi_handle img, efi_system_table* sys,
198 void* image, size_t isz, void* ramdisk, size_t rsz,
199 void* cmdline, size_t csz) {
200
201 efi_boot_services* bs = sys->BootServices;
202 uint64_t entry;
203
204 if (header_check(image, isz, &entry, NULL, NULL)) {
205 return -1;
206 }
207 if ((ramdisk == NULL) || (rsz < sizeof(zbi_header_t))) {
208 printf("boot: ramdisk missing or too small\n");
209 return -1;
210 }
211 if (isz > kernel_zone_size) {
212 printf("boot: kernel image too large\n");
213 return -1;
214 }
215
216 zbi_header_t* hdr0 = ramdisk;
217 if ((hdr0->type != ZBI_TYPE_CONTAINER) ||
218 (hdr0->extra != ZBI_CONTAINER_MAGIC) ||
219 !(hdr0->flags & ZBI_FLAG_VERSION)) {
220 printf("boot: ramdisk has invalid bootdata header\n");
221 return -1;
222 }
223
224 if ((hdr0->length > (rsz - sizeof(zbi_header_t)))) {
225 printf("boot: ramdisk has invalid bootdata length\n");
226 return -1;
227 }
228
229 // osboot ensures we have FRONT_BYTES ahead of the
230 // ramdisk to prepend our own bootdata items.
231 void* bptr = ramdisk - FRONT_BYTES;
232 size_t blen = FRONT_BYTES;
233
234 // We create a new container header of the same size
235 // as the one at the start of the ramdisk
236 zbi_header_t hdr = ZBI_CONTAINER_HEADER(hdr0->length + FRONT_BYTES);
237 memcpy(bptr, &hdr, sizeof(hdr));
238 bptr += sizeof(hdr);
239
240 // pass kernel commandline
241 hdr.type = ZBI_TYPE_CMDLINE;
242 hdr.length = csz;
243 hdr.extra = 0;
244 hdr.flags = ZBI_FLAG_VERSION;
245 if (add_bootdata(&bptr, &blen, &hdr, cmdline)) {
246 return -1;
247 }
248
249 // pass ACPI root pointer
250 uint64_t rsdp = find_acpi_root(img, sys);
251 if (rsdp != 0) {
252 hdr.type = ZBI_TYPE_ACPI_RSDP;
253 hdr.length = sizeof(rsdp);
254 if (add_bootdata(&bptr, &blen, &hdr, &rsdp)) {
255 return -1;
256 }
257 }
258
259 // pass SMBIOS entry point pointer
260 uint64_t smbios = find_smbios(img, sys);
261 if (smbios != 0) {
262 hdr.type = ZBI_TYPE_SMBIOS;
263 hdr.length = sizeof(smbios);
264 if (add_bootdata(&bptr, &blen, &hdr, &smbios)) {
265 return -1;
266 }
267 }
268
269 // pass EFI system table
270 uint64_t addr = (uintptr_t) sys;
271 hdr.type = ZBI_TYPE_EFI_SYSTEM_TABLE;
272 hdr.length = sizeof(sys);
273 if (add_bootdata(&bptr, &blen, &hdr, &addr)) {
274 return -1;
275 }
276
277 // pass framebuffer data
278 efi_graphics_output_protocol* gop = NULL;
279 bs->LocateProtocol(&GraphicsOutputProtocol, NULL, (void**)&gop);
280 if (gop) {
281 zbi_swfb_t fb = {
282 .base = gop->Mode->FrameBufferBase,
283 .width = gop->Mode->Info->HorizontalResolution,
284 .height = gop->Mode->Info->VerticalResolution,
285 .stride = gop->Mode->Info->PixelsPerScanLine,
286 .format = get_zx_pixel_format(gop),
287 };
288 hdr.type = ZBI_TYPE_FRAMEBUFFER;
289 hdr.length = sizeof(fb);
290 if (add_bootdata(&bptr, &blen, &hdr, &fb)) {
291 return -1;
292 }
293 }
294
295 memcpy((void*)kernel_zone_base, image, isz);
296
297 // Obtain the system memory map
298 size_t msize, dsize;
299 for (int attempts = 0;;attempts++) {
300 efi_memory_descriptor* mmap = (efi_memory_descriptor*) (scratch + sizeof(uint64_t));
301 uint32_t dversion = 0;
302 size_t mkey = 0;
303 msize = sizeof(scratch) - sizeof(uint64_t);
304 dsize = 0;
305 efi_status r = sys->BootServices->GetMemoryMap(&msize, mmap, &mkey, &dsize, &dversion);
306 if (r != EFI_SUCCESS) {
307 printf("boot: cannot GetMemoryMap()\n");
308 goto fail;
309 }
310
311 r = sys->BootServices->ExitBootServices(img, mkey);
312 if (r == EFI_SUCCESS) {
313 break;
314 }
315 if (r == EFI_INVALID_PARAMETER) {
316 if (attempts > 0) {
317 printf("boot: cannot ExitBootServices(): %s\n", xefi_strerror(r));
318 goto fail;
319 }
320 // Attempting to exit may cause us to have to re-grab the
321 // memory map, but if it happens more than once something's
322 // broken.
323 continue;
324 }
325 printf("boot: cannot ExitBootServices(): %s\n", xefi_strerror(r));
326 goto fail;
327 }
328 memcpy(scratch, &dsize, sizeof(uint64_t));
329
330 // install memory map
331 hdr.type = ZBI_TYPE_EFI_MEMORY_MAP;
332 hdr.length = msize + sizeof(uint64_t);
333 if (add_bootdata(&bptr, &blen, &hdr, scratch)) {
334 goto fail;
335 }
336
337 // obtain the last crashlog if we can
338 size_t sz = get_last_crashlog(sys, scratch, 4096);
339 if (sz > 0) {
340 hdr.type = ZBI_TYPE_CRASHLOG;
341 hdr.length = sz;
342 add_bootdata(&bptr, &blen, &hdr, scratch);
343 }
344
345 // fill the remaining gap between pre-data and ramdisk image
346 if ((blen < sizeof(hdr)) || (blen & 7)) {
347 goto fail;
348 }
349 hdr.type = ZBI_TYPE_DISCARD;
350 hdr.length = blen - sizeof(hdr);
351 hdr.flags = ZBI_FLAG_VERSION;
352 memcpy(bptr, &hdr, sizeof(hdr));
353
354 // jump to the kernel
355 start_zircon(entry, ramdisk - FRONT_BYTES);
356
357 fail:
358 return -1;
359 }
360
361 static char cmdline[CMDLINE_MAX];
362
zedboot(efi_handle img,efi_system_table * sys,void * image,size_t sz)363 int zedboot(efi_handle img, efi_system_table* sys,
364 void* image, size_t sz) {
365
366 size_t flen, klen;
367 if (header_check(image, sz, NULL, &flen, &klen)) {
368 return -1;
369 }
370
371 // ramdisk portion is file - headers - kernel len
372 uint32_t rlen = flen - sizeof(zbi_header_t) - klen;
373 uint32_t roff = (sizeof(zbi_header_t) * 2) + klen;
374 if (rlen == 0) {
375 printf("zedboot: no ramdisk?!\n");
376 return -1;
377 }
378
379 // allocate space for the ramdisk
380 efi_boot_services* bs = sys->BootServices;
381 size_t rsz = rlen + sizeof(zbi_header_t) + FRONT_BYTES;
382 size_t pages = BYTES_TO_PAGES(rsz);
383 void* ramdisk = NULL;
384 efi_status r = bs->AllocatePages(AllocateAnyPages, EfiLoaderData, pages,
385 (efi_physical_addr*)&ramdisk);
386 if (r) {
387 printf("zedboot: cannot allocate ramdisk buffer\n");
388 return -1;
389 }
390
391 ramdisk += FRONT_BYTES;
392 *(zbi_header_t*)ramdisk = (zbi_header_t)ZBI_CONTAINER_HEADER(rlen);
393 memcpy(ramdisk + sizeof(zbi_header_t), image + roff, rlen);
394 rlen += sizeof(zbi_header_t);
395
396 printf("ramdisk @ %p\n", ramdisk);
397
398 size_t csz = cmdline_to_string(cmdline, sizeof(cmdline));
399
400 // shrink original image header to include only the kernel
401 zircon_kernel_t* kernel = image;
402 kernel->hdr_file.length = sizeof(zbi_header_t) + klen;
403
404 return boot_zircon(img, sys, image, roff, ramdisk, rlen, cmdline, csz);
405 }
406
boot_kernel(efi_handle img,efi_system_table * sys,void * image,size_t sz,void * ramdisk,size_t rsz)407 int boot_kernel(efi_handle img, efi_system_table* sys,
408 void* image, size_t sz,
409 void* ramdisk, size_t rsz) {
410
411 size_t csz = cmdline_to_string(cmdline, sizeof(cmdline));
412
413 zbi_header_t* bd = image;
414 if ((bd->type == ZBI_TYPE_CONTAINER) &&
415 (bd->extra == ZBI_CONTAINER_MAGIC)) {
416 return boot_zircon(img, sys, image, sz, ramdisk, rsz, cmdline, csz);
417 } else {
418 return -1;
419 }
420 }
421