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 22 asm (".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 2048 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 */ 40 struct pvh_e820_table { 41 uint32_t entries; 42 uint32_t reserved; 43 struct hvm_memmap_table_entry table[E820_MAXENTRIES]; 44 }; 45 46 struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned)); 47 48 static struct hvm_start_info start_info; 49 static struct hvm_modlist_entry ramdisk_mod; 50 static uint8_t cmdline_buffer[CMDLINE_BUFSIZE]; 51 52 53 /* Search RSDP signature. */ 54 static 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. */ 70 extern void pvh_load_kernel(void) asm("pvh_load_kernel"); 71 72 void 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