qemu/pc-bios/optionrom/pvh_main.c
<<
>>
Prefs
   1/*
   2 * PVH Option ROM for fw_cfg DMA
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16 *
  17 * Copyright (c) 2019 Red Hat Inc.
  18 *   Authors:
  19 *     Stefano Garzarella <sgarzare@redhat.com>
  20 */
  21
  22asm (".code32"); /* this code will be executed in protected mode */
  23
  24#include <stddef.h>
  25#include <stdint.h>
  26#include "optrom.h"
  27#include "optrom_fw_cfg.h"
  28#include "../../include/hw/xen/start_info.h"
  29
  30#define RSDP_SIGNATURE          0x2052545020445352LL /* "RSD PTR " */
  31#define RSDP_AREA_ADDR          0x000E0000
  32#define RSDP_AREA_SIZE          0x00020000
  33#define EBDA_BASE_ADDR          0x0000040E
  34#define EBDA_SIZE               1024
  35
  36#define E820_MAXENTRIES         128
  37#define CMDLINE_BUFSIZE         4096
  38
  39/* e820 table filled in pvh.S using int 0x15 */
  40struct pvh_e820_table {
  41    uint32_t entries;
  42    uint32_t reserved;
  43    struct hvm_memmap_table_entry table[E820_MAXENTRIES];
  44};
  45
  46struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned));
  47
  48static struct hvm_start_info start_info;
  49static struct hvm_modlist_entry ramdisk_mod;
  50static uint8_t cmdline_buffer[CMDLINE_BUFSIZE];
  51
  52
  53/* Search RSDP signature. */
  54static uintptr_t search_rsdp(uint32_t start_addr, uint32_t end_addr)
  55{
  56    uint64_t *rsdp_p;
  57
  58    /* RSDP signature is always on a 16 byte boundary */
  59    for (rsdp_p = (uint64_t *)start_addr; rsdp_p < (uint64_t *)end_addr;
  60         rsdp_p += 2) {
  61        if (*rsdp_p == RSDP_SIGNATURE) {
  62            return (uintptr_t)rsdp_p;
  63        }
  64    }
  65
  66    return 0;
  67}
  68
  69/* Force the asm name without leading underscore, even on Win32. */
  70extern void pvh_load_kernel(void) asm("pvh_load_kernel");
  71
  72void pvh_load_kernel(void)
  73{
  74    void *cmdline_addr = &cmdline_buffer;
  75    void *kernel_entry, *initrd_addr;
  76    uint32_t cmdline_size, initrd_size, fw_cfg_version = bios_cfg_version();
  77
  78    start_info.magic = XEN_HVM_START_MAGIC_VALUE;
  79    start_info.version = 1;
  80
  81    /*
  82     * pvh_e820 is filled in the pvh.S before to switch in protected mode,
  83     * because we can use int 0x15 only in real mode.
  84     */
  85    start_info.memmap_entries = pvh_e820.entries;
  86    start_info.memmap_paddr = (uintptr_t)pvh_e820.table;
  87
  88    /*
  89     * Search RSDP in the main BIOS area below 1 MB.
  90     * SeaBIOS store the RSDP in this area, so we try it first.
  91     */
  92    start_info.rsdp_paddr = search_rsdp(RSDP_AREA_ADDR,
  93                                        RSDP_AREA_ADDR + RSDP_AREA_SIZE);
  94
  95    /* Search RSDP in the EBDA if it is not found */
  96    if (!start_info.rsdp_paddr) {
  97        /*
  98         * Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes
  99         * segment pointer to EBDA, so we must convert it to a linear address.
 100         */
 101        uint32_t ebda_paddr = ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR)) << 4;
 102        if (ebda_paddr > 0x400) {
 103            uint32_t *ebda = (uint32_t *)ebda_paddr;
 104
 105            start_info.rsdp_paddr = search_rsdp(*ebda, *ebda + EBDA_SIZE);
 106        }
 107    }
 108
 109    bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4, fw_cfg_version);
 110    bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size,
 111                        fw_cfg_version);
 112    start_info.cmdline_paddr = (uintptr_t)cmdline_addr;
 113
 114    /* Check if we have the initrd to load */
 115    bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4, fw_cfg_version);
 116    if (initrd_size) {
 117        bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4,
 118                            fw_cfg_version);
 119        bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size,
 120                            fw_cfg_version);
 121
 122        ramdisk_mod.paddr = (uintptr_t)initrd_addr;
 123        ramdisk_mod.size = initrd_size;
 124
 125        /* The first module is always ramdisk. */
 126        start_info.modlist_paddr = (uintptr_t)&ramdisk_mod;
 127        start_info.nr_modules = 1;
 128    }
 129
 130    bios_cfg_read_entry(&kernel_entry, FW_CFG_KERNEL_ENTRY, 4, fw_cfg_version);
 131
 132    asm volatile("jmp *%1" : : "b"(&start_info), "c"(kernel_entry));
 133}
 134