| /* |
| * Copyright 2013 Google Inc. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <arch/msr.h> |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include "arch/x86/zeropage.h" |
| #include "base/algorithm.h" |
| #include "base/cleanup.h" |
| #include "base/fwdb.h" |
| #include "base/physmem.h" |
| #include "base/timestamp.h" |
| #include "boot/boot.h" |
| #include "boot/magenta.h" |
| #include "drivers/framebuffer/framebuffer.h" |
| |
| static void * const ParamsBuff = (void *)(uintptr_t)0x1000; |
| static void * const CmdLineBuff = (void *)(uintptr_t)0x2000; |
| |
| static const uint32_t KernelV2Magic = 0x53726448; |
| static const uint16_t MinProtocol = 0x0202; |
| |
| static int prepare_zero_page(struct boot_params *boot_params) |
| { |
| struct setup_header *hdr = &boot_params->hdr; |
| |
| if (hdr->header != KernelV2Magic || hdr->version < MinProtocol) { |
| printf("Boot protocol is too old.\n"); |
| return 1; |
| } |
| |
| E820MemRanges *e820 = get_e820_mem_ranges(); |
| if (!e820) |
| return 1; |
| |
| int e820_idx = 0; |
| struct e820entry *entry = &boot_params->e820_map[e820_idx]; |
| if (e820->num_ranges) { |
| const E820MemRange *range = &e820->ranges[0]; |
| entry->type = range->type; |
| entry->addr = range->base; |
| entry->size = range->size; |
| } |
| for (int i = 1; i < e820->num_ranges; i++) { |
| const E820MemRange *range = &e820->ranges[i]; |
| |
| if (entry->type == range->type && |
| entry->addr + entry->size == range->base) { |
| entry->size += range->size; |
| } else { |
| if (e820_idx == ARRAY_SIZE(boot_params->e820_map) - 1) { |
| printf("boot_params e820 space exhausted.\n"); |
| break; |
| } |
| e820_idx++; |
| entry++; |
| entry->type = range->type; |
| entry->addr = range->base; |
| entry->size = range->size; |
| } |
| } |
| boot_params->e820_entries = e820_idx + 1; |
| |
| // Loader type is undefined. |
| hdr->type_of_loader = 0xFF; |
| |
| // Don't reload the data/code segments. |
| hdr->loadflags |= KEEP_SEGMENTS; |
| |
| hdr->cmd_line_ptr = (uintptr_t)CmdLineBuff; |
| |
| return 0; |
| } |
| |
| static void __attribute__((noreturn)) place_and_start_kernel( |
| struct boot_params *boot_params, char *cmd_line, void *kernel, |
| uintptr_t entry_offset) |
| { |
| cleanup_trigger(CleanupOnHandoff); |
| |
| memcpy(ParamsBuff, boot_params, sizeof(*boot_params)); |
| strcpy(CmdLineBuff, cmd_line); |
| if (CONFIG_HOSTED) { |
| // Copy the kernel to where it's supposed to run from, now |
| // that we have total control of the system. |
| memcpy((void *)(uintptr_t)CONFIG_KERNEL_START, |
| kernel, boot_params->hdr.syssize * 16); |
| } |
| |
| puts("\nStarting kernel ...\n\n"); |
| timestamp_add_now(TS_START_KERNEL); |
| |
| uintptr_t entry_addr = CONFIG_KERNEL_START + entry_offset; |
| boot_x86_zeropage_start_kernel(ParamsBuff, (void *)entry_addr); |
| } |
| |
| int boot_x86_linux(struct boot_params *boot_params, char *cmd_line, |
| void *kernel, uintptr_t entry_offset) |
| { |
| // We'll move the boot_params structure and the command line to where |
| // Linux suggests and to where they'll be safe from being trampled by |
| // the kernel as it's decompressed. Assert that it will fit while we |
| // can still report errors. |
| assert((uint8_t *)CmdLineBuff - (uint8_t *)ParamsBuff >= |
| sizeof(*boot_params)); |
| |
| if (prepare_zero_page(boot_params)) |
| return 1; |
| |
| place_and_start_kernel(boot_params, cmd_line, kernel, entry_offset); |
| } |
| |
| int boot_x86_magenta(struct boot_params *boot_params, char *cmd_line, |
| void *kernel, uintptr_t entry_offset) |
| { |
| // We'll move the boot_params structure and the command line to the |
| // same place Linux suggests and to where they'll be safe from being |
| // trampled by the kernel as it's decompressed. Assert that it will |
| // fit while we can still report errors. |
| assert((uint8_t *)CmdLineBuff - (uint8_t *)ParamsBuff >= |
| sizeof(*boot_params)); |
| |
| if (prepare_zero_page(boot_params)) |
| return 1; |
| |
| /* Fill in the fields in the zero page magenta has co-opted. */ |
| typedef struct { |
| uint8_t _pad0[0x080]; |
| |
| uint32_t acpi_rsdp; // 0x080 |
| |
| uint8_t _pad1[0x090 - 0x080 - sizeof(uint32_t)]; |
| |
| uint32_t fb_base; // 0x090 |
| uint32_t fb_width; // 0x094 |
| uint32_t fb_height; // 0x098 |
| uint32_t fb_stride; // 0x09c |
| uint32_t fb_format; // 0x0a0 |
| uint32_t fb_regbase; // 0x0a4 |
| uint32_t fb_size; // 0x0a8 |
| |
| uint8_t _pad2[0x220 - 0x0a8 - sizeof(uint32_t)]; |
| |
| uint32_t extra_magic; // 0x220 |
| } MagentaZpOverlay; |
| |
| MagentaZpOverlay *mgzp = (MagentaZpOverlay *)boot_params; |
| |
| // Set the "extra magic" value which signals the use of the new fields. |
| mgzp->extra_magic = 0xDBC64323; |
| |
| // The location of the ACPI RSD. |
| FwdbEntry acpi_rsdp_entry; |
| if (fwdb_access("acpi.rsdp_addr", &acpi_rsdp_entry, NULL)) { |
| printf("ACPI RSDP address not in the FWDB.\n"); |
| mgzp->acpi_rsdp = 0; |
| } else { |
| uintptr_t rsdp_addr; |
| assert(sizeof(rsdp_addr) == acpi_rsdp_entry.size); |
| rsdp_addr = *(typeof(rsdp_addr) *)acpi_rsdp_entry.ptr; |
| mgzp->acpi_rsdp = rsdp_addr; |
| } |
| |
| FrameBufferOps *framebuffer = magenta_framebuffer(); |
| FrameBuffer fb; |
| if (framebuffer_prepare(framebuffer, &fb)) |
| return 1; |
| |
| mgzp->fb_base = (uintptr_t)fb.buffer; |
| mgzp->fb_width = fb.resolution.x; |
| mgzp->fb_height = fb.resolution.y; |
| mgzp->fb_stride = fb.bytes_per_line / ((fb.bits_per_pixel + 7) / 8); |
| |
| // These values are hardcoded in gigaboot. |
| mgzp->fb_format = 5; // XRGB32 |
| mgzp->fb_regbase = 0; |
| mgzp->fb_size = 256 * 1024 * 1024; |
| |
| place_and_start_kernel(boot_params, cmd_line, kernel, entry_offset); |
| } |