blob: 8608615587aa5e5f649bf9d0262f3d5bf30a2b5c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/guest/zircon.h"
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fbl/unique_fd.h>
#include <zircon/assert.h>
#include <zircon/boot/bootdata.h>
#include <zircon/boot/kernel-drivers.h>
#include "garnet/bin/guest/kernel.h"
#include "garnet/lib/machina/guest.h"
#if __aarch64__
static constexpr uintptr_t kKernelOffset = 0;
#elif __x86_64__
static constexpr uintptr_t kKernelOffset = 0x100000;
#include "garnet/lib/machina/arch/x86/acpi.h"
#include "garnet/lib/machina/arch/x86/e820.h"
#endif
static bool is_bootdata(const bootdata_t* header) {
return header->type == BOOTDATA_CONTAINER &&
header->length > sizeof(bootdata_t) &&
header->extra == BOOTDATA_MAGIC && header->flags & BOOTDATA_FLAG_V2 &&
header->magic == BOOTITEM_MAGIC;
}
static void set_bootdata(bootdata_t* header, uint32_t type, uint32_t extra,
uint32_t len) {
// Guest memory is initially zeroed, so we skip fields that must be zero.
header->type = type;
header->length = len;
header->extra = extra;
header->flags = BOOTDATA_FLAG_V2;
header->magic = BOOTITEM_MAGIC;
header->crc32 = BOOTITEM_NO_CRC32;
}
static zx_status_t load_cmdline(const std::string& cmdline,
const machina::PhysMem& phys_mem,
const uintptr_t bootdata_off) {
auto container_hdr = phys_mem.as<bootdata_t>(bootdata_off);
const uintptr_t data_off =
bootdata_off + sizeof(bootdata_t) + BOOTDATA_ALIGN(container_hdr->length);
const size_t cmdline_len = cmdline.size() + 1;
if (cmdline_len > UINT32_MAX || data_off + cmdline_len > phys_mem.size()) {
FXL_LOG(ERROR) << "Command line is too long";
return ZX_ERR_OUT_OF_RANGE;
}
auto cmdline_hdr = phys_mem.as<bootdata_t>(data_off);
set_bootdata(cmdline_hdr, BOOTDATA_CMDLINE, 0,
static_cast<uint32_t>(cmdline_len));
memcpy(cmdline_hdr + 1, cmdline.c_str(), cmdline_len);
container_hdr->length += static_cast<uint32_t>(sizeof(bootdata_t)) +
BOOTDATA_ALIGN(cmdline_hdr->length);
return ZX_OK;
}
static zx_status_t load_bootfs(const int fd,
const machina::PhysMem& phys_mem,
const uintptr_t bootdata_off) {
bootdata_t ramdisk_hdr;
ssize_t ret = read(fd, &ramdisk_hdr, sizeof(bootdata_t));
if (ret != sizeof(bootdata_t)) {
FXL_LOG(ERROR) << "Failed to read BOOTFS image header";
return ZX_ERR_IO;
}
if (!is_bootdata(&ramdisk_hdr)) {
FXL_LOG(ERROR) << "Invalid BOOTFS image header";
return ZX_ERR_IO_DATA_INTEGRITY;
}
if (ramdisk_hdr.length > phys_mem.size() - bootdata_off) {
FXL_LOG(ERROR) << "BOOTFS image is too large";
return ZX_ERR_OUT_OF_RANGE;
}
auto container_hdr = phys_mem.as<bootdata_t>(bootdata_off);
uintptr_t data_off =
bootdata_off + sizeof(bootdata_t) + BOOTDATA_ALIGN(container_hdr->length);
ret = read(fd, phys_mem.as<void>(data_off, ramdisk_hdr.length),
ramdisk_hdr.length);
if (ret < 0 || (size_t)ret != ramdisk_hdr.length) {
FXL_LOG(ERROR) << "Failed to read BOOTFS image data";
return ZX_ERR_IO;
}
container_hdr->length += BOOTDATA_ALIGN(ramdisk_hdr.length) +
static_cast<uint32_t>(sizeof(bootdata_t));
return ZX_OK;
}
/*
status = set_mem_map_size(&mdi_root, static_cast<uint64_t>(phys_mem.size()));
status = set_cpu_count(&mdi_root, num_cpus); cfg.num_cpus()
*/
#if __aarch64__
static bootdata_mem_range_t mem_config[] = {
{
.type = BOOTDATA_MEM_RANGE_RAM,
.paddr = 0,
.length = 0, // set to phys_mem.size()
},
{
.type = BOOTDATA_MEM_RANGE_PERIPHERAL,
.paddr = 0xe8100000,
.length = 0x17f00000,
},
{
// Reserved for RTC
.type = BOOTDATA_MEM_RANGE_RESERVED,
.paddr = 0x09010000,
.length = 0x1000, // 4KB
},
{
// Reserved for MMIO
.type = BOOTDATA_MEM_RANGE_RESERVED,
.paddr = 0x06fe0000,
.length = 0x1000000, // 16MB
},
{
// Reserved for ECAM
.type = BOOTDATA_MEM_RANGE_RESERVED,
.paddr = 0x2e000000,
.length = 0x1000000, // 16MB
},
};
static const bootdata_platform_id_t platform_id = {
.vid = 3, // PDEV_VID_GOOGLE
.pid = 2, // PDEV_PID_MACHINA
.board_name = "machina",
};
static const bootdata_kernel_driver_t uart_driver = {
.mmio_phys = 0xfff32000,
.irq = 111,
};
static const bootdata_arm_gicv2_driver_t gicv2_driver = {
.mmio_phys = 0x001b0000,
.msi_frame_phys = 0xe82b0000,
.gicd_offset = 0x1000,
.gicc_offset = 0x2000,
.gich_offset = 0x4000,
.gicv_offset = 0x6000,
.ipi_base = 13,
.optional = true,
};
static const bootdata_arm_gicv3_driver_t gicv3_driver = {
.mmio_phys = 0xe82b0000,
.gicd_offset = 0x00000,
.gicr_offset = 0xa0000,
.gicr_stride = 0x20000,
.ipi_base = 13,
.optional = true,
};
static const bootdata_arm_psci_driver_t psci_driver = {
.use_hvc = false,
};
static const bootdata_arm_generic_timer_driver_t timer_driver = {
.irq_virt = 27,
};
#endif // __aarch64__
static zx_status_t create_bootdata(const machina::PhysMem& phys_mem,
uintptr_t bootdata_off, uint32_t num_cpus) {
if (BOOTDATA_ALIGN(bootdata_off) != bootdata_off) {
return ZX_ERR_INVALID_ARGS;
}
#if __aarch64__
bootdata_cpu_config_t cpu_config;
cpu_config.cluster_count = 0;
cpu_config.clusters[0].cpu_count = num_cpus;
const size_t bootdata_len = sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(cpu_config)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(mem_config)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(platform_id)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(uart_driver)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(gicv2_driver)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(gicv3_driver)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(psci_driver)) + sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(timer_driver)) + sizeof(bootdata_t);
#elif __x86_64__
const size_t e820_size = machina::e820_size(phys_mem.size());
const size_t bootdata_len = sizeof(bootdata_t) +
BOOTDATA_ALIGN(sizeof(uint64_t)) +
sizeof(bootdata_t) + BOOTDATA_ALIGN(e820_size);
#endif
if (bootdata_off + bootdata_len + sizeof(bootdata_t) > phys_mem.size()) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
// Bootdata container.
auto container_hdr = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(container_hdr, BOOTDATA_CONTAINER, BOOTDATA_MAGIC,
static_cast<uint32_t>(bootdata_len));
bootdata_off += sizeof(bootdata_t);
#if __aarch64__
// CPU config
auto bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_CPU_CONFIG, 0, sizeof(cpu_config));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &cpu_config, sizeof(cpu_config));
bootdata_off += BOOTDATA_ALIGN(sizeof(cpu_config));
// memory config
mem_config[0].length = phys_mem.size();
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_MEM_CONFIG, 0, sizeof(mem_config));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &mem_config, sizeof(mem_config));
bootdata_off += BOOTDATA_ALIGN(sizeof(mem_config));
// platform ID
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_PLATFORM_ID, 0, sizeof(platform_id));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &platform_id, sizeof(platform_id));
bootdata_off += BOOTDATA_ALIGN(sizeof(platform_id));
// uart driver
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_KERNEL_DRIVER, KDRV_PL011_UART,
sizeof(uart_driver));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &uart_driver, sizeof(uart_driver));
bootdata_off += BOOTDATA_ALIGN(sizeof(uart_driver));
// gicv2 driver
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_KERNEL_DRIVER, KDRV_ARM_GIC_V2,
sizeof(gicv2_driver));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &gicv2_driver, sizeof(gicv2_driver));
bootdata_off += BOOTDATA_ALIGN(sizeof(gicv2_driver));
// gicv3 driver
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_KERNEL_DRIVER, KDRV_ARM_GIC_V3,
sizeof(gicv3_driver));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &gicv3_driver, sizeof(gicv3_driver));
bootdata_off += BOOTDATA_ALIGN(sizeof(gicv3_driver));
// psci driver
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_KERNEL_DRIVER, KDRV_ARM_PSCI,
sizeof(psci_driver));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &psci_driver, sizeof(psci_driver));
bootdata_off += BOOTDATA_ALIGN(sizeof(psci_driver));
// timer driver
bootdata_header = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(bootdata_header, BOOTDATA_KERNEL_DRIVER, KDRV_ARM_GENERIC_TIMER,
sizeof(timer_driver));
bootdata_off += sizeof(bootdata_t);
memcpy(phys_mem.as<void*>(bootdata_off), &timer_driver, sizeof(timer_driver));
bootdata_off += BOOTDATA_ALIGN(sizeof(timer_driver));
return ZX_OK;
#elif __x86_64__
// ACPI root table pointer.
auto acpi_rsdp_hdr = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(acpi_rsdp_hdr, BOOTDATA_ACPI_RSDP, 0, sizeof(uint64_t));
bootdata_off += sizeof(bootdata_t);
*phys_mem.as<uint64_t>(bootdata_off) = machina::kAcpiOffset;
// E820 memory map.
bootdata_off += BOOTDATA_ALIGN(sizeof(uint64_t));
auto e820_table_hdr = phys_mem.as<bootdata_t>(bootdata_off);
set_bootdata(e820_table_hdr, BOOTDATA_E820_TABLE, 0,
static_cast<uint32_t>(e820_size));
bootdata_off += sizeof(bootdata_t);
return machina::create_e820(phys_mem, bootdata_off);
#endif
}
static zx_status_t read_bootdata(const machina::PhysMem& phys_mem,
uintptr_t* guest_ip) {
auto kernel_hdr = phys_mem.as<zircon_kernel_t>(kKernelOffset);
if (kernel_hdr->hdr_kernel.type != BOOTDATA_KERNEL) {
FXL_LOG(ERROR) << "Invalid Zircon kernel header";
return ZX_ERR_IO_DATA_INTEGRITY;
}
*guest_ip = kernel_hdr->data_kernel.entry64;
return ZX_OK;
}
zx_status_t setup_zircon(const GuestConfig cfg,
const machina::PhysMem& phys_mem,
uintptr_t* guest_ip,
uintptr_t* boot_ptr) {
// Read the kernel image.
zx_status_t status = load_kernel(cfg.kernel_path(), phys_mem, kKernelOffset);
if (status != ZX_OK) {
return status;
}
status = read_bootdata(phys_mem, guest_ip);
if (status != ZX_OK) {
return status;
}
// Create the BOOTDATA container.
status = create_bootdata(phys_mem, kRamdiskOffset, cfg.num_cpus());
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to create BOOTDATA";
return status;
}
// Load the kernel command line.
status = load_cmdline(cfg.cmdline(), phys_mem, kRamdiskOffset);
if (status != ZX_OK) {
return status;
}
// If we have been provided a BOOTFS image, load it.
if (!cfg.ramdisk_path().empty()) {
fbl::unique_fd boot_fd(open(cfg.ramdisk_path().c_str(), O_RDONLY));
if (!boot_fd) {
FXL_LOG(ERROR) << "Failed to open BOOTFS image " << cfg.ramdisk_path();
return ZX_ERR_IO;
}
status =
load_bootfs(boot_fd.get(), phys_mem, kRamdiskOffset);
if (status != ZX_OK) {
return status;
}
}
*boot_ptr = kRamdiskOffset;
return ZX_OK;
}