blob: 73c10fd5a06c8bc9a6fa76aef6707f10c0076556 [file] [log] [blame]
/*
* 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);
}