134 lines
4.4 KiB
C
134 lines
4.4 KiB
C
|
/*
|
||
|
* PVH Option ROM for fw_cfg DMA
|
||
|
*
|
||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* Copyright (c) 2019 Red Hat Inc.
|
||
|
* Authors:
|
||
|
* Stefano Garzarella <sgarzare@redhat.com>
|
||
|
*/
|
||
|
|
||
|
asm (".code32"); /* this code will be executed in protected mode */
|
||
|
|
||
|
#include <stddef.h>
|
||
|
#include <stdint.h>
|
||
|
#include "optrom.h"
|
||
|
#include "optrom_fw_cfg.h"
|
||
|
#include "../../include/hw/xen/start_info.h"
|
||
|
|
||
|
#define RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */
|
||
|
#define RSDP_AREA_ADDR 0x000E0000
|
||
|
#define RSDP_AREA_SIZE 0x00020000
|
||
|
#define EBDA_BASE_ADDR 0x0000040E
|
||
|
#define EBDA_SIZE 1024
|
||
|
|
||
|
#define E820_MAXENTRIES 128
|
||
|
#define CMDLINE_BUFSIZE 4096
|
||
|
|
||
|
/* e820 table filled in pvh.S using int 0x15 */
|
||
|
struct pvh_e820_table {
|
||
|
uint32_t entries;
|
||
|
uint32_t reserved;
|
||
|
struct hvm_memmap_table_entry table[E820_MAXENTRIES];
|
||
|
};
|
||
|
|
||
|
struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned));
|
||
|
|
||
|
static struct hvm_start_info start_info;
|
||
|
static struct hvm_modlist_entry ramdisk_mod;
|
||
|
static uint8_t cmdline_buffer[CMDLINE_BUFSIZE];
|
||
|
|
||
|
|
||
|
/* Search RSDP signature. */
|
||
|
static uintptr_t search_rsdp(uint32_t start_addr, uint32_t end_addr)
|
||
|
{
|
||
|
uint64_t *rsdp_p;
|
||
|
|
||
|
/* RSDP signature is always on a 16 byte boundary */
|
||
|
for (rsdp_p = (uint64_t *)start_addr; rsdp_p < (uint64_t *)end_addr;
|
||
|
rsdp_p += 2) {
|
||
|
if (*rsdp_p == RSDP_SIGNATURE) {
|
||
|
return (uintptr_t)rsdp_p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Force the asm name without leading underscore, even on Win32. */
|
||
|
extern void pvh_load_kernel(void) asm("pvh_load_kernel");
|
||
|
|
||
|
void pvh_load_kernel(void)
|
||
|
{
|
||
|
void *cmdline_addr = &cmdline_buffer;
|
||
|
void *kernel_entry, *initrd_addr;
|
||
|
uint32_t cmdline_size, initrd_size, fw_cfg_version = bios_cfg_version();
|
||
|
|
||
|
start_info.magic = XEN_HVM_START_MAGIC_VALUE;
|
||
|
start_info.version = 1;
|
||
|
|
||
|
/*
|
||
|
* pvh_e820 is filled in the pvh.S before to switch in protected mode,
|
||
|
* because we can use int 0x15 only in real mode.
|
||
|
*/
|
||
|
start_info.memmap_entries = pvh_e820.entries;
|
||
|
start_info.memmap_paddr = (uintptr_t)pvh_e820.table;
|
||
|
|
||
|
/*
|
||
|
* Search RSDP in the main BIOS area below 1 MB.
|
||
|
* SeaBIOS store the RSDP in this area, so we try it first.
|
||
|
*/
|
||
|
start_info.rsdp_paddr = search_rsdp(RSDP_AREA_ADDR,
|
||
|
RSDP_AREA_ADDR + RSDP_AREA_SIZE);
|
||
|
|
||
|
/* Search RSDP in the EBDA if it is not found */
|
||
|
if (!start_info.rsdp_paddr) {
|
||
|
/*
|
||
|
* Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes
|
||
|
* segment pointer to EBDA, so we must convert it to a linear address.
|
||
|
*/
|
||
|
uint32_t ebda_paddr = ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR)) << 4;
|
||
|
if (ebda_paddr > 0x400) {
|
||
|
uint32_t *ebda = (uint32_t *)ebda_paddr;
|
||
|
|
||
|
start_info.rsdp_paddr = search_rsdp(*ebda, *ebda + EBDA_SIZE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4, fw_cfg_version);
|
||
|
bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size,
|
||
|
fw_cfg_version);
|
||
|
start_info.cmdline_paddr = (uintptr_t)cmdline_addr;
|
||
|
|
||
|
/* Check if we have the initrd to load */
|
||
|
bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4, fw_cfg_version);
|
||
|
if (initrd_size) {
|
||
|
bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4,
|
||
|
fw_cfg_version);
|
||
|
bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size,
|
||
|
fw_cfg_version);
|
||
|
|
||
|
ramdisk_mod.paddr = (uintptr_t)initrd_addr;
|
||
|
ramdisk_mod.size = initrd_size;
|
||
|
|
||
|
/* The first module is always ramdisk. */
|
||
|
start_info.modlist_paddr = (uintptr_t)&ramdisk_mod;
|
||
|
start_info.nr_modules = 1;
|
||
|
}
|
||
|
|
||
|
bios_cfg_read_entry(&kernel_entry, FW_CFG_KERNEL_ENTRY, 4, fw_cfg_version);
|
||
|
|
||
|
asm volatile("jmp *%1" : : "b"(&start_info), "c"(kernel_entry));
|
||
|
}
|