[84176f3] | 1 | /*
|
---|
| 2 | * Copyright (c) 2015 Petr Pavlu
|
---|
| 3 | * All rights reserved.
|
---|
| 4 | *
|
---|
| 5 | * Redistribution and use in source and binary forms, with or without
|
---|
| 6 | * modification, are permitted provided that the following conditions
|
---|
| 7 | * are met:
|
---|
| 8 | *
|
---|
| 9 | * - Redistributions of source code must retain the above copyright
|
---|
| 10 | * notice, this list of conditions and the following disclaimer.
|
---|
| 11 | * - Redistributions in binary form must reproduce the above copyright
|
---|
| 12 | * notice, this list of conditions and the following disclaimer in the
|
---|
| 13 | * documentation and/or other materials provided with the distribution.
|
---|
| 14 | * - The name of the author may not be used to endorse or promote products
|
---|
| 15 | * derived from this software without specific prior written permission.
|
---|
| 16 | *
|
---|
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
---|
| 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
---|
| 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
---|
| 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
---|
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
---|
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
---|
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
| 27 | */
|
---|
| 28 |
|
---|
| 29 | /** @addtogroup boot_arm64
|
---|
| 30 | * @{
|
---|
| 31 | */
|
---|
| 32 | /** @file
|
---|
| 33 | * @brief Bootstrap.
|
---|
| 34 | */
|
---|
| 35 |
|
---|
| 36 | #include <stddef.h>
|
---|
| 37 | #include <align.h>
|
---|
| 38 | #include <arch/arch.h>
|
---|
| 39 | #include <arch/asm.h>
|
---|
| 40 | #include <arch/barrier.h>
|
---|
| 41 | #include <arch/main.h>
|
---|
| 42 | #include <arch/regutils.h>
|
---|
| 43 | #include <arch/types.h>
|
---|
| 44 | #include <errno.h>
|
---|
| 45 | #include <inflate.h>
|
---|
| 46 | #include <kernel.h>
|
---|
| 47 | #include <macros.h>
|
---|
| 48 | #include <memstr.h>
|
---|
| 49 | #include <payload.h>
|
---|
| 50 | #include <printf.h>
|
---|
| 51 | #include <putchar.h>
|
---|
| 52 | #include <str.h>
|
---|
| 53 | #include <version.h>
|
---|
| 54 |
|
---|
| 55 | static efi_system_table_t *efi_system_table;
|
---|
| 56 |
|
---|
| 57 | /** Translate given UEFI memory type to the bootinfo memory type.
|
---|
| 58 | *
|
---|
| 59 | * @param type UEFI memory type.
|
---|
| 60 | */
|
---|
| 61 | static memtype_t get_memtype(uint32_t type)
|
---|
| 62 | {
|
---|
| 63 | switch (type) {
|
---|
| 64 | case EFI_RESERVED:
|
---|
| 65 | case EFI_RUNTIME_SERVICES_CODE:
|
---|
| 66 | case EFI_RUNTIME_SERVICES_DATA:
|
---|
| 67 | case EFI_UNUSABLE_MEMORY:
|
---|
| 68 | case EFI_ACPI_MEMORY_NVS:
|
---|
| 69 | case EFI_MEMORY_MAPPED_IO:
|
---|
| 70 | case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
|
---|
| 71 | case EFI_PAL_CODE:
|
---|
| 72 | return MEMTYPE_UNUSABLE;
|
---|
| 73 | case EFI_LOADER_CODE:
|
---|
| 74 | case EFI_LOADER_DATA:
|
---|
| 75 | case EFI_BOOT_SERVICES_CODE:
|
---|
| 76 | case EFI_BOOT_SERVICES_DATA:
|
---|
| 77 | case EFI_CONVENTIONAL_MEMORY:
|
---|
| 78 | case EFI_PERSISTENT_MEMORY:
|
---|
| 79 | return MEMTYPE_AVAILABLE;
|
---|
| 80 | case EFI_ACPI_RECLAIM_MEMORY:
|
---|
| 81 | return MEMTYPE_ACPI_RECLAIM;
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | return MEMTYPE_UNUSABLE;
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | /** Send a byte to the UEFI console output.
|
---|
| 88 | *
|
---|
| 89 | * @param byte Byte to send.
|
---|
| 90 | */
|
---|
| 91 | static void scons_sendb(uint8_t byte)
|
---|
| 92 | {
|
---|
| 93 | int16_t out[2] = { byte, '\0' };
|
---|
| 94 | efi_system_table->cons_out->output_string(efi_system_table->cons_out,
|
---|
| 95 | out);
|
---|
| 96 | }
|
---|
| 97 |
|
---|
| 98 | /** Display a character.
|
---|
| 99 | *
|
---|
| 100 | * @param ch Character to display.
|
---|
[28a5ebd] | 101 | *
|
---|
[84176f3] | 102 | */
|
---|
[28a5ebd] | 103 | void putuchar(char32_t ch)
|
---|
[84176f3] | 104 | {
|
---|
| 105 | if (ch == '\n')
|
---|
| 106 | scons_sendb('\r');
|
---|
| 107 |
|
---|
| 108 | if (ascii_check(ch))
|
---|
| 109 | scons_sendb((uint8_t) ch);
|
---|
| 110 | else
|
---|
| 111 | scons_sendb('?');
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 | efi_status_t bootstrap(void *efi_handle_in,
|
---|
| 115 | efi_system_table_t *efi_system_table_in, void *load_address)
|
---|
| 116 | {
|
---|
| 117 | efi_status_t status;
|
---|
| 118 | uint64_t current_el;
|
---|
| 119 | uint64_t memmap = 0;
|
---|
| 120 | sysarg_t memmap_size;
|
---|
| 121 | sysarg_t memmap_key;
|
---|
| 122 | sysarg_t memmap_descriptor_size;
|
---|
| 123 | uint32_t memmap_descriptor_version;
|
---|
| 124 | uint64_t alloc_addr = 0;
|
---|
| 125 | sysarg_t alloc_pages = 0;
|
---|
| 126 |
|
---|
| 127 | /*
|
---|
| 128 | * Bootinfo structure is dynamically allocated in the ARM64 port. It is
|
---|
| 129 | * placed directly after the inflated components. This assures that if
|
---|
| 130 | * the kernel identity maps the first gigabyte of the main memory in the
|
---|
| 131 | * kernel/upper address space then it can access the bootinfo because
|
---|
| 132 | * the inflated components and bootinfo can always fit in this area.
|
---|
| 133 | */
|
---|
| 134 | bootinfo_t *bootinfo;
|
---|
| 135 |
|
---|
| 136 | efi_system_table = efi_system_table_in;
|
---|
| 137 |
|
---|
| 138 | version_print();
|
---|
| 139 |
|
---|
| 140 | printf("Boot loader: %p -> %p\n", loader_start, loader_end);
|
---|
| 141 | printf("\nMemory statistics\n");
|
---|
| 142 | printf(" %p|%p: loader\n", load_address, load_address);
|
---|
| 143 | printf(" %p|%p: UEFI system table\n", efi_system_table_in,
|
---|
| 144 | efi_system_table_in);
|
---|
| 145 |
|
---|
| 146 | /* Validate the exception level. */
|
---|
| 147 | current_el = CurrentEL_read();
|
---|
| 148 | if (current_el != CURRENT_EL_EL1) {
|
---|
| 149 | printf("Error: Unexpected CurrentEL value %0#18" PRIx64 ".\n",
|
---|
| 150 | current_el);
|
---|
| 151 | status = EFI_UNSUPPORTED;
|
---|
| 152 | goto fail;
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | /* Obtain memory map. */
|
---|
| 156 | status = efi_get_memory_map(efi_system_table, &memmap_size,
|
---|
| 157 | (efi_v1_memdesc_t **) &memmap, &memmap_key, &memmap_descriptor_size,
|
---|
| 158 | &memmap_descriptor_version);
|
---|
| 159 | if (status != EFI_SUCCESS) {
|
---|
| 160 | printf("Error: Unable to obtain initial memory map, status "
|
---|
| 161 | "code: %" PRIx64 ".\n", status);
|
---|
| 162 | goto fail;
|
---|
| 163 | }
|
---|
| 164 |
|
---|
| 165 | /* Find start of usable RAM. */
|
---|
| 166 | uint64_t memory_base = (uint64_t) -1;
|
---|
| 167 | for (sysarg_t i = 0; i < memmap_size / memmap_descriptor_size; i++) {
|
---|
| 168 | efi_v1_memdesc_t *desc = (void *) memmap +
|
---|
| 169 | (i * memmap_descriptor_size);
|
---|
| 170 | if (get_memtype(desc->type) != MEMTYPE_AVAILABLE ||
|
---|
| 171 | !(desc->attribute & EFI_MEMORY_WB))
|
---|
| 172 | continue;
|
---|
| 173 |
|
---|
| 174 | if (desc->phys_start < memory_base)
|
---|
| 175 | memory_base = desc->phys_start;
|
---|
| 176 | }
|
---|
| 177 |
|
---|
| 178 | /* Deallocate memory holding the map. */
|
---|
| 179 | efi_system_table->boot_services->free_pool((void *) memmap);
|
---|
| 180 | memmap = 0;
|
---|
| 181 |
|
---|
| 182 | if (memory_base == (uint64_t) -1) {
|
---|
| 183 | printf("Error: Memory map does not contain any usable RAM.\n");
|
---|
| 184 | status = EFI_UNSUPPORTED;
|
---|
| 185 | goto fail;
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | /*
|
---|
| 189 | * Check that everything is aligned on a 4kB boundary and the kernel can
|
---|
| 190 | * be placed by the decompression code at a correct address.
|
---|
| 191 | */
|
---|
| 192 |
|
---|
| 193 | /* Statically check PAGE_SIZE and BOOT_OFFSET. */
|
---|
[4099129] | 194 | _Static_assert(PAGE_SIZE == 4096, "PAGE_SIZE must be equal to 4096");
|
---|
| 195 | _Static_assert(IS_ALIGNED(BOOT_OFFSET, PAGE_SIZE),
|
---|
| 196 | "BOOT_OFFSET must be a multiple of PAGE_SIZE");
|
---|
| 197 |
|
---|
[84176f3] | 198 | /*
|
---|
| 199 | * Dynamically check the memory base. The condition should be always
|
---|
| 200 | * true because UEFI guarantees each physical/virtual address in the
|
---|
| 201 | * memory map is aligned on a 4kB boundary.
|
---|
| 202 | */
|
---|
| 203 | if (!IS_ALIGNED(memory_base, PAGE_SIZE)) {
|
---|
| 204 | printf("Error: Start of usable RAM (%p) is not aligned on a "
|
---|
| 205 | "4kB boundary.\n", (void *) memory_base);
|
---|
| 206 | status = EFI_UNSUPPORTED;
|
---|
| 207 | goto fail;
|
---|
| 208 | }
|
---|
| 209 |
|
---|
| 210 | /*
|
---|
| 211 | * Calculate where the components (including the kernel) will get
|
---|
| 212 | * placed.
|
---|
| 213 | */
|
---|
| 214 | uint64_t decompress_base = memory_base + BOOT_OFFSET;
|
---|
| 215 | printf(" %p|%p: kernel entry point\n", (void *) decompress_base,
|
---|
| 216 | (void *) decompress_base);
|
---|
| 217 |
|
---|
| 218 | /*
|
---|
| 219 | * Allocate memory for the decompressed components and for the bootinfo.
|
---|
| 220 | */
|
---|
| 221 | uint64_t component_pages =
|
---|
| 222 | ALIGN_UP(payload_unpacked_size(), EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
|
---|
| 223 | uint64_t bootinfo_pages = ALIGN_UP(sizeof(*bootinfo), EFI_PAGE_SIZE) /
|
---|
| 224 | EFI_PAGE_SIZE;
|
---|
| 225 | alloc_pages = component_pages + bootinfo_pages;
|
---|
| 226 | alloc_addr = decompress_base;
|
---|
| 227 | status = efi_system_table->boot_services->allocate_pages(
|
---|
| 228 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_CODE, alloc_pages, &alloc_addr);
|
---|
| 229 | if (status != EFI_SUCCESS) {
|
---|
| 230 | printf("Error: Unable to allocate memory for inflated "
|
---|
| 231 | "components and bootinfo, status code: %" PRIx64 ".\n",
|
---|
| 232 | status);
|
---|
| 233 | goto fail;
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | bootinfo = (void *) alloc_addr + component_pages * EFI_PAGE_SIZE;
|
---|
| 237 | printf(" %p|%p: boot info structure\n", bootinfo, bootinfo);
|
---|
| 238 | memset(bootinfo, 0, sizeof(*bootinfo));
|
---|
| 239 |
|
---|
| 240 | /* Decompress the components. */
|
---|
| 241 | uint8_t *kernel_dest = (uint8_t *) alloc_addr;
|
---|
| 242 | uint8_t *ram_end = kernel_dest + component_pages * EFI_PAGE_SIZE;
|
---|
| 243 |
|
---|
| 244 | extract_payload(&bootinfo->taskmap, kernel_dest, ram_end,
|
---|
| 245 | (uintptr_t) kernel_dest, ensure_visibility);
|
---|
| 246 |
|
---|
| 247 | /* Get final memory map. */
|
---|
| 248 | status = efi_get_memory_map(efi_system_table, &memmap_size,
|
---|
| 249 | (efi_v1_memdesc_t **) &memmap, &memmap_key, &memmap_descriptor_size,
|
---|
| 250 | &memmap_descriptor_version);
|
---|
| 251 | if (status != EFI_SUCCESS) {
|
---|
| 252 | printf("Error: Unable to obtain final memory map, status code: "
|
---|
| 253 | "%" PRIx64 ".\n", status);
|
---|
| 254 | goto fail;
|
---|
| 255 | }
|
---|
| 256 |
|
---|
| 257 | /* Convert the UEFI memory map to the bootinfo representation. */
|
---|
| 258 | size_t cnt = 0;
|
---|
| 259 | memtype_t current_type = MEMTYPE_UNUSABLE;
|
---|
| 260 | void *current_start = 0;
|
---|
| 261 | size_t current_size = 0;
|
---|
| 262 | sysarg_t memmap_items_count = memmap_size / memmap_descriptor_size;
|
---|
| 263 | for (sysarg_t i = 0; i < memmap_items_count; i++) {
|
---|
| 264 | efi_v1_memdesc_t *desc = (void *) memmap +
|
---|
| 265 | (i * memmap_descriptor_size);
|
---|
| 266 |
|
---|
| 267 | /* Get type of the new area. */
|
---|
| 268 | memtype_t type;
|
---|
| 269 | if (!(desc->attribute & EFI_MEMORY_WB))
|
---|
| 270 | type = MEMTYPE_UNUSABLE;
|
---|
| 271 | else
|
---|
| 272 | type = get_memtype(desc->type);
|
---|
| 273 |
|
---|
| 274 | /* Try to merge the new area with the previous one. */
|
---|
| 275 | if (type == current_type &&
|
---|
| 276 | (uint64_t)current_start + current_size == desc->phys_start) {
|
---|
| 277 | current_size += desc->pages * EFI_PAGE_SIZE;
|
---|
| 278 | if (i != memmap_items_count - 1)
|
---|
| 279 | continue;
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | /* Record the previous area. */
|
---|
| 283 | if (current_type != MEMTYPE_UNUSABLE) {
|
---|
| 284 | if (cnt >= MEMMAP_MAX_RECORDS) {
|
---|
| 285 | printf("Error: Too many usable memory "
|
---|
| 286 | "areas.\n");
|
---|
| 287 | status = EFI_UNSUPPORTED;
|
---|
| 288 | goto fail;
|
---|
| 289 | }
|
---|
| 290 | bootinfo->memmap.zones[cnt].type = current_type;
|
---|
| 291 | bootinfo->memmap.zones[cnt].start = current_start;
|
---|
| 292 | bootinfo->memmap.zones[cnt].size = current_size;
|
---|
| 293 | cnt++;
|
---|
| 294 | }
|
---|
| 295 |
|
---|
| 296 | /* Remember the new area. */
|
---|
| 297 | current_type = type;
|
---|
| 298 | current_start = (void *) desc->phys_start;
|
---|
| 299 | current_size = desc->pages * EFI_PAGE_SIZE;
|
---|
| 300 | }
|
---|
| 301 | bootinfo->memmap.cnt = cnt;
|
---|
| 302 |
|
---|
| 303 | uintptr_t entry = check_kernel_translated((void *) decompress_base,
|
---|
| 304 | BOOT_OFFSET);
|
---|
| 305 |
|
---|
| 306 | printf("Booting the kernel...\n");
|
---|
| 307 |
|
---|
| 308 | /* Exit boot services. This is a point of no return. */
|
---|
| 309 | efi_system_table->boot_services->exit_boot_services(efi_handle_in,
|
---|
| 310 | memmap_key);
|
---|
| 311 |
|
---|
| 312 | entry = memory_base + KA2PA(entry);
|
---|
| 313 | jump_to_kernel((void *) entry, bootinfo);
|
---|
| 314 |
|
---|
| 315 | fail:
|
---|
| 316 | if (memmap != 0)
|
---|
| 317 | efi_system_table->boot_services->free_pool((void *) memmap);
|
---|
| 318 |
|
---|
| 319 | if (alloc_addr != 0)
|
---|
| 320 | efi_system_table->boot_services->free_pages(alloc_addr,
|
---|
| 321 | alloc_pages);
|
---|
| 322 |
|
---|
| 323 | return status;
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 | /** @}
|
---|
| 327 | */
|
---|